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 229811 : LocalArrayBufferTracker::~LocalArrayBufferTracker() {
18 229811 : CHECK(array_buffers_.empty());
19 229816 : }
20 :
21 : template <typename Callback>
22 168788 : void LocalArrayBufferTracker::Process(Callback callback) {
23 : std::vector<JSArrayBuffer::Allocation> backing_stores_to_free;
24 168823 : TrackingData kept_array_buffers;
25 :
26 168823 : JSArrayBuffer new_buffer;
27 : JSArrayBuffer old_buffer;
28 : size_t freed_memory = 0;
29 624102 : for (TrackingData::iterator it = array_buffers_.begin();
30 : it != array_buffers_.end(); ++it) {
31 286453 : old_buffer = it->first;
32 : DCHECK_EQ(page_, Page::FromHeapObject(old_buffer));
33 286453 : const CallbackResult result = callback(old_buffer, &new_buffer);
34 286494 : if (result == kKeepEntry) {
35 0 : kept_array_buffers.insert(*it);
36 286494 : } else if (result == kUpdateEntry) {
37 : DCHECK(!new_buffer.is_null());
38 103323 : Page* target_page = Page::FromHeapObject(new_buffer);
39 : {
40 100827 : base::MutexGuard guard(target_page->mutex());
41 : LocalArrayBufferTracker* tracker = target_page->local_tracker();
42 100825 : if (tracker == nullptr) {
43 2494 : target_page->AllocateLocalTracker();
44 : tracker = target_page->local_tracker();
45 : }
46 : DCHECK_NOT_NULL(tracker);
47 100829 : 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 100829 : tracker->AddInternal(new_buffer, length);
52 100824 : MemoryChunk::MoveExternalBackingStoreBytes(
53 : ExternalBackingStoreType::kArrayBuffer,
54 : static_cast<MemoryChunk*>(page_),
55 100824 : static_cast<MemoryChunk*>(target_page), length);
56 : }
57 185667 : } else if (result == kRemoveEntry) {
58 185667 : 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 185667 : backing_stores_to_free.push_back(it->second);
63 : } else {
64 0 : UNREACHABLE();
65 : }
66 : }
67 168826 : if (freed_memory) {
68 6433 : page_->DecrementExternalBackingStoreBytes(
69 181689 : ExternalBackingStoreType::kArrayBuffer, freed_memory);
70 : // TODO(wez): Remove backing-store from external memory accounting.
71 : page_->heap()->update_external_memory_concurrently_freed(
72 6433 : 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 168823 : page_->heap()->array_buffer_collector()->QueueOrFreeGarbageAllocations(
80 337662 : std::move(backing_stores_to_free));
81 168837 : }
82 :
83 23490 : void ArrayBufferTracker::PrepareToFreeDeadInNewSpace(Heap* heap) {
84 : DCHECK_EQ(heap->gc_state(), Heap::HeapState::SCAVENGE);
85 111445 : for (Page* page :
86 : PageRange(heap->new_space()->from_space().first_page(), nullptr)) {
87 : bool empty = ProcessBuffers(page, kUpdateForwardedRemoveOthers);
88 87955 : CHECK(empty);
89 : }
90 23490 : }
91 :
92 280630 : void ArrayBufferTracker::FreeAll(Page* page) {
93 : LocalArrayBufferTracker* tracker = page->local_tracker();
94 561260 : if (tracker == nullptr) return;
95 134591 : tracker->Free([](JSArrayBuffer buffer) { return true; });
96 134591 : if (tracker->IsEmpty()) {
97 134591 : page->ReleaseLocalTracker();
98 : }
99 : }
100 :
101 171154 : bool ArrayBufferTracker::ProcessBuffers(Page* page, ProcessingMode mode) {
102 : LocalArrayBufferTracker* tracker = page->local_tracker();
103 171154 : if (tracker == nullptr) return true;
104 :
105 : DCHECK(page->SweepingDone());
106 286494 : tracker->Process([mode](JSArrayBuffer old_buffer, JSArrayBuffer* new_buffer) {
107 : MapWord map_word = old_buffer->map_word();
108 286494 : if (map_word.IsForwardingAddress()) {
109 100818 : *new_buffer = JSArrayBuffer::cast(map_word.ToForwardingAddress());
110 100818 : return LocalArrayBufferTracker::kUpdateEntry;
111 : }
112 185670 : return mode == kUpdateForwardedKeepOthers
113 : ? LocalArrayBufferTracker::kKeepEntry
114 185670 : : LocalArrayBufferTracker::kRemoveEntry;
115 168807 : });
116 83230 : return tracker->IsEmpty();
117 : }
118 :
119 190 : bool ArrayBufferTracker::IsTracked(JSArrayBuffer buffer) {
120 190 : Page* page = Page::FromHeapObject(buffer);
121 : {
122 190 : 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 122068 : void ArrayBufferTracker::TearDown(Heap* heap) {
130 : // ArrayBuffers can only be found in NEW_SPACE and OLD_SPACE.
131 205928 : for (Page* p : *heap->old_space()) {
132 144894 : FreeAll(p);
133 : }
134 : NewSpace* new_space = heap->new_space();
135 61034 : if (new_space->to_space().is_committed()) {
136 191595 : for (Page* p : new_space->to_space()) {
137 130561 : 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 61034 : }
148 :
149 : } // namespace internal
150 178779 : } // namespace v8
|