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