1 // Copyright 2016 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 #ifndef V8_HEAP_ARRAY_BUFFER_TRACKER_INL_H_ 6 #define V8_HEAP_ARRAY_BUFFER_TRACKER_INL_H_ 7 8 #include "src/conversions-inl.h" 9 #include "src/heap/array-buffer-tracker.h" 10 #include "src/heap/heap.h" 11 #include "src/heap/spaces.h" 12 #include "src/objects.h" 13 #include "src/objects/js-array-buffer-inl.h" 14 15 namespace v8 { 16 namespace internal { 17 18 void ArrayBufferTracker::RegisterNew(Heap* heap, JSArrayBuffer* buffer) { 19 if (buffer->backing_store() == nullptr) return; 20 21 const size_t length = NumberToSize(buffer->byte_length()); 22 Page* page = Page::FromAddress(buffer->address()); 23 { 24 base::LockGuard<base::Mutex> guard(page->mutex()); 25 LocalArrayBufferTracker* tracker = page->local_tracker(); 26 if (tracker == nullptr) { 27 page->AllocateLocalTracker(); 28 tracker = page->local_tracker(); 29 } 30 DCHECK_NOT_NULL(tracker); 31 tracker->Add(buffer, length); 32 } 33 34 // TODO(wez): Remove backing-store from external memory accounting. 35 // We may go over the limit of externally allocated memory here. We call the 36 // api function to trigger a GC in this case. 37 reinterpret_cast<v8::Isolate*>(heap->isolate()) 38 ->AdjustAmountOfExternalAllocatedMemory(length); 39 } 40 41 void ArrayBufferTracker::Unregister(Heap* heap, JSArrayBuffer* buffer) { 42 if (buffer->backing_store() == nullptr) return; 43 44 Page* page = Page::FromAddress(buffer->address()); 45 const size_t length = NumberToSize(buffer->byte_length()); 46 { 47 base::LockGuard<base::Mutex> guard(page->mutex()); 48 LocalArrayBufferTracker* tracker = page->local_tracker(); 49 DCHECK_NOT_NULL(tracker); 50 tracker->Remove(buffer, length); 51 } 52 53 // TODO(wez): Remove backing-store from external memory accounting. 54 heap->update_external_memory(-static_cast<intptr_t>(length)); 55 } 56 57 Space* LocalArrayBufferTracker::space() { return page_->owner(); } 58 59 template <typename Callback> 60 void LocalArrayBufferTracker::Free(Callback should_free) { 61 size_t freed_memory = 0; 62 Isolate* isolate = page_->heap()->isolate(); 63 for (TrackingData::iterator it = array_buffers_.begin(); 64 it != array_buffers_.end();) { 65 JSArrayBuffer* buffer = reinterpret_cast<JSArrayBuffer*>(it->first); 66 const size_t length = it->second.length; 67 68 if (should_free(buffer)) { 69 JSArrayBuffer::FreeBackingStore(isolate, it->second); 70 it = array_buffers_.erase(it); 71 freed_memory += length; 72 } else { 73 ++it; 74 } 75 } 76 if (freed_memory > 0) { 77 page_->DecrementExternalBackingStoreBytes( 78 ExternalBackingStoreType::kArrayBuffer, freed_memory); 79 80 // TODO(wez): Remove backing-store from external memory accounting. 81 page_->heap()->update_external_memory_concurrently_freed( 82 static_cast<intptr_t>(freed_memory)); 83 } 84 } 85 86 template <typename MarkingState> 87 void ArrayBufferTracker::FreeDead(Page* page, MarkingState* marking_state) { 88 // Callers need to ensure having the page lock. 89 LocalArrayBufferTracker* tracker = page->local_tracker(); 90 if (tracker == nullptr) return; 91 tracker->Free([marking_state](JSArrayBuffer* buffer) { 92 return marking_state->IsWhite(buffer); 93 }); 94 if (tracker->IsEmpty()) { 95 page->ReleaseLocalTracker(); 96 } 97 } 98 99 void LocalArrayBufferTracker::Add(JSArrayBuffer* buffer, size_t length) { 100 page_->IncrementExternalBackingStoreBytes( 101 ExternalBackingStoreType::kArrayBuffer, length); 102 103 auto ret = array_buffers_.insert( 104 {buffer, 105 {buffer->backing_store(), length, buffer->backing_store(), 106 buffer->is_wasm_memory()}}); 107 USE(ret); 108 // Check that we indeed inserted a new value and did not overwrite an existing 109 // one (which would be a bug). 110 DCHECK(ret.second); 111 } 112 113 void LocalArrayBufferTracker::Remove(JSArrayBuffer* buffer, size_t length) { 114 page_->DecrementExternalBackingStoreBytes( 115 ExternalBackingStoreType::kArrayBuffer, length); 116 117 TrackingData::iterator it = array_buffers_.find(buffer); 118 // Check that we indeed find a key to remove. 119 DCHECK(it != array_buffers_.end()); 120 DCHECK_EQ(length, it->second.length); 121 array_buffers_.erase(it); 122 } 123 124 } // namespace internal 125 } // namespace v8 126 127 #endif // V8_HEAP_ARRAY_BUFFER_TRACKER_INL_H_ 128