• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/heap/array-buffer-tracker.h"
6 #include "src/heap/array-buffer-tracker-inl.h"
7 #include "src/heap/heap.h"
8 
9 namespace v8 {
10 namespace internal {
11 
~LocalArrayBufferTracker()12 LocalArrayBufferTracker::~LocalArrayBufferTracker() {
13   CHECK(array_buffers_.empty());
14 }
15 
16 template <LocalArrayBufferTracker::FreeMode free_mode>
Free()17 void LocalArrayBufferTracker::Free() {
18   size_t freed_memory = 0;
19   for (TrackingMap::iterator it = array_buffers_.begin();
20        it != array_buffers_.end();) {
21     if ((free_mode == kFreeAll) ||
22         Marking::IsWhite(Marking::MarkBitFrom(it->first))) {
23       heap_->isolate()->array_buffer_allocator()->Free(it->second.first,
24                                                        it->second.second);
25       freed_memory += it->second.second;
26       it = array_buffers_.erase(it);
27     } else {
28       it++;
29     }
30   }
31   if (freed_memory > 0) {
32     heap_->update_external_memory_concurrently_freed(
33         static_cast<intptr_t>(freed_memory));
34   }
35 }
36 
37 template <typename Callback>
Process(Callback callback)38 void LocalArrayBufferTracker::Process(Callback callback) {
39   JSArrayBuffer* new_buffer = nullptr;
40   size_t freed_memory = 0;
41   for (TrackingMap::iterator it = array_buffers_.begin();
42        it != array_buffers_.end();) {
43     const CallbackResult result = callback(it->first, &new_buffer);
44     if (result == kKeepEntry) {
45       it++;
46     } else if (result == kUpdateEntry) {
47       DCHECK_NOT_NULL(new_buffer);
48       Page* target_page = Page::FromAddress(new_buffer->address());
49       // We need to lock the target page because we cannot guarantee
50       // exclusive access to new space pages.
51       if (target_page->InNewSpace()) target_page->mutex()->Lock();
52       LocalArrayBufferTracker* tracker = target_page->local_tracker();
53       if (tracker == nullptr) {
54         target_page->AllocateLocalTracker();
55         tracker = target_page->local_tracker();
56       }
57       DCHECK_NOT_NULL(tracker);
58       tracker->Add(new_buffer, it->second);
59       if (target_page->InNewSpace()) target_page->mutex()->Unlock();
60       it = array_buffers_.erase(it);
61     } else if (result == kRemoveEntry) {
62       heap_->isolate()->array_buffer_allocator()->Free(it->second.first,
63                                                        it->second.second);
64       freed_memory += it->second.second;
65       it = array_buffers_.erase(it);
66     } else {
67       UNREACHABLE();
68     }
69   }
70   if (freed_memory > 0) {
71     heap_->update_external_memory_concurrently_freed(
72         static_cast<intptr_t>(freed_memory));
73   }
74 }
75 
FreeDeadInNewSpace(Heap * heap)76 void ArrayBufferTracker::FreeDeadInNewSpace(Heap* heap) {
77   DCHECK_EQ(heap->gc_state(), Heap::HeapState::SCAVENGE);
78   for (Page* page : NewSpacePageRange(heap->new_space()->FromSpaceStart(),
79                                       heap->new_space()->FromSpaceEnd())) {
80     bool empty = ProcessBuffers(page, kUpdateForwardedRemoveOthers);
81     CHECK(empty);
82   }
83   heap->account_external_memory_concurrently_freed();
84 }
85 
FreeDead(Page * page)86 void ArrayBufferTracker::FreeDead(Page* page) {
87   // Callers need to ensure having the page lock.
88   LocalArrayBufferTracker* tracker = page->local_tracker();
89   if (tracker == nullptr) return;
90   DCHECK(!page->SweepingDone());
91   tracker->Free<LocalArrayBufferTracker::kFreeDead>();
92   if (tracker->IsEmpty()) {
93     page->ReleaseLocalTracker();
94   }
95 }
96 
FreeAll(Page * page)97 void ArrayBufferTracker::FreeAll(Page* page) {
98   LocalArrayBufferTracker* tracker = page->local_tracker();
99   if (tracker == nullptr) return;
100   tracker->Free<LocalArrayBufferTracker::kFreeAll>();
101   if (tracker->IsEmpty()) {
102     page->ReleaseLocalTracker();
103   }
104 }
105 
ProcessBuffers(Page * page,ProcessingMode mode)106 bool ArrayBufferTracker::ProcessBuffers(Page* page, ProcessingMode mode) {
107   LocalArrayBufferTracker* tracker = page->local_tracker();
108   if (tracker == nullptr) return true;
109 
110   DCHECK(page->SweepingDone());
111   tracker->Process(
112       [mode](JSArrayBuffer* old_buffer, JSArrayBuffer** new_buffer) {
113         MapWord map_word = old_buffer->map_word();
114         if (map_word.IsForwardingAddress()) {
115           *new_buffer = JSArrayBuffer::cast(map_word.ToForwardingAddress());
116           return LocalArrayBufferTracker::kUpdateEntry;
117         }
118         return mode == kUpdateForwardedKeepOthers
119                    ? LocalArrayBufferTracker::kKeepEntry
120                    : LocalArrayBufferTracker::kRemoveEntry;
121       });
122   return tracker->IsEmpty();
123 }
124 
IsTracked(JSArrayBuffer * buffer)125 bool ArrayBufferTracker::IsTracked(JSArrayBuffer* buffer) {
126   Page* page = Page::FromAddress(buffer->address());
127   {
128     base::LockGuard<base::Mutex> guard(page->mutex());
129     LocalArrayBufferTracker* tracker = page->local_tracker();
130     if (tracker == nullptr) return false;
131     return tracker->IsTracked(buffer);
132   }
133 }
134 
135 }  // namespace internal
136 }  // namespace v8
137