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_PERSISTENT_H_
6 #define INCLUDE_CPPGC_PERSISTENT_H_
7
8 #include <type_traits>
9
10 #include "cppgc/internal/persistent-node.h"
11 #include "cppgc/internal/pointer-policies.h"
12 #include "cppgc/sentinel-pointer.h"
13 #include "cppgc/source-location.h"
14 #include "cppgc/type-traits.h"
15 #include "cppgc/visitor.h"
16 #include "v8config.h" // NOLINT(build/include_directory)
17
18 namespace cppgc {
19 namespace internal {
20
21 // PersistentBase always refers to the object as const object and defers to
22 // BasicPersistent on casting to the right type as needed.
23 class PersistentBase {
24 protected:
25 PersistentBase() = default;
PersistentBase(const void* raw)26 explicit PersistentBase(const void* raw) : raw_(raw) {}
27
GetValue() const28 const void* GetValue() const { return raw_; }
SetValue(const void* value)29 void SetValue(const void* value) { raw_ = value; }
30
GetNode() const31 PersistentNode* GetNode() const { return node_; }
SetNode(PersistentNode* node)32 void SetNode(PersistentNode* node) { node_ = node; }
33
34 // Performs a shallow clear which assumes that internal persistent nodes are
35 // destroyed elsewhere.
ClearFromGC() const36 void ClearFromGC() const {
37 raw_ = nullptr;
38 node_ = nullptr;
39 }
40
41 protected:
42 mutable const void* raw_ = nullptr;
43 mutable PersistentNode* node_ = nullptr;
44
45 friend class PersistentRegionBase;
46 };
47
48 // The basic class from which all Persistent classes are generated.
49 template <typename T, typename WeaknessPolicy, typename LocationPolicy,
50 typename CheckingPolicy>
51 class BasicPersistent final : public PersistentBase,
52 public LocationPolicy,
53 private WeaknessPolicy,
54 private CheckingPolicy {
55 public:
56 using typename WeaknessPolicy::IsStrongPersistent;
57 using PointeeType = T;
58
59 // Null-state/sentinel constructors.
BasicPersistent( const SourceLocation& loc = SourceLocation::Current())60 BasicPersistent( // NOLINT
61 const SourceLocation& loc = SourceLocation::Current())
62 : LocationPolicy(loc) {}
63
BasicPersistent(std::nullptr_t, const SourceLocation& loc = SourceLocation::Current())64 BasicPersistent(std::nullptr_t, // NOLINT
65 const SourceLocation& loc = SourceLocation::Current())
66 : LocationPolicy(loc) {}
67
BasicPersistent( SentinelPointer s, const SourceLocation& loc = SourceLocation::Current())68 BasicPersistent( // NOLINT
69 SentinelPointer s, const SourceLocation& loc = SourceLocation::Current())
70 : PersistentBase(s), LocationPolicy(loc) {}
71
72 // Raw value constructors.
BasicPersistent(T* raw, const SourceLocation& loc = SourceLocation::Current())73 BasicPersistent(T* raw, // NOLINT
74 const SourceLocation& loc = SourceLocation::Current())
75 : PersistentBase(raw), LocationPolicy(loc) {
76 if (!IsValid()) return;
77 SetNode(WeaknessPolicy::GetPersistentRegion(GetValue())
78 .AllocateNode(this, &TraceAsRoot));
79 this->CheckPointer(Get());
80 }
81
BasicPersistent(T& raw, const SourceLocation& loc = SourceLocation::Current())82 BasicPersistent(T& raw, // NOLINT
83 const SourceLocation& loc = SourceLocation::Current())
84 : BasicPersistent(&raw, loc) {}
85
86 // Copy ctor.
BasicPersistent(const BasicPersistent& other, const SourceLocation& loc = SourceLocation::Current())87 BasicPersistent(const BasicPersistent& other,
88 const SourceLocation& loc = SourceLocation::Current())
89 : BasicPersistent(other.Get(), loc) {}
90
91 // Heterogeneous ctor.
92 template <typename U, typename OtherWeaknessPolicy,
93 typename OtherLocationPolicy, typename OtherCheckingPolicy,
94 typename = std::enable_if_t<std::is_base_of<T, U>::value>>
BasicPersistent( const BasicPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy, OtherCheckingPolicy>& other, const SourceLocation& loc = SourceLocation::Current())95 BasicPersistent(
96 const BasicPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
97 OtherCheckingPolicy>& other,
98 const SourceLocation& loc = SourceLocation::Current())
99 : BasicPersistent(other.Get(), loc) {}
100
101 // Move ctor. The heterogeneous move ctor is not supported since e.g.
102 // persistent can't reuse persistent node from weak persistent.
103 BasicPersistent(
104 BasicPersistent&& other,
105 const SourceLocation& loc = SourceLocation::Current()) noexcept
move(other)106 : PersistentBase(std::move(other)), LocationPolicy(std::move(other)) {
107 if (!IsValid()) return;
108 GetNode()->UpdateOwner(this);
109 other.SetValue(nullptr);
110 other.SetNode(nullptr);
111 this->CheckPointer(Get());
112 }
113
114 // Constructor from member.
115 template <typename U, typename MemberBarrierPolicy,
116 typename MemberWeaknessTag, typename MemberCheckingPolicy,
117 typename MemberStorageType,
118 typename = std::enable_if_t<std::is_base_of<T, U>::value>>
BasicPersistent(const internal::BasicMember< U, MemberBarrierPolicy, MemberWeaknessTag, MemberCheckingPolicy, MemberStorageType>& member, const SourceLocation& loc = SourceLocation::Current())119 BasicPersistent(const internal::BasicMember<
120 U, MemberBarrierPolicy, MemberWeaknessTag,
121 MemberCheckingPolicy, MemberStorageType>& member,
122 const SourceLocation& loc = SourceLocation::Current())
123 : BasicPersistent(member.Get(), loc) {}
124
~BasicPersistent()125 ~BasicPersistent() { Clear(); }
126
127 // Copy assignment.
operator =(const BasicPersistent& other)128 BasicPersistent& operator=(const BasicPersistent& other) {
129 return operator=(other.Get());
130 }
131
132 template <typename U, typename OtherWeaknessPolicy,
133 typename OtherLocationPolicy, typename OtherCheckingPolicy,
134 typename = std::enable_if_t<std::is_base_of<T, U>::value>>
operator =( const BasicPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy, OtherCheckingPolicy>& other)135 BasicPersistent& operator=(
136 const BasicPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
137 OtherCheckingPolicy>& other) {
138 return operator=(other.Get());
139 }
140
141 // Move assignment.
142 BasicPersistent& operator=(BasicPersistent&& other) noexcept {
143 if (this == &other) return *this;
144 Clear();
145 PersistentBase::operator=(std::move(other));
146 LocationPolicy::operator=(std::move(other));
147 if (!IsValid()) return *this;
148 GetNode()->UpdateOwner(this);
149 other.SetValue(nullptr);
150 other.SetNode(nullptr);
151 this->CheckPointer(Get());
152 return *this;
153 }
154
155 // Assignment from member.
156 template <typename U, typename MemberBarrierPolicy,
157 typename MemberWeaknessTag, typename MemberCheckingPolicy,
158 typename MemberStorageType,
159 typename = std::enable_if_t<std::is_base_of<T, U>::value>>
operator =( const internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag, MemberCheckingPolicy, MemberStorageType>& member)160 BasicPersistent& operator=(
161 const internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag,
162 MemberCheckingPolicy, MemberStorageType>&
163 member) {
164 return operator=(member.Get());
165 }
166
operator =(T* other)167 BasicPersistent& operator=(T* other) {
168 Assign(other);
169 return *this;
170 }
171
operator =(std::nullptr_t)172 BasicPersistent& operator=(std::nullptr_t) {
173 Clear();
174 return *this;
175 }
176
operator =(SentinelPointer s)177 BasicPersistent& operator=(SentinelPointer s) {
178 Assign(s);
179 return *this;
180 }
181
operator bool() const182 explicit operator bool() const { return Get(); }
operator T*() const183 operator T*() const { return Get(); }
operator ->() const184 T* operator->() const { return Get(); }
operator *() const185 T& operator*() const { return *Get(); }
186
187 // CFI cast exemption to allow passing SentinelPointer through T* and support
188 // heterogeneous assignments between different Member and Persistent handles
189 // based on their actual types.
Get() const190 V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") T* Get() const {
191 // The const_cast below removes the constness from PersistentBase storage.
192 // The following static_cast re-adds any constness if specified through the
193 // user-visible template parameter T.
194 return static_cast<T*>(const_cast<void*>(GetValue()));
195 }
196
Clear()197 void Clear() {
198 // Simplified version of `Assign()` to allow calling without a complete type
199 // `T`.
200 if (IsValid()) {
201 WeaknessPolicy::GetPersistentRegion(GetValue()).FreeNode(GetNode());
202 SetNode(nullptr);
203 }
204 SetValue(nullptr);
205 }
206
Release()207 T* Release() {
208 T* result = Get();
209 Clear();
210 return result;
211 }
212
213 template <typename U, typename OtherWeaknessPolicy = WeaknessPolicy,
214 typename OtherLocationPolicy = LocationPolicy,
215 typename OtherCheckingPolicy = CheckingPolicy>
216 BasicPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
217 OtherCheckingPolicy>
To() const218 To() const {
219 return BasicPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
220 OtherCheckingPolicy>(static_cast<U*>(Get()));
221 }
222
223 private:
TraceAsRoot(RootVisitor& root_visitor, const void* ptr)224 static void TraceAsRoot(RootVisitor& root_visitor, const void* ptr) {
225 root_visitor.Trace(*static_cast<const BasicPersistent*>(ptr));
226 }
227
IsValid() const228 bool IsValid() const {
229 // Ideally, handling kSentinelPointer would be done by the embedder. On the
230 // other hand, having Persistent aware of it is beneficial since no node
231 // gets wasted.
232 return GetValue() != nullptr && GetValue() != kSentinelPointer;
233 }
234
Assign(T* ptr)235 void Assign(T* ptr) {
236 if (IsValid()) {
237 if (ptr && ptr != kSentinelPointer) {
238 // Simply assign the pointer reusing the existing node.
239 SetValue(ptr);
240 this->CheckPointer(ptr);
241 return;
242 }
243 WeaknessPolicy::GetPersistentRegion(GetValue()).FreeNode(GetNode());
244 SetNode(nullptr);
245 }
246 SetValue(ptr);
247 if (!IsValid()) return;
248 SetNode(WeaknessPolicy::GetPersistentRegion(GetValue())
249 .AllocateNode(this, &TraceAsRoot));
250 this->CheckPointer(Get());
251 }
252
ClearFromGC() const253 void ClearFromGC() const {
254 if (IsValid()) {
255 WeaknessPolicy::GetPersistentRegion(GetValue()).FreeNode(GetNode());
256 PersistentBase::ClearFromGC();
257 }
258 }
259
260 // Set Get() for details.
261 V8_CLANG_NO_SANITIZE("cfi-unrelated-cast")
GetFromGC() const262 T* GetFromGC() const {
263 return static_cast<T*>(const_cast<void*>(GetValue()));
264 }
265
266 friend class internal::RootVisitor;
267 };
268
269 template <typename T1, typename WeaknessPolicy1, typename LocationPolicy1,
270 typename CheckingPolicy1, typename T2, typename WeaknessPolicy2,
271 typename LocationPolicy2, typename CheckingPolicy2>
operator ==(const BasicPersistent<T1, WeaknessPolicy1, LocationPolicy1, CheckingPolicy1>& p1, const BasicPersistent<T2, WeaknessPolicy2, LocationPolicy2, CheckingPolicy2>& p2)272 bool operator==(const BasicPersistent<T1, WeaknessPolicy1, LocationPolicy1,
273 CheckingPolicy1>& p1,
274 const BasicPersistent<T2, WeaknessPolicy2, LocationPolicy2,
275 CheckingPolicy2>& p2) {
276 return p1.Get() == p2.Get();
277 }
278
279 template <typename T1, typename WeaknessPolicy1, typename LocationPolicy1,
280 typename CheckingPolicy1, typename T2, typename WeaknessPolicy2,
281 typename LocationPolicy2, typename CheckingPolicy2>
operator !=(const BasicPersistent<T1, WeaknessPolicy1, LocationPolicy1, CheckingPolicy1>& p1, const BasicPersistent<T2, WeaknessPolicy2, LocationPolicy2, CheckingPolicy2>& p2)282 bool operator!=(const BasicPersistent<T1, WeaknessPolicy1, LocationPolicy1,
283 CheckingPolicy1>& p1,
284 const BasicPersistent<T2, WeaknessPolicy2, LocationPolicy2,
285 CheckingPolicy2>& p2) {
286 return !(p1 == p2);
287 }
288
289 template <typename T1, typename PersistentWeaknessPolicy,
290 typename PersistentLocationPolicy, typename PersistentCheckingPolicy,
291 typename T2, typename MemberWriteBarrierPolicy,
292 typename MemberWeaknessTag, typename MemberCheckingPolicy,
293 typename MemberStorageType>
operator ==( const BasicPersistent<T1, PersistentWeaknessPolicy, PersistentLocationPolicy, PersistentCheckingPolicy>& p, const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy, MemberCheckingPolicy, MemberStorageType>& m)294 bool operator==(
295 const BasicPersistent<T1, PersistentWeaknessPolicy,
296 PersistentLocationPolicy, PersistentCheckingPolicy>&
297 p,
298 const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy,
299 MemberCheckingPolicy, MemberStorageType>& m) {
300 return p.Get() == m.Get();
301 }
302
303 template <typename T1, typename PersistentWeaknessPolicy,
304 typename PersistentLocationPolicy, typename PersistentCheckingPolicy,
305 typename T2, typename MemberWriteBarrierPolicy,
306 typename MemberWeaknessTag, typename MemberCheckingPolicy,
307 typename MemberStorageType>
operator !=( const BasicPersistent<T1, PersistentWeaknessPolicy, PersistentLocationPolicy, PersistentCheckingPolicy>& p, const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy, MemberCheckingPolicy, MemberStorageType>& m)308 bool operator!=(
309 const BasicPersistent<T1, PersistentWeaknessPolicy,
310 PersistentLocationPolicy, PersistentCheckingPolicy>&
311 p,
312 const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy,
313 MemberCheckingPolicy, MemberStorageType>& m) {
314 return !(p == m);
315 }
316
317 template <typename T1, typename MemberWriteBarrierPolicy,
318 typename MemberWeaknessTag, typename MemberCheckingPolicy,
319 typename MemberStorageType, typename T2,
320 typename PersistentWeaknessPolicy, typename PersistentLocationPolicy,
321 typename PersistentCheckingPolicy>
operator ==( const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy, MemberCheckingPolicy, MemberStorageType>& m, const BasicPersistent<T1, PersistentWeaknessPolicy, PersistentLocationPolicy, PersistentCheckingPolicy>& p)322 bool operator==(
323 const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy,
324 MemberCheckingPolicy, MemberStorageType>& m,
325 const BasicPersistent<T1, PersistentWeaknessPolicy,
326 PersistentLocationPolicy, PersistentCheckingPolicy>&
327 p) {
328 return m.Get() == p.Get();
329 }
330
331 template <typename T1, typename MemberWriteBarrierPolicy,
332 typename MemberWeaknessTag, typename MemberCheckingPolicy,
333 typename MemberStorageType, typename T2,
334 typename PersistentWeaknessPolicy, typename PersistentLocationPolicy,
335 typename PersistentCheckingPolicy>
operator !=( const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy, MemberCheckingPolicy, MemberStorageType>& m, const BasicPersistent<T1, PersistentWeaknessPolicy, PersistentLocationPolicy, PersistentCheckingPolicy>& p)336 bool operator!=(
337 const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy,
338 MemberCheckingPolicy, MemberStorageType>& m,
339 const BasicPersistent<T1, PersistentWeaknessPolicy,
340 PersistentLocationPolicy, PersistentCheckingPolicy>&
341 p) {
342 return !(m == p);
343 }
344
345 template <typename T, typename LocationPolicy, typename CheckingPolicy>
346 struct IsWeak<BasicPersistent<T, internal::WeakPersistentPolicy, LocationPolicy,
347 CheckingPolicy>> : std::true_type {};
348 } // namespace internal
349
350 /**
351 * Persistent is a way to create a strong pointer from an off-heap object to
352 * another on-heap object. As long as the Persistent handle is alive the GC will
353 * keep the object pointed to alive. The Persistent handle is always a GC root
354 * from the point of view of the GC. Persistent must be constructed and
355 * destructed in the same thread.
356 */
357 template <typename T>
358 using Persistent =
359 internal::BasicPersistent<T, internal::StrongPersistentPolicy>;
360
361 /**
362 * WeakPersistent is a way to create a weak pointer from an off-heap object to
363 * an on-heap object. The pointer is automatically cleared when the pointee gets
364 * collected. WeakPersistent must be constructed and destructed in the same
365 * thread.
366 */
367 template <typename T>
368 using WeakPersistent =
369 internal::BasicPersistent<T, internal::WeakPersistentPolicy>;
370
371 } // namespace cppgc
372
373 #endif // INCLUDE_CPPGC_PERSISTENT_H_
374