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