Line data Source code
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 :
7 : #include <vector>
8 :
9 : #include "src/heap/array-buffer-collector.h"
10 : #include "src/heap/array-buffer-tracker-inl.h"
11 : #include "src/heap/heap.h"
12 : #include "src/heap/spaces.h"
13 :
14 : namespace v8 {
15 : namespace internal {
16 :
17 870982 : LocalArrayBufferTracker::~LocalArrayBufferTracker() {
18 435493 : CHECK(array_buffers_.empty());
19 435489 : }
20 :
21 : template <typename Callback>
22 319809 : void LocalArrayBufferTracker::Process(Callback callback) {
23 : std::vector<JSArrayBuffer::Allocation> backing_stores_to_free;
24 : TrackingData kept_array_buffers;
25 :
26 : JSArrayBuffer new_buffer;
27 : JSArrayBuffer old_buffer;
28 : size_t freed_memory = 0;
29 607436 : for (TrackingData::iterator it = array_buffers_.begin();
30 : it != array_buffers_.end(); ++it) {
31 287818 : old_buffer = it->first;
32 : DCHECK_EQ(page_, Page::FromHeapObject(old_buffer));
33 : const CallbackResult result = callback(old_buffer, &new_buffer);
34 287818 : if (result == kKeepEntry) {
35 : kept_array_buffers.insert(*it);
36 287818 : } else if (result == kUpdateEntry) {
37 : DCHECK(!new_buffer.is_null());
38 : Page* target_page = Page::FromHeapObject(new_buffer);
39 : {
40 : base::MutexGuard guard(target_page->mutex());
41 : LocalArrayBufferTracker* tracker = target_page->local_tracker();
42 31459 : if (tracker == nullptr) {
43 1101 : target_page->AllocateLocalTracker();
44 : tracker = target_page->local_tracker();
45 : }
46 : DCHECK_NOT_NULL(tracker);
47 31459 : const size_t length = it->second.length;
48 : // We should decrement before adding to avoid potential overflows in
49 : // the external memory counters.
50 : DCHECK_EQ(it->first->is_wasm_memory(), it->second.is_wasm_memory);
51 31459 : tracker->AddInternal(new_buffer, length);
52 31455 : MemoryChunk::MoveExternalBackingStoreBytes(
53 : ExternalBackingStoreType::kArrayBuffer,
54 : static_cast<MemoryChunk*>(page_),
55 : static_cast<MemoryChunk*>(target_page), length);
56 : }
57 256366 : } else if (result == kRemoveEntry) {
58 256366 : freed_memory += it->second.length;
59 : // We pass backing_store() and stored length to the collector for freeing
60 : // the backing store. Wasm allocations will go through their own tracker
61 : // based on the backing store.
62 256366 : backing_stores_to_free.push_back(it->second);
63 : } else {
64 0 : UNREACHABLE();
65 : }
66 : }
67 319618 : if (freed_memory) {
68 5126 : page_->DecrementExternalBackingStoreBytes(
69 : ExternalBackingStoreType::kArrayBuffer, freed_memory);
70 : // TODO(wez): Remove backing-store from external memory accounting.
71 5126 : page_->heap()->update_external_memory_concurrently_freed(
72 : static_cast<intptr_t>(freed_memory));
73 : }
74 :
75 : array_buffers_.swap(kept_array_buffers);
76 :
77 : // Pass the backing stores that need to be freed to the main thread for
78 : // potential later distribution.
79 958849 : page_->heap()->array_buffer_collector()->QueueOrFreeGarbageAllocations(
80 : std::move(backing_stores_to_free));
81 319667 : }
82 :
83 26098 : void ArrayBufferTracker::PrepareToFreeDeadInNewSpace(Heap* heap) {
84 : DCHECK_EQ(heap->gc_state(), Heap::HeapState::SCAVENGE);
85 261804 : for (Page* page :
86 : PageRange(heap->new_space()->from_space().first_page(), nullptr)) {
87 : bool empty = ProcessBuffers(page, kUpdateForwardedRemoveOthers);
88 235706 : CHECK(empty);
89 : }
90 26098 : }
91 :
92 453707 : void ArrayBufferTracker::FreeAll(Page* page) {
93 : LocalArrayBufferTracker* tracker = page->local_tracker();
94 453707 : if (tracker == nullptr) return;
95 276802 : tracker->Free([](JSArrayBuffer buffer) { return true; });
96 276802 : if (tracker->IsEmpty()) {
97 276802 : page->ReleaseLocalTracker();
98 : }
99 : }
100 :
101 86309 : bool ArrayBufferTracker::ProcessBuffers(Page* page, ProcessingMode mode) {
102 : LocalArrayBufferTracker* tracker = page->local_tracker();
103 322015 : if (tracker == nullptr) return true;
104 :
105 : DCHECK(page->SweepingDone());
106 256367 : tracker->Process([mode](JSArrayBuffer old_buffer, JSArrayBuffer* new_buffer) {
107 : MapWord map_word = old_buffer->map_word();
108 287818 : if (map_word.IsForwardingAddress()) {
109 : *new_buffer = JSArrayBuffer::cast(map_word.ToForwardingAddress());
110 : return LocalArrayBufferTracker::kUpdateEntry;
111 : }
112 : return mode == kUpdateForwardedKeepOthers
113 : ? LocalArrayBufferTracker::kKeepEntry
114 256367 : : LocalArrayBufferTracker::kRemoveEntry;
115 319638 : });
116 86360 : return tracker->IsEmpty();
117 : }
118 :
119 190 : bool ArrayBufferTracker::IsTracked(JSArrayBuffer buffer) {
120 : Page* page = Page::FromHeapObject(buffer);
121 : {
122 : base::MutexGuard guard(page->mutex());
123 : LocalArrayBufferTracker* tracker = page->local_tracker();
124 190 : if (tracker == nullptr) return false;
125 170 : return tracker->IsTracked(buffer);
126 : }
127 : }
128 :
129 62426 : void ArrayBufferTracker::TearDown(Heap* heap) {
130 : // ArrayBuffers can only be found in NEW_SPACE and OLD_SPACE.
131 232759 : for (Page* p : *heap->old_space()) {
132 170332 : FreeAll(p);
133 : }
134 : NewSpace* new_space = heap->new_space();
135 62427 : if (new_space->to_space().is_committed()) {
136 334288 : for (Page* p : new_space->to_space()) {
137 271860 : FreeAll(p);
138 : }
139 : }
140 : #ifdef DEBUG
141 : if (new_space->from_space().is_committed()) {
142 : for (Page* p : new_space->from_space()) {
143 : DCHECK(!p->contains_array_buffers());
144 : }
145 : }
146 : #endif // DEBUG
147 62428 : }
148 :
149 : } // namespace internal
150 122036 : } // namespace v8
|