v8 14.1.146 (node 25.0.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
10#include "cppgc/internal/persistent-node.h"
11#include "cppgc/internal/pointer-policies.h"
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.
42 PersistentNode* GetNodeSafe() const {
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()) {
86 PersistentRegionLock guard;
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)) {
92 CrossThreadPersistentRegion& region =
93 this->GetPersistentRegion(old_value);
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
105 BasicCrossThreadPersistent(SourceLocation loc = SourceLocation::Current())
106 : LocationPolicy(loc) {}
107
109 SourceLocation loc = SourceLocation::Current())
110 : LocationPolicy(loc) {}
111
113 SourceLocation loc = SourceLocation::Current())
114 : CrossThreadPersistentBase(s), LocationPolicy(loc) {}
115
117 SourceLocation loc = SourceLocation::Current())
118 : CrossThreadPersistentBase(raw), LocationPolicy(loc) {
119 if (!IsValid(raw)) return;
120 PersistentRegionLock guard;
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
135 SourceLocation loc = SourceLocation::Current())
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
144 SourceLocation loc = SourceLocation::Current())
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,
155 SourceLocation loc = SourceLocation::Current())
156 : BasicCrossThreadPersistent(member.Get(), loc) {}
157
159 SourceLocation loc = SourceLocation::Current())
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,
172 SourceLocation loc = SourceLocation::Current())
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) {
186 PersistentRegionLock guard;
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) {
198 PersistentRegionLock guard;
199 AssignSafe(guard, other.Get());
200 return *this;
201 }
202
204 if (this == &other) return *this;
205 Clear();
206 PersistentRegionLock guard;
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
217 /**
218 * Assigns a raw pointer.
219 *
220 * Note: **Not thread-safe.**
221 */
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
239 /**
240 * Assigns a nullptr.
241 *
242 * \returns the handle.
243 */
245 Clear();
246 return *this;
247 }
248
249 /**
250 * Assigns the sentinel pointer.
251 *
252 * \returns the handle.
253 */
255 PersistentRegionLock guard;
256 AssignSafe(guard, s);
257 return *this;
258 }
259
260 /**
261 * Returns a pointer to the stored object.
262 *
263 * Note: **Not thread-safe.**
264 *
265 * \returns a pointer to the stored object.
266 */
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
274 /**
275 * Clears the stored object.
276 */
277 void Clear() {
278 PersistentRegionLock guard;
279 AssignSafe(guard, nullptr);
280 }
281
282 /**
283 * Returns a pointer to the stored object and releases it.
284 *
285 * Note: **Not thread-safe.**
286 *
287 * \returns a pointer to the stored object.
288 */
289 T* Release() {
290 T* result = Get();
291 Clear();
292 return result;
293 }
294
295 /**
296 * Conversio to boolean.
297 *
298 * Note: **Not thread-safe.**
299 *
300 * \returns true if an actual object has been stored and false otherwise.
301 */
302 explicit operator bool() const { return Get(); }
303
304 /**
305 * Conversion to object of type T.
306 *
307 * Note: **Not thread-safe.**
308 *
309 * \returns the object.
310 */
311 operator T*() const { return Get(); }
312
313 /**
314 * Dereferences the stored object.
315 *
316 * Note: **Not thread-safe.**
317 */
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>;
330 PersistentRegionLock guard;
331 return OtherBasicCrossThreadPersistent(
332 typename OtherBasicCrossThreadPersistent::UnsafeCtorTag(),
333 static_cast<U*>(Get()));
334 }
335
336 template <typename U = T,
340 Lock() const {
341 return BasicCrossThreadPersistent<
342 U, internal::StrongCrossThreadPersistentPolicy>(*this);
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 }
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) {
383 PersistentRegionLock::AssertLocked();
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 }
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
416 friend class internal::RootVisitor;
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
429/**
430 * **DO NOT USE: Has known caveats, see below.**
431 *
432 * CrossThreadPersistent allows retaining objects from threads other than the
433 * thread the owning heap is operating on.
434 *
435 * Known caveats:
436 * - Does not protect the heap owning an object from terminating.
437 * - Reaching transitively through the graph is unsupported as objects may be
438 * moved concurrently on the thread owning the object.
439 */
440template <typename T>
443
444/**
445 * **DO NOT USE: Has known caveats, see below.**
446 *
447 * CrossThreadPersistent allows weakly retaining objects from threads other than
448 * the thread the owning heap is operating on.
449 *
450 * Known caveats:
451 * - Does not protect the heap owning an object from terminating.
452 * - Reaching transitively through the graph is unsupported as objects may be
453 * moved concurrently on the thread owning the object.
454 */
455template <typename T>
458
459} // namespace subtle
460} // namespace cppgc
461
462#endif // INCLUDE_CPPGC_CROSS_THREAD_PERSISTENT_H_
BasicCrossThreadPersistent & operator=(const BasicCrossThreadPersistent< U, OtherWeaknessPolicy, OtherLocationPolicy, OtherCheckingPolicy > &other)
BasicCrossThreadPersistent(UnsafeCtorTag, T *raw, SourceLocation loc=SourceLocation::Current())
BasicCrossThreadPersistent(SentinelPointer s, SourceLocation loc=SourceLocation::Current())
BasicCrossThreadPersistent & operator=(T *other)
BasicCrossThreadPersistent & operator=(std::nullptr_t)
BasicCrossThreadPersistent(const BasicCrossThreadPersistent &other, SourceLocation loc=SourceLocation::Current())
BasicCrossThreadPersistent(T &raw, SourceLocation loc=SourceLocation::Current())
BasicCrossThreadPersistent(internal::BasicMember< U, MemberBarrierPolicy, MemberWeaknessTag, MemberCheckingPolicy, MemberStorageType > member, SourceLocation loc=SourceLocation::Current())
BasicCrossThreadPersistent & operator=(BasicCrossThreadPersistent &&other)
BasicCrossThreadPersistent(std::nullptr_t, SourceLocation loc=SourceLocation::Current())
BasicCrossThreadPersistent(SourceLocation loc=SourceLocation::Current())
BasicCrossThreadPersistent(BasicCrossThreadPersistent &&other, SourceLocation loc=SourceLocation::Current()) noexcept
BasicCrossThreadPersistent(const BasicCrossThreadPersistent< U, OtherWeaknessPolicy, OtherLocationPolicy, OtherCheckingPolicy > &other, SourceLocation loc=SourceLocation::Current())
BasicCrossThreadPersistent< U, internal::StrongCrossThreadPersistentPolicy > Lock() const
BasicCrossThreadPersistent & operator=(SentinelPointer s)
BasicCrossThreadPersistent & operator=(const BasicCrossThreadPersistent &other)
BasicCrossThreadPersistent & operator=(internal::BasicMember< U, MemberBarrierPolicy, MemberWeaknessTag, MemberCheckingPolicy, MemberStorageType > member)
BasicCrossThreadPersistent(T *raw, SourceLocation loc=SourceLocation::Current())
BasicCrossThreadPersistent< U, OtherWeaknessPolicy, OtherLocationPolicy, OtherCheckingPolicy > To() 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
PersistentBase(const void *raw)
Definition persistent.h:26
void SetValue(const void *value)
Definition persistent.h:29
PersistentNode * GetNode() const
Definition persistent.h:31
void SetNode(PersistentNode *node)
Definition persistent.h:32
#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:824