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 (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> 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 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 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 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 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 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