1 // Copyright 2015 The Chromium 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 "third_party/blink/renderer/platform/heap/impl/persistent_node.h"
6
7 #include "base/debug/alias.h"
8 #include "third_party/blink/renderer/platform/heap/handle.h"
9 #include "third_party/blink/renderer/platform/heap/persistent.h"
10 #include "third_party/blink/renderer/platform/heap/process_heap.h"
11
12 namespace blink {
13
14 namespace {
15
16 class DummyGCBase final : public GarbageCollected<DummyGCBase> {
17 public:
Trace(Visitor * visitor) const18 void Trace(Visitor* visitor) const {}
19 };
20 } // namespace
21
~PersistentRegionBase()22 PersistentRegionBase::~PersistentRegionBase() {
23 PersistentNodeSlots* slots = slots_;
24 while (slots) {
25 PersistentNodeSlots* dead_slots = slots;
26 slots = slots->next;
27 delete dead_slots;
28 }
29 }
30
NodesInUse() const31 int PersistentRegionBase::NodesInUse() const {
32 size_t persistent_count = 0;
33 for (PersistentNodeSlots* slots = slots_; slots; slots = slots->next) {
34 for (int i = 0; i < PersistentNodeSlots::kSlotCount; ++i) {
35 if (!slots->slot[i].IsUnused())
36 ++persistent_count;
37 }
38 }
39 #if DCHECK_IS_ON()
40 DCHECK_EQ(persistent_count, used_node_count_);
41 #endif
42 return persistent_count;
43 }
44
EnsureNodeSlots()45 void PersistentRegionBase::EnsureNodeSlots() {
46 DCHECK(!free_list_head_);
47 PersistentNodeSlots* slots = new PersistentNodeSlots;
48 for (int i = 0; i < PersistentNodeSlots::kSlotCount; ++i) {
49 PersistentNode* node = &slots->slot[i];
50 node->SetFreeListNext(free_list_head_);
51 free_list_head_ = node;
52 DCHECK(node->IsUnused());
53 }
54 slots->next = slots_;
55 slots_ = slots;
56 }
57
TraceNodesImpl(Visitor * visitor,ShouldTraceCallback should_trace)58 void PersistentRegionBase::TraceNodesImpl(Visitor* visitor,
59 ShouldTraceCallback should_trace) {
60 free_list_head_ = nullptr;
61 size_t persistent_count = 0;
62 PersistentNodeSlots** prev_next = &slots_;
63 PersistentNodeSlots* slots = slots_;
64 while (slots) {
65 PersistentNode* free_list_next = nullptr;
66 PersistentNode* free_list_last = nullptr;
67 int free_count = 0;
68 for (int i = 0; i < PersistentNodeSlots::kSlotCount; ++i) {
69 PersistentNode* node = &slots->slot[i];
70 if (node->IsUnused()) {
71 if (!free_list_next)
72 free_list_last = node;
73 node->SetFreeListNext(free_list_next);
74 free_list_next = node;
75 ++free_count;
76 } else {
77 ++persistent_count;
78 if (!should_trace(visitor, node))
79 continue;
80 node->TracePersistentNode(visitor);
81 }
82 }
83 if (free_count == PersistentNodeSlots::kSlotCount) {
84 PersistentNodeSlots* dead_slots = slots;
85 *prev_next = slots->next;
86 slots = slots->next;
87 delete dead_slots;
88 } else {
89 if (free_list_last) {
90 DCHECK(free_list_next);
91 DCHECK(!free_list_last->FreeListNext());
92 free_list_last->SetFreeListNext(free_list_head_);
93 free_list_head_ = free_list_next;
94 }
95 prev_next = &slots->next;
96 slots = slots->next;
97 }
98 }
99 #if DCHECK_IS_ON()
100 DCHECK_EQ(persistent_count, used_node_count_);
101 #endif
102 }
103
ReleaseNode(PersistentNode * persistent_node)104 void PersistentRegion::ReleaseNode(PersistentNode* persistent_node) {
105 DCHECK(!persistent_node->IsUnused());
106 // 'self' is in use, containing the persistent wrapper object.
107 void* self = persistent_node->Self();
108 Persistent<DummyGCBase>* persistent =
109 reinterpret_cast<Persistent<DummyGCBase>*>(self);
110 persistent->Clear();
111 DCHECK(persistent_node->IsUnused());
112 }
113
PrepareForThreadStateTermination(ThreadState * state)114 void PersistentRegion::PrepareForThreadStateTermination(ThreadState* state) {
115 DCHECK_EQ(state, ThreadState::Current());
116 DCHECK(!IsMainThread());
117 PersistentNodeSlots* slots = slots_;
118 while (slots) {
119 for (int i = 0; i < PersistentNodeSlots::kSlotCount; ++i) {
120 PersistentNode* node = &slots->slot[i];
121 if (node->IsUnused())
122 continue;
123 // It is safe to cast to Persistent<DummyGCBase> because persistent heap
124 // collections are banned in non-main threads.
125 Persistent<DummyGCBase>* persistent =
126 reinterpret_cast<Persistent<DummyGCBase>*>(node->Self());
127 DCHECK(persistent);
128 persistent->Clear();
129 DCHECK(node->IsUnused());
130 }
131 slots = slots->next;
132 }
133 #if DCHECK_IS_ON()
134 DCHECK_EQ(used_node_count_, 0u);
135 #endif
136 }
137
ShouldTracePersistentNode(Visitor * visitor,PersistentNode * node)138 bool CrossThreadPersistentRegion::ShouldTracePersistentNode(
139 Visitor* visitor,
140 PersistentNode* node) {
141 CrossThreadPersistent<DummyGCBase>* persistent =
142 reinterpret_cast<CrossThreadPersistent<DummyGCBase>*>(node->Self());
143 DCHECK(persistent);
144 DCHECK(!persistent->IsHashTableDeletedValue());
145 Address raw_object = reinterpret_cast<Address>(persistent->Get());
146 if (!raw_object)
147 return false;
148 return &visitor->Heap() == &ThreadState::FromObject(raw_object)->Heap();
149 }
150
PrepareForThreadStateTermination(ThreadState * thread_state)151 void CrossThreadPersistentRegion::PrepareForThreadStateTermination(
152 ThreadState* thread_state) {
153 // For heaps belonging to a thread that's detaching, any cross-thread
154 // persistents pointing into them needs to be disabled. Do that by clearing
155 // out the underlying heap reference.
156 MutexLocker lock(ProcessHeap::CrossThreadPersistentMutex());
157
158 PersistentNodeSlots* slots = slots_;
159 while (slots) {
160 for (int i = 0; i < PersistentNodeSlots::kSlotCount; ++i) {
161 if (slots->slot[i].IsUnused())
162 continue;
163
164 // 'self' is in use, containing the cross-thread persistent wrapper
165 // object.
166 CrossThreadPersistent<DummyGCBase>* persistent =
167 reinterpret_cast<CrossThreadPersistent<DummyGCBase>*>(
168 slots->slot[i].Self());
169 DCHECK(persistent);
170 void* raw_object = persistent->Get();
171 if (!raw_object)
172 continue;
173 BasePage* page = PageFromObject(raw_object);
174 DCHECK(page);
175 if (page->Arena()->GetThreadState() == thread_state) {
176 persistent->ClearWithLockHeld();
177 DCHECK(slots->slot[i].IsUnused());
178 }
179 }
180 slots = slots->next;
181 }
182 }
183
184 #if defined(ADDRESS_SANITIZER)
UnpoisonCrossThreadPersistents()185 void CrossThreadPersistentRegion::UnpoisonCrossThreadPersistents() {
186 #if DCHECK_IS_ON()
187 ProcessHeap::CrossThreadPersistentMutex().AssertAcquired();
188 #endif
189 size_t persistent_count = 0;
190 for (PersistentNodeSlots* slots = slots_; slots; slots = slots->next) {
191 for (int i = 0; i < PersistentNodeSlots::kSlotCount; ++i) {
192 const PersistentNode& node = slots->slot[i];
193 if (!node.IsUnused()) {
194 ASAN_UNPOISON_MEMORY_REGION(node.Self(),
195 sizeof(CrossThreadPersistent<void*>));
196 ++persistent_count;
197 }
198 }
199 }
200 #if DCHECK_IS_ON()
201 DCHECK_EQ(persistent_count, used_node_count_);
202 #endif
203 }
204 #endif
205
206 } // namespace blink
207