1 // Copyright 2020 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 "include/cppgc/internal/persistent-node.h"
6
7 #include <algorithm>
8 #include <numeric>
9
10 #include "include/cppgc/cross-thread-persistent.h"
11 #include "include/cppgc/persistent.h"
12 #include "src/base/platform/platform.h"
13 #include "src/heap/cppgc/platform.h"
14 #include "src/heap/cppgc/process-heap.h"
15
16 namespace cppgc {
17 namespace internal {
18
PersistentRegionBase( const FatalOutOfMemoryHandler& oom_handler)19 PersistentRegionBase::PersistentRegionBase(
20 const FatalOutOfMemoryHandler& oom_handler)
21 : oom_handler_(oom_handler) {}
22
~PersistentRegionBase()23 PersistentRegionBase::~PersistentRegionBase() { ClearAllUsedNodes(); }
24
25 template <typename PersistentBaseClass>
ClearAllUsedNodes()26 void PersistentRegionBase::ClearAllUsedNodes() {
27 for (auto& slots : nodes_) {
28 for (auto& node : *slots) {
29 if (!node.IsUsed()) continue;
30
31 static_cast<PersistentBaseClass*>(node.owner())->ClearFromGC();
32
33 // Add nodes back to the free list to allow reusing for subsequent
34 // creation calls.
35 node.InitializeAsFreeNode(free_list_head_);
36 free_list_head_ = &node;
37 CPPGC_DCHECK(nodes_in_use_ > 0);
38 nodes_in_use_--;
39 }
40 }
41 CPPGC_DCHECK(0u == nodes_in_use_);
42 }
43
44 template void
45 PersistentRegionBase::ClearAllUsedNodes<CrossThreadPersistentBase>();
46 template void PersistentRegionBase::ClearAllUsedNodes<PersistentBase>();
47
ClearAllUsedNodes()48 void PersistentRegionBase::ClearAllUsedNodes() {
49 ClearAllUsedNodes<PersistentBase>();
50 }
51
NodesInUse() const52 size_t PersistentRegionBase::NodesInUse() const {
53 #ifdef DEBUG
54 const size_t accumulated_nodes_in_use_ = std::accumulate(
55 nodes_.cbegin(), nodes_.cend(), 0u, [](size_t acc, const auto& slots) {
56 return acc + std::count_if(slots->cbegin(), slots->cend(),
57 [](const PersistentNode& node) {
58 return node.IsUsed();
59 });
60 });
61 DCHECK_EQ(accumulated_nodes_in_use_, nodes_in_use_);
62 #endif // DEBUG
63 return nodes_in_use_;
64 }
65
RefillFreeList()66 void PersistentRegionBase::RefillFreeList() {
67 auto node_slots = std::make_unique<PersistentNodeSlots>();
68 if (!node_slots.get()) {
69 oom_handler_("Oilpan: PersistentRegionBase::RefillFreeList()");
70 }
71 nodes_.push_back(std::move(node_slots));
72 for (auto& node : *nodes_.back()) {
73 node.InitializeAsFreeNode(free_list_head_);
74 free_list_head_ = &node;
75 }
76 }
77
RefillFreeListAndAllocateNode( void* owner, TraceCallback trace)78 PersistentNode* PersistentRegionBase::RefillFreeListAndAllocateNode(
79 void* owner, TraceCallback trace) {
80 RefillFreeList();
81 auto* node = TryAllocateNodeFromFreeList(owner, trace);
82 CPPGC_DCHECK(node);
83 return node;
84 }
85
Trace(Visitor* visitor)86 void PersistentRegionBase::Trace(Visitor* visitor) {
87 free_list_head_ = nullptr;
88 for (auto& slots : nodes_) {
89 bool is_empty = true;
90 for (auto& node : *slots) {
91 if (node.IsUsed()) {
92 node.Trace(visitor);
93 is_empty = false;
94 } else {
95 node.InitializeAsFreeNode(free_list_head_);
96 free_list_head_ = &node;
97 }
98 }
99 if (is_empty) {
100 PersistentNode* first_next = (*slots)[0].FreeListNext();
101 // First next was processed first in the loop above, guaranteeing that it
102 // either points to null or into a different node block.
103 CPPGC_DCHECK(!first_next || first_next < &slots->front() ||
104 first_next > &slots->back());
105 free_list_head_ = first_next;
106 slots.reset();
107 }
108 }
109 nodes_.erase(std::remove_if(nodes_.begin(), nodes_.end(),
110 [](const auto& ptr) { return !ptr; }),
111 nodes_.end());
112 }
113
PersistentRegion(const FatalOutOfMemoryHandler& oom_handler)114 PersistentRegion::PersistentRegion(const FatalOutOfMemoryHandler& oom_handler)
115 : PersistentRegionBase(oom_handler),
116 creation_thread_id_(v8::base::OS::GetCurrentThreadId()) {
117 USE(creation_thread_id_);
118 }
119
IsCreationThread()120 bool PersistentRegion::IsCreationThread() {
121 return creation_thread_id_ == v8::base::OS::GetCurrentThreadId();
122 }
123
PersistentRegionLock()124 PersistentRegionLock::PersistentRegionLock() {
125 g_process_mutex.Pointer()->Lock();
126 }
127
~PersistentRegionLock()128 PersistentRegionLock::~PersistentRegionLock() {
129 g_process_mutex.Pointer()->Unlock();
130 }
131
132 // static
AssertLocked()133 void PersistentRegionLock::AssertLocked() {
134 return g_process_mutex.Pointer()->AssertHeld();
135 }
136
CrossThreadPersistentRegion( const FatalOutOfMemoryHandler& oom_handler)137 CrossThreadPersistentRegion::CrossThreadPersistentRegion(
138 const FatalOutOfMemoryHandler& oom_handler)
139 : PersistentRegionBase(oom_handler) {}
140
~CrossThreadPersistentRegion()141 CrossThreadPersistentRegion::~CrossThreadPersistentRegion() {
142 PersistentRegionLock guard;
143 PersistentRegionBase::ClearAllUsedNodes<CrossThreadPersistentBase>();
144 nodes_.clear();
145 // PersistentRegionBase destructor will be a noop.
146 }
147
Trace(Visitor* visitor)148 void CrossThreadPersistentRegion::Trace(Visitor* visitor) {
149 PersistentRegionLock::AssertLocked();
150 PersistentRegionBase::Trace(visitor);
151 }
152
NodesInUse() const153 size_t CrossThreadPersistentRegion::NodesInUse() const {
154 // This method does not require a lock.
155 return PersistentRegionBase::NodesInUse();
156 }
157
ClearAllUsedNodes()158 void CrossThreadPersistentRegion::ClearAllUsedNodes() {
159 PersistentRegionLock::AssertLocked();
160 PersistentRegionBase::ClearAllUsedNodes<CrossThreadPersistentBase>();
161 }
162
163 } // namespace internal
164 } // namespace cppgc
165