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
20 class Visitor;
21
22 namespace internal {
23
24 // PersistentBase always refers to the object as const object and defers to
25 // BasicPersistent on casting to the right type as needed.
26 class PersistentBase {
27 protected:
28 PersistentBase() = default;
PersistentBase(const void* raw)29 explicit PersistentBase(const void* raw) : raw_(raw) {}
30
GetValue() const31 const void* GetValue() const { return raw_; }
SetValue(const void* value)32 void SetValue(const void* value) { raw_ = value; }
33
GetNode() const34 PersistentNode* GetNode() const { return node_; }
SetNode(PersistentNode* node)35 void SetNode(PersistentNode* node) { node_ = node; }
36
37 // Performs a shallow clear which assumes that internal persistent nodes are
38 // destroyed elsewhere.
ClearFromGC() const39 void ClearFromGC() const {
40 raw_ = nullptr;
41 node_ = nullptr;
42 }
43
44 protected:
45 mutable const void* raw_ = nullptr;
46 mutable PersistentNode* node_ = nullptr;
47
48 friend class PersistentRegionBase;
49 };
50
51 // The basic class from which all Persistent classes are generated.
52 template <typename T, typename WeaknessPolicy, typename LocationPolicy,
53 typename CheckingPolicy>
54 class BasicPersistent final : public PersistentBase,
55 public LocationPolicy,
56 private WeaknessPolicy,
57 private CheckingPolicy {
58 public:
59 using typename WeaknessPolicy::IsStrongPersistent;
60 using PointeeType = T;
61
62 // Null-state/sentinel constructors.
BasicPersistent( const SourceLocation& loc = SourceLocation::Current())63 BasicPersistent( // NOLINT
64 const SourceLocation& loc = SourceLocation::Current())
65 : LocationPolicy(loc) {}
66
BasicPersistent(std::nullptr_t, const SourceLocation& loc = SourceLocation::Current())67 BasicPersistent(std::nullptr_t, // NOLINT
68 const SourceLocation& loc = SourceLocation::Current())
69 : LocationPolicy(loc) {}
70
BasicPersistent( SentinelPointer s, const SourceLocation& loc = SourceLocation::Current())71 BasicPersistent( // NOLINT
72 SentinelPointer s, const SourceLocation& loc = SourceLocation::Current())
73 : PersistentBase(s), LocationPolicy(loc) {}
74
75 // Raw value constructors.
BasicPersistent(T* raw, const SourceLocation& loc = SourceLocation::Current())76 BasicPersistent(T* raw, // NOLINT
77 const SourceLocation& loc = SourceLocation::Current())
78 : PersistentBase(raw), LocationPolicy(loc) {
79 if (!IsValid()) return;
80 SetNode(WeaknessPolicy::GetPersistentRegion(GetValue())
81 .AllocateNode(this, &BasicPersistent::Trace));
82 this->CheckPointer(Get());
83 }
84
BasicPersistent(T& raw, const SourceLocation& loc = SourceLocation::Current())85 BasicPersistent(T& raw, // NOLINT
86 const SourceLocation& loc = SourceLocation::Current())
87 : BasicPersistent(&raw, loc) {}
88
89 // Copy ctor.
BasicPersistent(const BasicPersistent& other, const SourceLocation& loc = SourceLocation::Current())90 BasicPersistent(const BasicPersistent& other,
91 const SourceLocation& loc = SourceLocation::Current())
92 : BasicPersistent(other.Get(), loc) {}
93
94 // Heterogeneous ctor.
95 template <typename U, typename OtherWeaknessPolicy,
96 typename OtherLocationPolicy, typename OtherCheckingPolicy,
97 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())98 BasicPersistent(
99 const BasicPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
100 OtherCheckingPolicy>& other,
101 const SourceLocation& loc = SourceLocation::Current())
102 : BasicPersistent(other.Get(), loc) {}
103
104 // Move ctor. The heterogeneous move ctor is not supported since e.g.
105 // persistent can't reuse persistent node from weak persistent.
106 BasicPersistent(
107 BasicPersistent&& other,
108 const SourceLocation& loc = SourceLocation::Current()) noexcept
move(other)109 : PersistentBase(std::move(other)), LocationPolicy(std::move(other)) {
110 if (!IsValid()) return;
111 GetNode()->UpdateOwner(this);
112 other.SetValue(nullptr);
113 other.SetNode(nullptr);
114 this->CheckPointer(Get());
115 }
116
117 // Constructor from member.
118 template <typename U, typename MemberBarrierPolicy,
119 typename MemberWeaknessTag, typename MemberCheckingPolicy,
120 typename = std::enable_if_t<std::is_base_of<T, U>::value>>
BasicPersistent( const internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag, MemberCheckingPolicy>& member, const SourceLocation& loc = SourceLocation::Current())121 BasicPersistent(
122 const internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag,
123 MemberCheckingPolicy>& member,
124 const SourceLocation& loc = SourceLocation::Current())
125 : BasicPersistent(member.Get(), loc) {}
126
~BasicPersistent()127 ~BasicPersistent() { Clear(); }
128
129 // Copy assignment.
operator =(const BasicPersistent& other)130 BasicPersistent& operator=(const BasicPersistent& other) {
131 return operator=(other.Get());
132 }
133
134 template <typename U, typename OtherWeaknessPolicy,
135 typename OtherLocationPolicy, typename OtherCheckingPolicy,
136 typename = std::enable_if_t<std::is_base_of<T, U>::value>>
operator =( const BasicPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy, OtherCheckingPolicy>& other)137 BasicPersistent& operator=(
138 const BasicPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
139 OtherCheckingPolicy>& other) {
140 return operator=(other.Get());
141 }
142
143 // Move assignment.
144 BasicPersistent& operator=(BasicPersistent&& other) noexcept {
145 if (this == &other) return *this;
146 Clear();
147 PersistentBase::operator=(std::move(other));
148 LocationPolicy::operator=(std::move(other));
149 if (!IsValid()) return *this;
150 GetNode()->UpdateOwner(this);
151 other.SetValue(nullptr);
152 other.SetNode(nullptr);
153 this->CheckPointer(Get());
154 return *this;
155 }
156
157 // Assignment from member.
158 template <typename U, typename MemberBarrierPolicy,
159 typename MemberWeaknessTag, typename MemberCheckingPolicy,
160 typename = std::enable_if_t<std::is_base_of<T, U>::value>>
operator =( const internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag, MemberCheckingPolicy>& member)161 BasicPersistent& operator=(
162 const internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag,
163 MemberCheckingPolicy>& 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:
Trace(Visitor* v, const void* ptr)224 static void Trace(Visitor* v, const void* ptr) {
225 const auto* persistent = static_cast<const BasicPersistent*>(ptr);
226 v->TraceRoot(*persistent, persistent->Location());
227 }
228
IsValid() const229 bool IsValid() const {
230 // Ideally, handling kSentinelPointer would be done by the embedder. On the
231 // other hand, having Persistent aware of it is beneficial since no node
232 // gets wasted.
233 return GetValue() != nullptr && GetValue() != kSentinelPointer;
234 }
235
Assign(T* ptr)236 void Assign(T* ptr) {
237 if (IsValid()) {
238 if (ptr && ptr != kSentinelPointer) {
239 // Simply assign the pointer reusing the existing node.
240 SetValue(ptr);
241 this->CheckPointer(ptr);
242 return;
243 }
244 WeaknessPolicy::GetPersistentRegion(GetValue()).FreeNode(GetNode());
245 SetNode(nullptr);
246 }
247 SetValue(ptr);
248 if (!IsValid()) return;
249 SetNode(WeaknessPolicy::GetPersistentRegion(GetValue())
250 .AllocateNode(this, &BasicPersistent::Trace));
251 this->CheckPointer(Get());
252 }
253
ClearFromGC() const254 void ClearFromGC() const {
255 if (IsValid()) {
256 WeaknessPolicy::GetPersistentRegion(GetValue()).FreeNode(GetNode());
257 PersistentBase::ClearFromGC();
258 }
259 }
260
261 // Set Get() for details.
262 V8_CLANG_NO_SANITIZE("cfi-unrelated-cast")
GetFromGC() const263 T* GetFromGC() const {
264 return static_cast<T*>(const_cast<void*>(GetValue()));
265 }
266
267 friend class cppgc::Visitor;
268 };
269
270 template <typename T1, typename WeaknessPolicy1, typename LocationPolicy1,
271 typename CheckingPolicy1, typename T2, typename WeaknessPolicy2,
272 typename LocationPolicy2, typename CheckingPolicy2>
operator ==(const BasicPersistent<T1, WeaknessPolicy1, LocationPolicy1, CheckingPolicy1>& p1, const BasicPersistent<T2, WeaknessPolicy2, LocationPolicy2, CheckingPolicy2>& p2)273 bool operator==(const BasicPersistent<T1, WeaknessPolicy1, LocationPolicy1,
274 CheckingPolicy1>& p1,
275 const BasicPersistent<T2, WeaknessPolicy2, LocationPolicy2,
276 CheckingPolicy2>& p2) {
277 return p1.Get() == p2.Get();
278 }
279
280 template <typename T1, typename WeaknessPolicy1, typename LocationPolicy1,
281 typename CheckingPolicy1, typename T2, typename WeaknessPolicy2,
282 typename LocationPolicy2, typename CheckingPolicy2>
operator !=(const BasicPersistent<T1, WeaknessPolicy1, LocationPolicy1, CheckingPolicy1>& p1, const BasicPersistent<T2, WeaknessPolicy2, LocationPolicy2, CheckingPolicy2>& p2)283 bool operator!=(const BasicPersistent<T1, WeaknessPolicy1, LocationPolicy1,
284 CheckingPolicy1>& p1,
285 const BasicPersistent<T2, WeaknessPolicy2, LocationPolicy2,
286 CheckingPolicy2>& p2) {
287 return !(p1 == p2);
288 }
289
290 template <typename T1, typename PersistentWeaknessPolicy,
291 typename PersistentLocationPolicy, typename PersistentCheckingPolicy,
292 typename T2, typename MemberWriteBarrierPolicy,
293 typename MemberWeaknessTag, typename MemberCheckingPolicy>
operator ==( const BasicPersistent<T1, PersistentWeaknessPolicy, PersistentLocationPolicy, PersistentCheckingPolicy>& p, const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy, MemberCheckingPolicy>& m)294 bool operator==(
295 const BasicPersistent<T1, PersistentWeaknessPolicy,
296 PersistentLocationPolicy, PersistentCheckingPolicy>&
297 p,
298 const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy,
299 MemberCheckingPolicy>& 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>
operator !=( const BasicPersistent<T1, PersistentWeaknessPolicy, PersistentLocationPolicy, PersistentCheckingPolicy>& p, const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy, MemberCheckingPolicy>& m)307 bool operator!=(
308 const BasicPersistent<T1, PersistentWeaknessPolicy,
309 PersistentLocationPolicy, PersistentCheckingPolicy>&
310 p,
311 const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy,
312 MemberCheckingPolicy>& m) {
313 return !(p == m);
314 }
315
316 template <typename T1, typename MemberWriteBarrierPolicy,
317 typename MemberWeaknessTag, typename MemberCheckingPolicy,
318 typename T2, typename PersistentWeaknessPolicy,
319 typename PersistentLocationPolicy, typename PersistentCheckingPolicy>
operator ==( const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy, MemberCheckingPolicy>& m, const BasicPersistent<T1, PersistentWeaknessPolicy, PersistentLocationPolicy, PersistentCheckingPolicy>& p)320 bool operator==(
321 const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy,
322 MemberCheckingPolicy>& m,
323 const BasicPersistent<T1, PersistentWeaknessPolicy,
324 PersistentLocationPolicy, PersistentCheckingPolicy>&
325 p) {
326 return m.Get() == p.Get();
327 }
328
329 template <typename T1, typename MemberWriteBarrierPolicy,
330 typename MemberWeaknessTag, typename MemberCheckingPolicy,
331 typename T2, typename PersistentWeaknessPolicy,
332 typename PersistentLocationPolicy, typename PersistentCheckingPolicy>
operator !=( const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy, MemberCheckingPolicy>& m, const BasicPersistent<T1, PersistentWeaknessPolicy, PersistentLocationPolicy, PersistentCheckingPolicy>& p)333 bool operator!=(
334 const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy,
335 MemberCheckingPolicy>& m,
336 const BasicPersistent<T1, PersistentWeaknessPolicy,
337 PersistentLocationPolicy, PersistentCheckingPolicy>&
338 p) {
339 return !(m == p);
340 }
341
342 template <typename T, typename LocationPolicy, typename CheckingPolicy>
343 struct IsWeak<BasicPersistent<T, internal::WeakPersistentPolicy, LocationPolicy,
344 CheckingPolicy>> : std::true_type {};
345 } // namespace internal
346
347 /**
348 * Persistent is a way to create a strong pointer from an off-heap object to
349 * another on-heap object. As long as the Persistent handle is alive the GC will
350 * keep the object pointed to alive. The Persistent handle is always a GC root
351 * from the point of view of the GC. Persistent must be constructed and
352 * destructed in the same thread.
353 */
354 template <typename T>
355 using Persistent =
356 internal::BasicPersistent<T, internal::StrongPersistentPolicy>;
357
358 /**
359 * WeakPersistent is a way to create a weak pointer from an off-heap object to
360 * an on-heap object. The pointer is automatically cleared when the pointee gets
361 * collected. WeakPersistent must be constructed and destructed in the same
362 * thread.
363 */
364 template <typename T>
365 using WeakPersistent =
366 internal::BasicPersistent<T, internal::WeakPersistentPolicy>;
367
368 } // namespace cppgc
369
370 #endif // INCLUDE_CPPGC_PERSISTENT_H_
371