v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
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>
71class BasicCrossThreadPersistent final : public CrossThreadPersistentBase,
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
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
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>
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)) {
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)) {
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;
382 SetNode(this->GetPersistentRegion(ptr).AllocateNode(this, &TraceAsRoot));
383 this->CheckPointer(ptr);
384 }
385
388 const void* old_value = GetValue();
389 if (IsValid(old_value)) {
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_
#define T
BasicCrossThreadPersistent & operator=(const BasicCrossThreadPersistent< U, OtherWeaknessPolicy, OtherLocationPolicy, OtherCheckingPolicy > &other)
void AssignSafe(PersistentRegionLock &, T *ptr)
BasicCrossThreadPersistent(const SourceLocation &loc=SourceLocation::Current())
V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") T *Get() const
BasicCrossThreadPersistent(const BasicCrossThreadPersistent< U, OtherWeaknessPolicy, OtherLocationPolicy, OtherCheckingPolicy > &other, const SourceLocation &loc=SourceLocation::Current())
BasicCrossThreadPersistent(const BasicCrossThreadPersistent &other, const SourceLocation &loc=SourceLocation::Current())
BasicCrossThreadPersistent & operator=(T *other)
BasicCrossThreadPersistent & operator=(BasicCrossThreadPersistent &&other)
BasicCrossThreadPersistent< U, internal::StrongCrossThreadPersistentPolicy > Lock() const
static void TraceAsRoot(RootVisitor &root_visitor, const void *ptr)
BasicCrossThreadPersistent(SentinelPointer s, const SourceLocation &loc=SourceLocation::Current())
BasicCrossThreadPersistent(std::nullptr_t, const SourceLocation &loc=SourceLocation::Current())
BasicCrossThreadPersistent(T &raw, const SourceLocation &loc=SourceLocation::Current())
BasicCrossThreadPersistent< U, OtherWeaknessPolicy, OtherLocationPolicy, OtherCheckingPolicy > To() const
BasicCrossThreadPersistent(BasicCrossThreadPersistent &&other, const SourceLocation &loc=SourceLocation::Current()) noexcept
BasicCrossThreadPersistent & operator=(std::nullptr_t)
BasicCrossThreadPersistent(UnsafeCtorTag, T *raw, const SourceLocation &loc=SourceLocation::Current())
BasicCrossThreadPersistent(internal::BasicMember< U, MemberBarrierPolicy, MemberWeaknessTag, MemberCheckingPolicy, MemberStorageType > member, const SourceLocation &loc=SourceLocation::Current())
BasicCrossThreadPersistent & operator=(const BasicCrossThreadPersistent &other)
BasicCrossThreadPersistent & operator=(internal::BasicMember< U, MemberBarrierPolicy, MemberWeaknessTag, MemberCheckingPolicy, MemberStorageType > member)
BasicCrossThreadPersistent(T *raw, const SourceLocation &loc=SourceLocation::Current())
BasicCrossThreadPersistent & operator=(SentinelPointer s)
V8_CLANG_NO_SANITIZE("address") const void *GetValueFromGC() const
void SetNodeSafe(PersistentNode *value) const
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
void Trace(const AnyStrongPersistentType &p)
Definition visitor.h:474
static constexpr SourceLocation Current()
too high values may cause the compiler to set high thresholds for inlining to as much as possible avoid inlined allocation of objects that cannot escape trace load stores from virtual maglev objects use TurboFan fast string builder analyze liveness of environment slots and zap dead values trace TurboFan load elimination emit data about basic block usage in builtins to this enable builtin reordering when run mksnapshot flag for emit warnings when applying builtin profile data verify register allocation in TurboFan randomly schedule instructions to stress dependency tracking enable store store elimination in TurboFan rewrite far to near simulate GC compiler thread race related to allow float parameters to be passed in simulator mode JS Wasm Run additional turbo_optimize_inlined_js_wasm_wrappers enable experimental feedback collection in generic lowering enable Turboshaft s WasmLoadElimination enable Turboshaft s low level load elimination for JS enable Turboshaft s escape analysis for string concatenation use enable Turbolev features that we want to ship in the not too far future trace individual Turboshaft reduction steps trace intermediate Turboshaft reduction steps invocation count threshold for early optimization Enables optimizations which favor memory size over execution speed Enables sampling allocation profiler with X as a sample interval min size of a semi the new space consists of two semi spaces max size of the Collect garbage after Collect garbage after keeps maps alive for< n > old space garbage collections print one detailed trace line in allocation gc speed threshold for starting incremental marking via a task in percent of available threshold for starting incremental marking immediately in percent of available Use a single schedule for determining a marking schedule between JS and C objects schedules the minor GC task with kUserVisible priority max worker number of concurrent for NumberOfWorkerThreads start background threads that allocate memory concurrent_array_buffer_sweeping use parallel threads to clear weak refs in the atomic pause trace progress of the incremental marking trace object counts and memory usage report a tick only when allocated zone memory changes by this amount TracingFlags::gc_stats store(v8::tracing::TracingCategoryObserver::ENABLED_BY_NATIVE)) DEFINE_GENERIC_IMPLICATION(trace_gc_object_stats
#define CPPGC_DCHECK(condition)
Definition logging.h:36
ZoneVector< RpoNumber > & result
constexpr internal::SentinelPointer kSentinelPointer
#define V8_CLANG_NO_SANITIZE(what)
defined(V8_TRIVIAL_ABI)
Definition v8config.h:765