• 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 (TrackingData::iterator it = array_buffers_.begin();
20        it != array_buffers_.end();) {
21     JSArrayBuffer* buffer = reinterpret_cast<JSArrayBuffer*>(it->first);
22     if ((free_mode == kFreeAll) || ObjectMarking::IsWhite(buffer)) {
23       const size_t len = it->second;
24       heap_->isolate()->array_buffer_allocator()->Free(buffer->backing_store(),
25                                                        len);
26       freed_memory += len;
27       it = array_buffers_.erase(it);
28     } else {
29       ++it;
30     }
31   }
32   if (freed_memory > 0) {
33     heap_->update_external_memory_concurrently_freed(
34         static_cast<intptr_t>(freed_memory));
35   }
36 }
37 
38 template <typename Callback>
Process(Callback callback)39 void LocalArrayBufferTracker::Process(Callback callback) {
40   JSArrayBuffer* new_buffer = nullptr;
41   size_t freed_memory = 0;
42   for (TrackingData::iterator it = array_buffers_.begin();
43        it != array_buffers_.end();) {
44     const CallbackResult result = callback(it->first, &new_buffer);
45     if (result == kKeepEntry) {
46       ++it;
47     } else if (result == kUpdateEntry) {
48       DCHECK_NOT_NULL(new_buffer);
49       Page* target_page = Page::FromAddress(new_buffer->address());
50       // We need to lock the target page because we cannot guarantee
51       // exclusive access to new space pages.
52       if (target_page->InNewSpace()) target_page->mutex()->Lock();
53       LocalArrayBufferTracker* tracker = target_page->local_tracker();
54       if (tracker == nullptr) {
55         target_page->AllocateLocalTracker();
56         tracker = target_page->local_tracker();
57       }
58       DCHECK_NOT_NULL(tracker);
59       tracker->Add(new_buffer, it->second);
60       if (target_page->InNewSpace()) target_page->mutex()->Unlock();
61       it = array_buffers_.erase(it);
62     } else if (result == kRemoveEntry) {
63       const size_t len = it->second;
64       heap_->isolate()->array_buffer_allocator()->Free(
65           it->first->backing_store(), len);
66       freed_memory += len;
67       it = array_buffers_.erase(it);
68     } else {
69       UNREACHABLE();
70     }
71   }
72   if (freed_memory > 0) {
73     heap_->update_external_memory_concurrently_freed(
74         static_cast<intptr_t>(freed_memory));
75   }
76 }
77 
FreeDeadInNewSpace(Heap * heap)78 void ArrayBufferTracker::FreeDeadInNewSpace(Heap* heap) {
79   DCHECK_EQ(heap->gc_state(), Heap::HeapState::SCAVENGE);
80   for (Page* page : PageRange(heap->new_space()->FromSpaceStart(),
81                               heap->new_space()->FromSpaceEnd())) {
82     bool empty = ProcessBuffers(page, kUpdateForwardedRemoveOthers);
83     CHECK(empty);
84   }
85   heap->account_external_memory_concurrently_freed();
86 }
87 
FreeDead(Page * page)88 void ArrayBufferTracker::FreeDead(Page* page) {
89   // Callers need to ensure having the page lock.
90   LocalArrayBufferTracker* tracker = page->local_tracker();
91   if (tracker == nullptr) return;
92   DCHECK(!page->SweepingDone());
93   tracker->Free<LocalArrayBufferTracker::kFreeDead>();
94   if (tracker->IsEmpty()) {
95     page->ReleaseLocalTracker();
96   }
97 }
98 
FreeAll(Page * page)99 void ArrayBufferTracker::FreeAll(Page* page) {
100   LocalArrayBufferTracker* tracker = page->local_tracker();
101   if (tracker == nullptr) return;
102   tracker->Free<LocalArrayBufferTracker::kFreeAll>();
103   if (tracker->IsEmpty()) {
104     page->ReleaseLocalTracker();
105   }
106 }
107 
ProcessBuffers(Page * page,ProcessingMode mode)108 bool ArrayBufferTracker::ProcessBuffers(Page* page, ProcessingMode mode) {
109   LocalArrayBufferTracker* tracker = page->local_tracker();
110   if (tracker == nullptr) return true;
111 
112   DCHECK(page->SweepingDone());
113   tracker->Process(
114       [mode](JSArrayBuffer* old_buffer, JSArrayBuffer** new_buffer) {
115         MapWord map_word = old_buffer->map_word();
116         if (map_word.IsForwardingAddress()) {
117           *new_buffer = JSArrayBuffer::cast(map_word.ToForwardingAddress());
118           return LocalArrayBufferTracker::kUpdateEntry;
119         }
120         return mode == kUpdateForwardedKeepOthers
121                    ? LocalArrayBufferTracker::kKeepEntry
122                    : LocalArrayBufferTracker::kRemoveEntry;
123       });
124   return tracker->IsEmpty();
125 }
126 
IsTracked(JSArrayBuffer * buffer)127 bool ArrayBufferTracker::IsTracked(JSArrayBuffer* buffer) {
128   Page* page = Page::FromAddress(buffer->address());
129   {
130     base::LockGuard<base::Mutex> guard(page->mutex());
131     LocalArrayBufferTracker* tracker = page->local_tracker();
132     if (tracker == nullptr) return false;
133     return tracker->IsTracked(buffer);
134   }
135 }
136 
137 }  // namespace internal
138 }  // namespace v8
139