v8 10.2.154 (node 18.16.0)
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, &Trace));
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, &Trace));
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 = std::enable_if_t<std::is_base_of<T, U>::value>>
153 internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag,
154 MemberCheckingPolicy>
155 member,
157 : BasicCrossThreadPersistent(member.Get(), loc) {}
158
160 const BasicCrossThreadPersistent& other,
163 // Invoke operator=.
164 *this = other;
165 }
166
167 // Heterogeneous ctor.
168 template <typename U, typename OtherWeaknessPolicy,
169 typename OtherLocationPolicy, typename OtherCheckingPolicy,
170 typename = std::enable_if_t<std::is_base_of<T, U>::value>>
172 const BasicCrossThreadPersistent<U, OtherWeaknessPolicy,
173 OtherLocationPolicy,
174 OtherCheckingPolicy>& other,
177 *this = other;
178 }
179
182 const SourceLocation& loc = SourceLocation::Current()) noexcept {
183 // Invoke operator=.
184 *this = std::move(other);
185 }
186
188 const BasicCrossThreadPersistent& other) {
190 AssignSafe(guard, other.Get());
191 return *this;
192 }
193
194 template <typename U, typename OtherWeaknessPolicy,
195 typename OtherLocationPolicy, typename OtherCheckingPolicy,
196 typename = std::enable_if_t<std::is_base_of<T, U>::value>>
198 const BasicCrossThreadPersistent<U, OtherWeaknessPolicy,
199 OtherLocationPolicy,
200 OtherCheckingPolicy>& other) {
202 AssignSafe(guard, other.Get());
203 return *this;
204 }
205
207 if (this == &other) return *this;
208 Clear();
210 PersistentBase::operator=(std::move(other));
211 LocationPolicy::operator=(std::move(other));
212 if (!IsValid(GetValue())) return *this;
213 GetNode()->UpdateOwner(this);
214 other.SetValue(nullptr);
215 other.SetNode(nullptr);
216 this->CheckPointer(Get());
217 return *this;
218 }
219
226 AssignUnsafe(other);
227 return *this;
228 }
229
230 // Assignment from member.
231 template <typename U, typename MemberBarrierPolicy,
232 typename MemberWeaknessTag, typename MemberCheckingPolicy,
233 typename = std::enable_if_t<std::is_base_of<T, U>::value>>
235 internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag,
236 MemberCheckingPolicy>
237 member) {
238 return operator=(member.Get());
239 }
240
247 Clear();
248 return *this;
249 }
250
258 AssignSafe(guard, s);
259 return *this;
260 }
261
269 // CFI cast exemption to allow passing SentinelPointer through T* and support
270 // heterogeneous assignments between different Member and Persistent handles
271 // based on their actual types.
272 V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") T* Get() const {
273 return static_cast<T*>(const_cast<void*>(GetValue()));
274 }
275
279 void Clear() {
281 AssignSafe(guard, nullptr);
282 }
283
291 T* Release() {
292 T* result = Get();
293 Clear();
294 return result;
295 }
296
304 explicit operator bool() const { return Get(); }
305
313 operator T*() const { return Get(); }
314
320 T* operator->() const { return Get(); }
321 T& operator*() const { return *Get(); }
322
323 template <typename U, typename OtherWeaknessPolicy = WeaknessPolicy,
324 typename OtherLocationPolicy = LocationPolicy,
325 typename OtherCheckingPolicy = CheckingPolicy>
326 BasicCrossThreadPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
327 OtherCheckingPolicy>
328 To() const {
329 using OtherBasicCrossThreadPersistent =
330 BasicCrossThreadPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
331 OtherCheckingPolicy>;
333 return OtherBasicCrossThreadPersistent(
334 typename OtherBasicCrossThreadPersistent::UnsafeCtorTag(),
335 static_cast<U*>(Get()));
336 }
337
338 template <typename U = T,
339 typename = typename std::enable_if<!BasicCrossThreadPersistent<
340 U, WeaknessPolicy>::IsStrongPersistent::value>::type>
342 Lock() const {
345 }
346
347 private:
348 static bool IsValid(const void* ptr) {
349 return ptr && ptr != kSentinelPointer;
350 }
351
352 static void Trace(Visitor* v, const void* ptr) {
353 const auto* handle = static_cast<const BasicCrossThreadPersistent*>(ptr);
354 v->TraceRoot(*handle, handle->Location());
355 }
356
357 void AssignUnsafe(T* ptr) {
358 const void* old_value = GetValue();
359 if (IsValid(old_value)) {
360 PersistentRegionLock guard;
361 old_value = GetValue();
362 // The fast path check (IsValid()) does not acquire the lock. Reload
363 // the value to ensure the reference has not been cleared.
364 if (IsValid(old_value)) {
365 CrossThreadPersistentRegion& region =
366 this->GetPersistentRegion(old_value);
367 if (IsValid(ptr) && (&region == &this->GetPersistentRegion(ptr))) {
368 SetValue(ptr);
369 this->CheckPointer(ptr);
370 return;
371 }
372 region.FreeNode(GetNode());
373 SetNode(nullptr);
374 } else {
376 }
377 }
378 SetValue(ptr);
379 if (!IsValid(ptr)) return;
380 PersistentRegionLock guard;
381 SetNode(this->GetPersistentRegion(ptr).AllocateNode(this, &Trace));
382 this->CheckPointer(ptr);
383 }
384
385 void AssignSafe(PersistentRegionLock&, T* ptr) {
387 const void* old_value = GetValue();
388 if (IsValid(old_value)) {
389 CrossThreadPersistentRegion& region =
390 this->GetPersistentRegion(old_value);
391 if (IsValid(ptr) && (&region == &this->GetPersistentRegion(ptr))) {
392 SetValue(ptr);
393 this->CheckPointer(ptr);
394 return;
395 }
396 region.FreeNode(GetNode());
397 SetNode(nullptr);
398 }
399 SetValue(ptr);
400 if (!IsValid(ptr)) return;
401 SetNode(this->GetPersistentRegion(ptr).AllocateNode(this, &Trace));
402 this->CheckPointer(ptr);
403 }
404
405 void ClearFromGC() const {
406 if (IsValid(GetValueFromGC())) {
407 WeaknessPolicy::GetPersistentRegion(GetValueFromGC())
408 .FreeNode(GetNodeFromGC());
410 }
411 }
412
413 // See Get() for details.
414 V8_CLANG_NO_SANITIZE("cfi-unrelated-cast")
415 T* GetFromGC() const {
416 return static_cast<T*>(const_cast<void*>(GetValueFromGC()));
417 }
418
419 friend class cppgc::Visitor;
420};
421
422template <typename T, typename LocationPolicy, typename CheckingPolicy>
423struct IsWeak<
425 LocationPolicy, CheckingPolicy>>
426 : std::true_type {};
427
428} // namespace internal
429
430namespace subtle {
431
443template <typename T>
446
458template <typename T>
461
462} // namespace subtle
463} // namespace cppgc
464
465#endif // INCLUDE_CPPGC_CROSS_THREAD_PERSISTENT_H_
static constexpr SourceLocation Current()
BasicCrossThreadPersistent & operator=(const BasicCrossThreadPersistent< U, OtherWeaknessPolicy, OtherLocationPolicy, OtherCheckingPolicy > &other)
BasicCrossThreadPersistent(internal::BasicMember< U, MemberBarrierPolicy, MemberWeaknessTag, MemberCheckingPolicy > member, const SourceLocation &loc=SourceLocation::Current())
BasicCrossThreadPersistent & operator=(T *other)
BasicCrossThreadPersistent & operator=(std::nullptr_t)
BasicCrossThreadPersistent & operator=(internal::BasicMember< U, MemberBarrierPolicy, MemberWeaknessTag, MemberCheckingPolicy > member)
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(T &raw, 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, TraceCallback trace)
V8_INLINE void FreeNode(PersistentNode *node)
const void * GetValue() const
Definition persistent.h:31
void SetValue(const void *value)
Definition persistent.h:32
PersistentNode * GetNode() const
Definition persistent.h:34
void SetNode(PersistentNode *node)
Definition persistent.h:35
#define CPPGC_DCHECK(condition)
Definition logging.h:36
constexpr internal::SentinelPointer kSentinelPointer
#define V8_CLANG_NO_SANITIZE(what)
Definition v8config.h:544