v8 12.4.254 (node 22.4.1)
V8 is Google's open source JavaScript engine
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
107 : LocationPolicy(loc) {}
108
110 std::nullptr_t, const SourceLocation& loc = SourceLocation::Current())
111 : LocationPolicy(loc) {}
112
115 : CrossThreadPersistentBase(s), LocationPolicy(loc) {}
116
118 T* raw, const SourceLocation& loc = SourceLocation::Current())
119 : CrossThreadPersistentBase(raw), LocationPolicy(loc) {
120 if (!IsValid(raw)) return;
122 CrossThreadPersistentRegion& region = this->GetPersistentRegion(raw);
123 SetNode(region.AllocateNode(this, &TraceAsRoot));
124 this->CheckPointer(raw);
125 }
126
128 private:
129 UnsafeCtorTag() = default;
130 template <typename U, typename OtherWeaknessPolicy,
131 typename OtherLocationPolicy, typename OtherCheckingPolicy>
133 };
134
136 UnsafeCtorTag, T* raw,
138 : CrossThreadPersistentBase(raw), LocationPolicy(loc) {
139 if (!IsValid(raw)) return;
140 CrossThreadPersistentRegion& region = this->GetPersistentRegion(raw);
141 SetNode(region.AllocateNode(this, &TraceAsRoot));
142 this->CheckPointer(raw);
143 }
144
146 T& raw, const SourceLocation& loc = SourceLocation::Current())
147 : BasicCrossThreadPersistent(&raw, loc) {}
148
149 template <typename U, typename MemberBarrierPolicy,
150 typename MemberWeaknessTag, typename MemberCheckingPolicy,
151 typename MemberStorageType,
152 typename = std::enable_if_t<std::is_base_of<T, U>::value>>
154 internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag,
155 MemberCheckingPolicy, MemberStorageType>
156 member,
158 : BasicCrossThreadPersistent(member.Get(), loc) {}
159
161 const BasicCrossThreadPersistent& other,
164 // Invoke operator=.
165 *this = other;
166 }
167
168 // Heterogeneous ctor.
169 template <typename U, typename OtherWeaknessPolicy,
170 typename OtherLocationPolicy, typename OtherCheckingPolicy,
171 typename = std::enable_if_t<std::is_base_of<T, U>::value>>
173 const BasicCrossThreadPersistent<U, OtherWeaknessPolicy,
174 OtherLocationPolicy,
175 OtherCheckingPolicy>& other,
178 *this = other;
179 }
180
183 const SourceLocation& loc = SourceLocation::Current()) noexcept {
184 // Invoke operator=.
185 *this = std::move(other);
186 }
187
189 const BasicCrossThreadPersistent& other) {
191 AssignSafe(guard, other.Get());
192 return *this;
193 }
194
195 template <typename U, typename OtherWeaknessPolicy,
196 typename OtherLocationPolicy, typename OtherCheckingPolicy,
197 typename = std::enable_if_t<std::is_base_of<T, U>::value>>
199 const BasicCrossThreadPersistent<U, OtherWeaknessPolicy,
200 OtherLocationPolicy,
201 OtherCheckingPolicy>& other) {
203 AssignSafe(guard, other.Get());
204 return *this;
205 }
206
208 if (this == &other) return *this;
209 Clear();
211 PersistentBase::operator=(std::move(other));
212 LocationPolicy::operator=(std::move(other));
213 if (!IsValid(GetValue())) return *this;
214 GetNode()->UpdateOwner(this);
215 other.SetValue(nullptr);
216 other.SetNode(nullptr);
217 this->CheckPointer(Get());
218 return *this;
219 }
220
227 AssignUnsafe(other);
228 return *this;
229 }
230
231 // Assignment from member.
232 template <typename U, typename MemberBarrierPolicy,
233 typename MemberWeaknessTag, typename MemberCheckingPolicy,
234 typename MemberStorageType,
235 typename = std::enable_if_t<std::is_base_of<T, U>::value>>
237 internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag,
238 MemberCheckingPolicy, MemberStorageType>
239 member) {
240 return operator=(member.Get());
241 }
242
249 Clear();
250 return *this;
251 }
252
260 AssignSafe(guard, s);
261 return *this;
262 }
263
271 // CFI cast exemption to allow passing SentinelPointer through T* and support
272 // heterogeneous assignments between different Member and Persistent handles
273 // based on their actual types.
274 V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") T* Get() const {
275 return static_cast<T*>(const_cast<void*>(GetValue()));
276 }
277
281 void Clear() {
283 AssignSafe(guard, nullptr);
284 }
285
293 T* Release() {
294 T* result = Get();
295 Clear();
296 return result;
297 }
298
306 explicit operator bool() const { return Get(); }
307
315 operator T*() const { return Get(); }
316
322 T* operator->() const { return Get(); }
323 T& operator*() const { return *Get(); }
324
325 template <typename U, typename OtherWeaknessPolicy = WeaknessPolicy,
326 typename OtherLocationPolicy = LocationPolicy,
327 typename OtherCheckingPolicy = CheckingPolicy>
328 BasicCrossThreadPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
329 OtherCheckingPolicy>
330 To() const {
331 using OtherBasicCrossThreadPersistent =
332 BasicCrossThreadPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
333 OtherCheckingPolicy>;
335 return OtherBasicCrossThreadPersistent(
336 typename OtherBasicCrossThreadPersistent::UnsafeCtorTag(),
337 static_cast<U*>(Get()));
338 }
339
340 template <typename U = T,
341 typename = typename std::enable_if<!BasicCrossThreadPersistent<
342 U, WeaknessPolicy>::IsStrongPersistent::value>::type>
344 Lock() const {
347 }
348
349 private:
350 static bool IsValid(const void* ptr) {
351 return ptr && ptr != kSentinelPointer;
352 }
353
354 static void TraceAsRoot(RootVisitor& root_visitor, const void* ptr) {
355 root_visitor.Trace(*static_cast<const BasicCrossThreadPersistent*>(ptr));
356 }
357
358 void AssignUnsafe(T* ptr) {
359 const void* old_value = GetValue();
360 if (IsValid(old_value)) {
361 PersistentRegionLock guard;
362 old_value = GetValue();
363 // The fast path check (IsValid()) does not acquire the lock. Reload
364 // the value to ensure the reference has not been cleared.
365 if (IsValid(old_value)) {
366 CrossThreadPersistentRegion& region =
367 this->GetPersistentRegion(old_value);
368 if (IsValid(ptr) && (&region == &this->GetPersistentRegion(ptr))) {
369 SetValue(ptr);
370 this->CheckPointer(ptr);
371 return;
372 }
373 region.FreeNode(GetNode());
374 SetNode(nullptr);
375 } else {
377 }
378 }
379 SetValue(ptr);
380 if (!IsValid(ptr)) return;
381 PersistentRegionLock guard;
382 SetNode(this->GetPersistentRegion(ptr).AllocateNode(this, &TraceAsRoot));
383 this->CheckPointer(ptr);
384 }
385
386 void AssignSafe(PersistentRegionLock&, T* ptr) {
388 const void* old_value = GetValue();
389 if (IsValid(old_value)) {
390 CrossThreadPersistentRegion& region =
391 this->GetPersistentRegion(old_value);
392 if (IsValid(ptr) && (&region == &this->GetPersistentRegion(ptr))) {
393 SetValue(ptr);
394 this->CheckPointer(ptr);
395 return;
396 }
397 region.FreeNode(GetNode());
398 SetNode(nullptr);
399 }
400 SetValue(ptr);
401 if (!IsValid(ptr)) return;
402 SetNode(this->GetPersistentRegion(ptr).AllocateNode(this, &TraceAsRoot));
403 this->CheckPointer(ptr);
404 }
405
406 void ClearFromGC() const {
407 if (IsValid(GetValueFromGC())) {
408 WeaknessPolicy::GetPersistentRegion(GetValueFromGC())
409 .FreeNode(GetNodeFromGC());
411 }
412 }
413
414 // See Get() for details.
415 V8_CLANG_NO_SANITIZE("cfi-unrelated-cast")
416 T* GetFromGC() const {
417 return static_cast<T*>(const_cast<void*>(GetValueFromGC()));
418 }
419
421};
422
423template <typename T, typename LocationPolicy, typename CheckingPolicy>
424struct IsWeak<
426 LocationPolicy, CheckingPolicy>>
427 : std::true_type {};
428
429} // namespace internal
430
431namespace subtle {
432
444template <typename T>
447
459template <typename T>
462
463} // namespace subtle
464} // namespace cppgc
465
466#endif // INCLUDE_CPPGC_CROSS_THREAD_PERSISTENT_H_
BasicCrossThreadPersistent & operator=(const BasicCrossThreadPersistent< U, OtherWeaknessPolicy, OtherLocationPolicy, OtherCheckingPolicy > &other)
BasicCrossThreadPersistent & operator=(T *other)
BasicCrossThreadPersistent & operator=(std::nullptr_t)
BasicCrossThreadPersistent(UnsafeCtorTag, T *raw, const SourceLocation &loc=SourceLocation::Current())
V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") T *Get() const
BasicCrossThreadPersistent(const BasicCrossThreadPersistent &other, const SourceLocation &loc=SourceLocation::Current())
BasicCrossThreadPersistent & operator=(BasicCrossThreadPersistent &&other)
BasicCrossThreadPersistent(std::nullptr_t, const SourceLocation &loc=SourceLocation::Current())
BasicCrossThreadPersistent(T *raw, const SourceLocation &loc=SourceLocation::Current())
BasicCrossThreadPersistent(BasicCrossThreadPersistent &&other, const SourceLocation &loc=SourceLocation::Current()) noexcept
BasicCrossThreadPersistent< U, internal::StrongCrossThreadPersistentPolicy > Lock() const
BasicCrossThreadPersistent & operator=(SentinelPointer s)
BasicCrossThreadPersistent & operator=(const BasicCrossThreadPersistent &other)
BasicCrossThreadPersistent(SentinelPointer s, const SourceLocation &loc=SourceLocation::Current())
BasicCrossThreadPersistent & operator=(internal::BasicMember< U, MemberBarrierPolicy, MemberWeaknessTag, MemberCheckingPolicy, MemberStorageType > member)
BasicCrossThreadPersistent(T &raw, const SourceLocation &loc=SourceLocation::Current())
BasicCrossThreadPersistent(internal::BasicMember< U, MemberBarrierPolicy, MemberWeaknessTag, MemberCheckingPolicy, MemberStorageType > member, const SourceLocation &loc=SourceLocation::Current())
BasicCrossThreadPersistent(const BasicCrossThreadPersistent< U, OtherWeaknessPolicy, OtherLocationPolicy, OtherCheckingPolicy > &other, const SourceLocation &loc=SourceLocation::Current())
BasicCrossThreadPersistent< U, OtherWeaknessPolicy, OtherLocationPolicy, OtherCheckingPolicy > To() const
BasicCrossThreadPersistent(const SourceLocation &loc=SourceLocation::Current())
V8_CLANG_NO_SANITIZE("address") const void *GetValueFromGC() const
void SetNodeSafe(PersistentNode *value) const
V8_INLINE PersistentNode * AllocateNode(void *owner, TraceRootCallback trace)
V8_INLINE void FreeNode(PersistentNode *node)
const void * GetValue() const
Definition persistent.h:28
void SetValue(const void *value)
Definition persistent.h:29
PersistentNode * GetNode() const
Definition persistent.h:31
void SetNode(PersistentNode *node)
Definition persistent.h:32
static constexpr SourceLocation Current()
#define CPPGC_DCHECK(condition)
Definition logging.h:36
constexpr internal::SentinelPointer kSentinelPointer
#define V8_CLANG_NO_SANITIZE(what)
defined(V8_TRIVIAL_ABI)
Definition v8config.h:714