/src/node/deps/v8/include/cppgc/internal/persistent-node.h
Line | Count | Source |
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 | | #ifndef INCLUDE_CPPGC_INTERNAL_PERSISTENT_NODE_H_ |
6 | | #define INCLUDE_CPPGC_INTERNAL_PERSISTENT_NODE_H_ |
7 | | |
8 | | #include <array> |
9 | | #include <memory> |
10 | | #include <vector> |
11 | | |
12 | | #include "cppgc/internal/logging.h" |
13 | | #include "cppgc/trace-trait.h" |
14 | | #include "v8config.h" // NOLINT(build/include_directory) |
15 | | |
16 | | namespace cppgc { |
17 | | namespace internal { |
18 | | |
19 | | class CrossThreadPersistentRegion; |
20 | | class FatalOutOfMemoryHandler; |
21 | | class HeapBase; |
22 | | class RootVisitor; |
23 | | |
24 | | // PersistentNode represents a variant of two states: |
25 | | // 1) traceable node with a back pointer to the Persistent object; |
26 | | // 2) freelist entry. |
27 | | class PersistentNode final { |
28 | | public: |
29 | | PersistentNode() = default; |
30 | | |
31 | | PersistentNode(const PersistentNode&) = delete; |
32 | | PersistentNode& operator=(const PersistentNode&) = delete; |
33 | | |
34 | 0 | void InitializeAsUsedNode(void* owner, TraceRootCallback trace) { |
35 | 0 | CPPGC_DCHECK(trace); |
36 | 0 | owner_ = owner; |
37 | 0 | trace_ = trace; |
38 | 0 | } |
39 | | |
40 | 0 | void InitializeAsFreeNode(PersistentNode* next) { |
41 | 0 | next_ = next; |
42 | 0 | trace_ = nullptr; |
43 | 0 | } |
44 | | |
45 | 0 | void UpdateOwner(void* owner) { |
46 | 0 | CPPGC_DCHECK(IsUsed()); |
47 | 0 | owner_ = owner; |
48 | 0 | } |
49 | | |
50 | 0 | PersistentNode* FreeListNext() const { |
51 | 0 | CPPGC_DCHECK(!IsUsed()); |
52 | 0 | return next_; |
53 | 0 | } |
54 | | |
55 | 0 | void Trace(RootVisitor& root_visitor) const { |
56 | 0 | CPPGC_DCHECK(IsUsed()); |
57 | 0 | trace_(root_visitor, owner_); |
58 | 0 | } |
59 | | |
60 | 0 | bool IsUsed() const { return trace_; } |
61 | | |
62 | 0 | void* owner() const { |
63 | 0 | CPPGC_DCHECK(IsUsed()); |
64 | 0 | return owner_; |
65 | 0 | } |
66 | | |
67 | | private: |
68 | | // PersistentNode acts as a designated union: |
69 | | // If trace_ != nullptr, owner_ points to the corresponding Persistent handle. |
70 | | // Otherwise, next_ points to the next freed PersistentNode. |
71 | | union { |
72 | | void* owner_ = nullptr; |
73 | | PersistentNode* next_; |
74 | | }; |
75 | | TraceRootCallback trace_ = nullptr; |
76 | | }; |
77 | | |
78 | | class V8_EXPORT PersistentRegionBase { |
79 | | using PersistentNodeSlots = std::array<PersistentNode, 256u>; |
80 | | |
81 | | public: |
82 | | // Clears Persistent fields to avoid stale pointers after heap teardown. |
83 | | ~PersistentRegionBase(); |
84 | | |
85 | | PersistentRegionBase(const PersistentRegionBase&) = delete; |
86 | | PersistentRegionBase& operator=(const PersistentRegionBase&) = delete; |
87 | | |
88 | | void Iterate(RootVisitor&); |
89 | | |
90 | | size_t NodesInUse() const; |
91 | | |
92 | | void ClearAllUsedNodes(); |
93 | | |
94 | | protected: |
95 | | explicit PersistentRegionBase(const FatalOutOfMemoryHandler& oom_handler); |
96 | | |
97 | | PersistentNode* TryAllocateNodeFromFreeList(void* owner, |
98 | 0 | TraceRootCallback trace) { |
99 | 0 | PersistentNode* node = nullptr; |
100 | 0 | if (V8_LIKELY(free_list_head_)) { |
101 | 0 | node = free_list_head_; |
102 | 0 | free_list_head_ = free_list_head_->FreeListNext(); |
103 | 0 | CPPGC_DCHECK(!node->IsUsed()); |
104 | 0 | node->InitializeAsUsedNode(owner, trace); |
105 | 0 | nodes_in_use_++; |
106 | 0 | } |
107 | 0 | return node; |
108 | 0 | } |
109 | | |
110 | 0 | void FreeNode(PersistentNode* node) { |
111 | 0 | CPPGC_DCHECK(node); |
112 | 0 | CPPGC_DCHECK(node->IsUsed()); |
113 | 0 | node->InitializeAsFreeNode(free_list_head_); |
114 | 0 | free_list_head_ = node; |
115 | 0 | CPPGC_DCHECK(nodes_in_use_ > 0); |
116 | 0 | nodes_in_use_--; |
117 | 0 | } |
118 | | |
119 | | PersistentNode* RefillFreeListAndAllocateNode(void* owner, |
120 | | TraceRootCallback trace); |
121 | | |
122 | | private: |
123 | | template <typename PersistentBaseClass> |
124 | | void ClearAllUsedNodes(); |
125 | | |
126 | | void RefillFreeList(); |
127 | | |
128 | | std::vector<std::unique_ptr<PersistentNodeSlots>> nodes_; |
129 | | PersistentNode* free_list_head_ = nullptr; |
130 | | size_t nodes_in_use_ = 0; |
131 | | const FatalOutOfMemoryHandler& oom_handler_; |
132 | | |
133 | | friend class CrossThreadPersistentRegion; |
134 | | }; |
135 | | |
136 | | // Variant of PersistentRegionBase that checks whether the allocation and |
137 | | // freeing happens only on the thread that created the heap. |
138 | | class V8_EXPORT PersistentRegion final : public PersistentRegionBase { |
139 | | public: |
140 | | V8_INLINE PersistentRegion(const HeapBase& heap, |
141 | | const FatalOutOfMemoryHandler& oom_handler) |
142 | 0 | : PersistentRegionBase(oom_handler), heap_(heap) { |
143 | 0 | CPPGC_DCHECK(IsCreationThread()); |
144 | 0 | } |
145 | | // Clears Persistent fields to avoid stale pointers after heap teardown. |
146 | | ~PersistentRegion() = default; |
147 | | |
148 | | PersistentRegion(const PersistentRegion&) = delete; |
149 | | PersistentRegion& operator=(const PersistentRegion&) = delete; |
150 | | |
151 | 0 | V8_INLINE PersistentNode* AllocateNode(void* owner, TraceRootCallback trace) { |
152 | 0 | CPPGC_DCHECK(IsCreationThread()); |
153 | 0 | auto* node = TryAllocateNodeFromFreeList(owner, trace); |
154 | 0 | if (V8_LIKELY(node)) return node; |
155 | | |
156 | | // Slow path allocation allows for checking thread correspondence. |
157 | 0 | CPPGC_CHECK(IsCreationThread()); |
158 | 0 | return RefillFreeListAndAllocateNode(owner, trace); |
159 | 0 | } |
160 | | |
161 | 0 | V8_INLINE void FreeNode(PersistentNode* node) { |
162 | 0 | CPPGC_DCHECK(IsCreationThread()); |
163 | 0 | PersistentRegionBase::FreeNode(node); |
164 | 0 | } |
165 | | |
166 | | private: |
167 | | bool IsCreationThread(); |
168 | | |
169 | | const HeapBase& heap_; |
170 | | }; |
171 | | |
172 | | // CrossThreadPersistent uses PersistentRegionBase but protects it using this |
173 | | // lock when needed. |
174 | | class V8_EXPORT PersistentRegionLock final { |
175 | | public: |
176 | | PersistentRegionLock(); |
177 | | ~PersistentRegionLock(); |
178 | | |
179 | | static void AssertLocked(); |
180 | | }; |
181 | | |
182 | | // Variant of PersistentRegionBase that checks whether the PersistentRegionLock |
183 | | // is locked. |
184 | | class V8_EXPORT CrossThreadPersistentRegion final |
185 | | : protected PersistentRegionBase { |
186 | | public: |
187 | | explicit CrossThreadPersistentRegion(const FatalOutOfMemoryHandler&); |
188 | | // Clears Persistent fields to avoid stale pointers after heap teardown. |
189 | | ~CrossThreadPersistentRegion(); |
190 | | |
191 | | CrossThreadPersistentRegion(const CrossThreadPersistentRegion&) = delete; |
192 | | CrossThreadPersistentRegion& operator=(const CrossThreadPersistentRegion&) = |
193 | | delete; |
194 | | |
195 | 0 | V8_INLINE PersistentNode* AllocateNode(void* owner, TraceRootCallback trace) { |
196 | 0 | PersistentRegionLock::AssertLocked(); |
197 | 0 | auto* node = TryAllocateNodeFromFreeList(owner, trace); |
198 | 0 | if (V8_LIKELY(node)) return node; |
199 | 0 |
|
200 | 0 | return RefillFreeListAndAllocateNode(owner, trace); |
201 | 0 | } |
202 | | |
203 | 0 | V8_INLINE void FreeNode(PersistentNode* node) { |
204 | 0 | PersistentRegionLock::AssertLocked(); |
205 | 0 | PersistentRegionBase::FreeNode(node); |
206 | 0 | } |
207 | | |
208 | | void Iterate(RootVisitor&); |
209 | | |
210 | | size_t NodesInUse() const; |
211 | | |
212 | | void ClearAllUsedNodes(); |
213 | | }; |
214 | | |
215 | | } // namespace internal |
216 | | |
217 | | } // namespace cppgc |
218 | | |
219 | | #endif // INCLUDE_CPPGC_INTERNAL_PERSISTENT_NODE_H_ |