Coverage Report

Created: 2025-10-31 09:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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_