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