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