Loading...
Searching...
No Matches
cross-thread-persistent.h
Go to the documentation of this file.
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_CROSS_THREAD_PERSISTENT_H_
6#define INCLUDE_CPPGC_CROSS_THREAD_PERSISTENT_H_
7
8#include <atomic>
9
12#include "cppgc/persistent.h"
13#include "cppgc/visitor.h"
14
15namespace cppgc {
16namespace internal {
17
18// Wrapper around PersistentBase that allows accessing poisoned memory when
19// using ASAN. This is needed as the GC of the heap that owns the value
20// of a CTP, may clear it (heap termination, weakness) while the object
21// holding the CTP may be poisoned as itself may be deemed dead.
23 public:
25 explicit CrossThreadPersistentBase(const void* raw) : PersistentBase(raw) {}
26
27 V8_CLANG_NO_SANITIZE("address") const void* GetValueFromGC() const {
28 return raw_;
29 }
30
31 V8_CLANG_NO_SANITIZE("address")
32 PersistentNode* GetNodeFromGC() const { return node_; }
33
34 V8_CLANG_NO_SANITIZE("address")
35 void ClearFromGC() const {
36 raw_ = nullptr;
37 SetNodeSafe(nullptr);
38 }
39
40 // GetNodeSafe() can be used for a thread-safe IsValid() check in a
41 // double-checked locking pattern. See ~BasicCrossThreadPersistent.
43 return reinterpret_cast<std::atomic<PersistentNode*>*>(&node_)->load(
44 std::memory_order_acquire);
45 }
46
47 // The GC writes using SetNodeSafe() while holding the lock.
48 V8_CLANG_NO_SANITIZE("address")
49 void SetNodeSafe(PersistentNode* value) const {
50#if defined(__has_feature)
51#if __has_feature(address_sanitizer)
52#define V8_IS_ASAN 1
53#endif
54#endif
55
56#ifdef V8_IS_ASAN
57 __atomic_store(&node_, &value, __ATOMIC_RELEASE);
58#else // !V8_IS_ASAN
59 // Non-ASAN builds can use atomics. This also covers MSVC which does not
60 // have the __atomic_store intrinsic.
61 reinterpret_cast<std::atomic<PersistentNode*>*>(&node_)->store(
62 value, std::memory_order_release);
63#endif // !V8_IS_ASAN
64
65#undef V8_IS_ASAN
66 }
67};
68
69template <typename T, typename WeaknessPolicy, typename LocationPolicy,
70 typename CheckingPolicy>
72 public LocationPolicy,
73 private WeaknessPolicy,
74 private CheckingPolicy {
75 public:
76 using typename WeaknessPolicy::IsStrongPersistent;
77 using PointeeType = T;
78
80 // This implements fast path for destroying empty/sentinel.
81 //
82 // Simplified version of `AssignUnsafe()` to allow calling without a
83 // complete type `T`. Uses double-checked locking with a simple thread-safe
84 // check for a valid handle based on a node.
85 if (GetNodeSafe()) {
87 const void* old_value = GetValue();
88 // The fast path check (GetNodeSafe()) does not acquire the lock. Recheck
89 // validity while holding the lock to ensure the reference has not been
90 // cleared.
91 if (IsValid(old_value)) {
93 this->GetPersistentRegion(old_value);
94 region.FreeNode(GetNode());
95 SetNode(nullptr);
96 } else {
98 }
99 }
100 // No need to call SetValue() as the handle is not used anymore. This can
101 // leave behind stale sentinel values but will always destroy the underlying
102 // node.
103 }
104
106 : LocationPolicy(loc) {}
107
110 : LocationPolicy(loc) {}
111
114 : CrossThreadPersistentBase(s), LocationPolicy(loc) {}
115
118 : CrossThreadPersistentBase(raw), LocationPolicy(loc) {
119 if (!IsValid(raw)) return;
121 CrossThreadPersistentRegion& region = this->GetPersistentRegion(raw);
122 SetNode(region.AllocateNode(this, &TraceAsRoot));
123 this->CheckPointer(raw);
124 }
125
127 private:
128 UnsafeCtorTag() = default;
129 template <typename U, typename OtherWeaknessPolicy,
130 typename OtherLocationPolicy, typename OtherCheckingPolicy>
132 };
133
136 : CrossThreadPersistentBase(raw), LocationPolicy(loc) {
137 if (!IsValid(raw)) return;
138 CrossThreadPersistentRegion& region = this->GetPersistentRegion(raw);
139 SetNode(region.AllocateNode(this, &TraceAsRoot));
140 this->CheckPointer(raw);
141 }
142
145 : BasicCrossThreadPersistent(&raw, loc) {}
146
147 template <typename U, typename MemberBarrierPolicy,
148 typename MemberWeaknessTag, typename MemberCheckingPolicy,
149 typename MemberStorageType,
150 typename = std::enable_if_t<std::is_base_of_v<T, U>>>
152 internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag,
153 MemberCheckingPolicy, MemberStorageType>
154 member,
156 : BasicCrossThreadPersistent(member.Get(), loc) {}
157
161 // Invoke operator=.
162 *this = other;
163 }
164
165 // Heterogeneous ctor.
166 template <typename U, typename OtherWeaknessPolicy,
167 typename OtherLocationPolicy, typename OtherCheckingPolicy,
168 typename = std::enable_if_t<std::is_base_of_v<T, U>>>
170 U, OtherWeaknessPolicy, OtherLocationPolicy,
171 OtherCheckingPolicy>& other,
174 *this = other;
175 }
176
179 SourceLocation loc = SourceLocation::Current()) noexcept {
180 // Invoke operator=.
181 *this = std::move(other);
182 }
183
185 const BasicCrossThreadPersistent& other) {
187 AssignSafe(guard, other.Get());
188 return *this;
189 }
190
191 template <typename U, typename OtherWeaknessPolicy,
192 typename OtherLocationPolicy, typename OtherCheckingPolicy,
193 typename = std::enable_if_t<std::is_base_of_v<T, U>>>
195 const BasicCrossThreadPersistent<U, OtherWeaknessPolicy,
196 OtherLocationPolicy,
197 OtherCheckingPolicy>& other) {
199 AssignSafe(guard, other.Get());
200 return *this;
201 }
202
204 if (this == &other) return *this;
205 Clear();
207 PersistentBase::operator=(std::move(other));
208 LocationPolicy::operator=(std::move(other));
209 if (!IsValid(GetValue())) return *this;
210 GetNode()->UpdateOwner(this);
211 other.SetValue(nullptr);
212 other.SetNode(nullptr);
213 this->CheckPointer(Get());
214 return *this;
215 }
216
223 AssignUnsafe(other);
224 return *this;
225 }
226
227 // Assignment from member.
228 template <typename U, typename MemberBarrierPolicy,
229 typename MemberWeaknessTag, typename MemberCheckingPolicy,
230 typename MemberStorageType,
231 typename = std::enable_if_t<std::is_base_of_v<T, U>>>
233 internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag,
234 MemberCheckingPolicy, MemberStorageType>
235 member) {
236 return operator=(member.Get());
237 }
238
245 Clear();
246 return *this;
247 }
248
256 AssignSafe(guard, s);
257 return *this;
258 }
259
267 // CFI cast exemption to allow passing SentinelPointer through T* and support
268 // heterogeneous assignments between different Member and Persistent handles
269 // based on their actual types.
270 V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") T* Get() const {
271 return static_cast<T*>(const_cast<void*>(GetValue()));
272 }
273
277 void Clear() {
279 AssignSafe(guard, nullptr);
280 }
281
289 T* Release() {
290 T* result = Get();
291 Clear();
292 return result;
293 }
294
302 explicit operator bool() const { return Get(); }
303
311 operator T*() const { return Get(); }
312
318 T* operator->() const { return Get(); }
319 T& operator*() const { return *Get(); }
320
321 template <typename U, typename OtherWeaknessPolicy = WeaknessPolicy,
322 typename OtherLocationPolicy = LocationPolicy,
323 typename OtherCheckingPolicy = CheckingPolicy>
324 BasicCrossThreadPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
325 OtherCheckingPolicy>
326 To() const {
327 using OtherBasicCrossThreadPersistent =
328 BasicCrossThreadPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
329 OtherCheckingPolicy>;
331 return OtherBasicCrossThreadPersistent(
332 typename OtherBasicCrossThreadPersistent::UnsafeCtorTag(),
333 static_cast<U*>(Get()));
334 }
335
336 template <typename U = T,
337 typename = std::enable_if_t<!BasicCrossThreadPersistent<
338 U, WeaknessPolicy>::IsStrongPersistent::value>>
340 Lock() const {
343 }
344
345 private:
346 static bool IsValid(const void* ptr) {
347 return ptr && ptr != kSentinelPointer;
348 }
349
350 static void TraceAsRoot(RootVisitor& root_visitor, const void* ptr) {
351 root_visitor.Trace(*static_cast<const BasicCrossThreadPersistent*>(ptr));
352 }
353
354 void AssignUnsafe(T* ptr) {
355 const void* old_value = GetValue();
356 if (IsValid(old_value)) {
357 PersistentRegionLock guard;
358 old_value = GetValue();
359 // The fast path check (IsValid()) does not acquire the lock. Reload
360 // the value to ensure the reference has not been cleared.
361 if (IsValid(old_value)) {
362 CrossThreadPersistentRegion& region =
363 this->GetPersistentRegion(old_value);
364 if (IsValid(ptr) && (&region == &this->GetPersistentRegion(ptr))) {
365 SetValue(ptr);
366 this->CheckPointer(ptr);
367 return;
368 }
369 region.FreeNode(GetNode());
370 SetNode(nullptr);
371 } else {
373 }
374 }
375 SetValue(ptr);
376 if (!IsValid(ptr)) return;
377 PersistentRegionLock guard;
378 SetNode(this->GetPersistentRegion(ptr).AllocateNode(this, &TraceAsRoot));
379 this->CheckPointer(ptr);
380 }
381
382 void AssignSafe(PersistentRegionLock&, T* ptr) {
384 const void* old_value = GetValue();
385 if (IsValid(old_value)) {
386 CrossThreadPersistentRegion& region =
387 this->GetPersistentRegion(old_value);
388 if (IsValid(ptr) && (&region == &this->GetPersistentRegion(ptr))) {
389 SetValue(ptr);
390 this->CheckPointer(ptr);
391 return;
392 }
393 region.FreeNode(GetNode());
394 SetNode(nullptr);
395 }
396 SetValue(ptr);
397 if (!IsValid(ptr)) return;
398 SetNode(this->GetPersistentRegion(ptr).AllocateNode(this, &TraceAsRoot));
399 this->CheckPointer(ptr);
400 }
401
402 void ClearFromGC() const {
403 if (IsValid(GetValueFromGC())) {
404 WeaknessPolicy::GetPersistentRegion(GetValueFromGC())
405 .FreeNode(GetNodeFromGC());
407 }
408 }
409
410 // See Get() for details.
411 V8_CLANG_NO_SANITIZE("cfi-unrelated-cast")
412 T* GetFromGC() const {
413 return static_cast<T*>(const_cast<void*>(GetValueFromGC()));
414 }
415
417};
418
419template <typename T, typename LocationPolicy, typename CheckingPolicy>
420struct IsWeak<
422 LocationPolicy, CheckingPolicy>>
423 : std::true_type {};
424
425} // namespace internal
426
427namespace subtle {
428
440template <typename T>
443
455template <typename T>
458
459} // namespace subtle
460} // namespace cppgc
461
462#endif // INCLUDE_CPPGC_CROSS_THREAD_PERSISTENT_H_
Definition: cross-thread-persistent.h:126
Definition: cross-thread-persistent.h:74
BasicCrossThreadPersistent & operator=(const BasicCrossThreadPersistent< U, OtherWeaknessPolicy, OtherLocationPolicy, OtherCheckingPolicy > &other)
Definition: cross-thread-persistent.h:194
T * Get() const
Definition: cross-thread-persistent.h:270
T & operator*() const
Definition: cross-thread-persistent.h:319
friend class internal::RootVisitor
Definition: cross-thread-persistent.h:416
BasicCrossThreadPersistent(T *raw, SourceLocation loc=SourceLocation::Current())
Definition: cross-thread-persistent.h:116
BasicCrossThreadPersistent & operator=(T *other)
Definition: cross-thread-persistent.h:222
void Clear()
Definition: cross-thread-persistent.h:277
BasicCrossThreadPersistent & operator=(BasicCrossThreadPersistent &&other)
Definition: cross-thread-persistent.h:203
~BasicCrossThreadPersistent()
Definition: cross-thread-persistent.h:79
BasicCrossThreadPersistent(BasicCrossThreadPersistent &&other, SourceLocation loc=SourceLocation::Current()) noexcept
Definition: cross-thread-persistent.h:177
BasicCrossThreadPersistent< U, internal::StrongCrossThreadPersistentPolicy > Lock() const
Definition: cross-thread-persistent.h:340
T PointeeType
Definition: cross-thread-persistent.h:77
BasicCrossThreadPersistent< U, OtherWeaknessPolicy, OtherLocationPolicy, OtherCheckingPolicy > To() const
Definition: cross-thread-persistent.h:326
T * operator->() const
Definition: cross-thread-persistent.h:318
BasicCrossThreadPersistent(SourceLocation loc=SourceLocation::Current())
Definition: cross-thread-persistent.h:105
BasicCrossThreadPersistent(internal::BasicMember< U, MemberBarrierPolicy, MemberWeaknessTag, MemberCheckingPolicy, MemberStorageType > member, SourceLocation loc=SourceLocation::Current())
Definition: cross-thread-persistent.h:151
BasicCrossThreadPersistent & operator=(std::nullptr_t)
Definition: cross-thread-persistent.h:244
BasicCrossThreadPersistent(std::nullptr_t, SourceLocation loc=SourceLocation::Current())
Definition: cross-thread-persistent.h:108
BasicCrossThreadPersistent(T &raw, SourceLocation loc=SourceLocation::Current())
Definition: cross-thread-persistent.h:143
BasicCrossThreadPersistent(UnsafeCtorTag, T *raw, SourceLocation loc=SourceLocation::Current())
Definition: cross-thread-persistent.h:134
T * Release()
Definition: cross-thread-persistent.h:289
BasicCrossThreadPersistent & operator=(const BasicCrossThreadPersistent &other)
Definition: cross-thread-persistent.h:184
BasicCrossThreadPersistent(const BasicCrossThreadPersistent< U, OtherWeaknessPolicy, OtherLocationPolicy, OtherCheckingPolicy > &other, SourceLocation loc=SourceLocation::Current())
Definition: cross-thread-persistent.h:169
BasicCrossThreadPersistent(SentinelPointer s, SourceLocation loc=SourceLocation::Current())
Definition: cross-thread-persistent.h:112
BasicCrossThreadPersistent & operator=(internal::BasicMember< U, MemberBarrierPolicy, MemberWeaknessTag, MemberCheckingPolicy, MemberStorageType > member)
Definition: cross-thread-persistent.h:232
BasicCrossThreadPersistent(const BasicCrossThreadPersistent &other, SourceLocation loc=SourceLocation::Current())
Definition: cross-thread-persistent.h:158
BasicCrossThreadPersistent & operator=(SentinelPointer s)
Definition: cross-thread-persistent.h:254
Definition: member.h:79
Definition: cross-thread-persistent.h:22
PersistentNode * GetNodeFromGC() const
Definition: cross-thread-persistent.h:32
CrossThreadPersistentBase(const void *raw)
Definition: cross-thread-persistent.h:25
const void * GetValueFromGC() const
Definition: cross-thread-persistent.h:27
void SetNodeSafe(PersistentNode *value) const
Definition: cross-thread-persistent.h:49
PersistentNode * GetNodeSafe() const
Definition: cross-thread-persistent.h:42
void ClearFromGC() const
Definition: cross-thread-persistent.h:35
Definition: persistent-node.h:185
void FreeNode(PersistentNode *node)
Definition: persistent-node.h:203
PersistentNode * AllocateNode(void *owner, TraceRootCallback trace)
Definition: persistent-node.h:195
Definition: persistent.h:23
const void * GetValue() const
Definition: persistent.h:28
void SetValue(const void *value)
Definition: persistent.h:29
PersistentNode * GetNode() const
Definition: persistent.h:31
const void * raw_
Definition: persistent.h:42
PersistentNode * node_
Definition: persistent.h:43
void SetNode(PersistentNode *node)
Definition: persistent.h:32
Definition: persistent-node.h:27
void UpdateOwner(void *owner)
Definition: persistent-node.h:45
Definition: persistent-node.h:174
Definition: visitor.h:470
Definition: v8-source-location.h:22
static constexpr SourceLocation Current(const std::source_location &loc=std::source_location::current())
Definition: v8-source-location.h:28
#define CPPGC_DCHECK(condition)
Definition: logging.h:36
Definition: allocation.h:38
constexpr internal::SentinelPointer kSentinelPointer
Definition: sentinel-pointer.h:35
Definition: type-traits.h:30
Definition: sentinel-pointer.h:17
Definition: pointer-policies.h:250
Definition: pointer-policies.h:256
#define V8_CLANG_NO_SANITIZE(what)
defined(V8_TRIVIAL_ABI)
Definition: v8config.h:824