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