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/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)18   void Trace(Visitor* visitor) {}
19 };
20 }
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