v8  9.4.146 (node 16.13.0)
V8 is Google's open source JavaScript engine
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 
15 namespace cppgc {
16 namespace 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  node_ = nullptr;
38  }
39 };
40 
41 template <typename T, typename WeaknessPolicy, typename LocationPolicy,
42  typename CheckingPolicy>
44  public LocationPolicy,
45  private WeaknessPolicy,
46  private CheckingPolicy {
47  public:
48  using typename WeaknessPolicy::IsStrongPersistent;
49  using PointeeType = T;
50 
52 
54  const SourceLocation& loc = SourceLocation::Current())
55  : LocationPolicy(loc) {}
56 
58  std::nullptr_t, const SourceLocation& loc = SourceLocation::Current())
59  : LocationPolicy(loc) {}
60 
62  SentinelPointer s, const SourceLocation& loc = SourceLocation::Current())
63  : CrossThreadPersistentBase(s), LocationPolicy(loc) {}
64 
66  T* raw, const SourceLocation& loc = SourceLocation::Current())
67  : CrossThreadPersistentBase(raw), LocationPolicy(loc) {
68  if (!IsValid(raw)) return;
69  PersistentRegionLock guard;
70  CrossThreadPersistentRegion& region = this->GetPersistentRegion(raw);
71  SetNode(region.AllocateNode(this, &Trace));
72  this->CheckPointer(raw);
73  }
74 
75  class UnsafeCtorTag {
76  private:
77  UnsafeCtorTag() = default;
78  template <typename U, typename OtherWeaknessPolicy,
79  typename OtherLocationPolicy, typename OtherCheckingPolicy>
81  };
82 
84  UnsafeCtorTag, T* raw,
85  const SourceLocation& loc = SourceLocation::Current())
86  : CrossThreadPersistentBase(raw), LocationPolicy(loc) {
87  if (!IsValid(raw)) return;
88  CrossThreadPersistentRegion& region = this->GetPersistentRegion(raw);
89  SetNode(region.AllocateNode(this, &Trace));
90  this->CheckPointer(raw);
91  }
92 
94  T& raw, const SourceLocation& loc = SourceLocation::Current())
95  : BasicCrossThreadPersistent(&raw, loc) {}
96 
97  template <typename U, typename MemberBarrierPolicy,
98  typename MemberWeaknessTag, typename MemberCheckingPolicy,
99  typename = std::enable_if_t<std::is_base_of<T, U>::value>>
101  internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag,
102  MemberCheckingPolicy>
103  member,
104  const SourceLocation& loc = SourceLocation::Current())
105  : BasicCrossThreadPersistent(member.Get(), loc) {}
106 
108  const BasicCrossThreadPersistent& other,
109  const SourceLocation& loc = SourceLocation::Current())
111  // Invoke operator=.
112  *this = other;
113  }
114 
115  // Heterogeneous ctor.
116  template <typename U, typename OtherWeaknessPolicy,
117  typename OtherLocationPolicy, typename OtherCheckingPolicy,
118  typename = std::enable_if_t<std::is_base_of<T, U>::value>>
120  const BasicCrossThreadPersistent<U, OtherWeaknessPolicy,
121  OtherLocationPolicy,
122  OtherCheckingPolicy>& other,
123  const SourceLocation& loc = SourceLocation::Current())
125  *this = other;
126  }
127 
130  const SourceLocation& loc = SourceLocation::Current()) noexcept {
131  // Invoke operator=.
132  *this = std::move(other);
133  }
134 
136  const BasicCrossThreadPersistent& other) {
137  PersistentRegionLock guard;
138  AssignUnsafe(other.Get());
139  return *this;
140  }
141 
142  template <typename U, typename OtherWeaknessPolicy,
143  typename OtherLocationPolicy, typename OtherCheckingPolicy,
144  typename = std::enable_if_t<std::is_base_of<T, U>::value>>
146  const BasicCrossThreadPersistent<U, OtherWeaknessPolicy,
147  OtherLocationPolicy,
148  OtherCheckingPolicy>& other) {
149  PersistentRegionLock guard;
150  AssignUnsafe(other.Get());
151  return *this;
152  }
153 
155  if (this == &other) return *this;
156  Clear();
157  PersistentRegionLock guard;
158  PersistentBase::operator=(std::move(other));
159  LocationPolicy::operator=(std::move(other));
160  if (!IsValid(GetValue())) return *this;
161  GetNode()->UpdateOwner(this);
162  other.SetValue(nullptr);
163  other.SetNode(nullptr);
164  this->CheckPointer(Get());
165  return *this;
166  }
167 
169  Assign(other);
170  return *this;
171  }
172 
173  // Assignment from member.
174  template <typename U, typename MemberBarrierPolicy,
175  typename MemberWeaknessTag, typename MemberCheckingPolicy,
176  typename = std::enable_if_t<std::is_base_of<T, U>::value>>
178  internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag,
179  MemberCheckingPolicy>
180  member) {
181  return operator=(member.Get());
182  }
183 
185  Clear();
186  return *this;
187  }
188 
190  Assign(s);
191  return *this;
192  }
193 
194  /**
195  * Returns a pointer to the stored object.
196  *
197  * Note: **Not thread-safe.**
198  *
199  * \returns a pointer to the stored object.
200  */
201  // CFI cast exemption to allow passing SentinelPointer through T* and support
202  // heterogeneous assignments between different Member and Persistent handles
203  // based on their actual types.
204  V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") T* Get() const {
205  return static_cast<T*>(const_cast<void*>(GetValue()));
206  }
207 
208  /**
209  * Clears the stored object.
210  */
211  void Clear() {
212  // Simplified version of `Assign()` to allow calling without a complete type
213  // `T`.
214  const void* old_value = GetValue();
215  if (IsValid(old_value)) {
216  PersistentRegionLock guard;
217  old_value = GetValue();
218  // The fast path check (IsValid()) does not acquire the lock. Reload
219  // the value to ensure the reference has not been cleared.
220  if (IsValid(old_value)) {
221  CrossThreadPersistentRegion& region =
222  this->GetPersistentRegion(old_value);
223  region.FreeNode(GetNode());
224  SetNode(nullptr);
225  } else {
227  }
228  }
229  SetValue(nullptr);
230  }
231 
232  /**
233  * Returns a pointer to the stored object and releases it.
234  *
235  * Note: **Not thread-safe.**
236  *
237  * \returns a pointer to the stored object.
238  */
239  T* Release() {
240  T* result = Get();
241  Clear();
242  return result;
243  }
244 
245  /**
246  * Conversio to boolean.
247  *
248  * Note: **Not thread-safe.**
249  *
250  * \returns true if an actual object has been stored and false otherwise.
251  */
252  explicit operator bool() const { return Get(); }
253 
254  /**
255  * Conversion to object of type T.
256  *
257  * Note: **Not thread-safe.**
258  *
259  * \returns the object.
260  */
261  operator T*() const { return Get(); }
262 
263  /**
264  * Dereferences the stored object.
265  *
266  * Note: **Not thread-safe.**
267  */
268  T* operator->() const { return Get(); }
269  T& operator*() const { return *Get(); }
270 
271  template <typename U, typename OtherWeaknessPolicy = WeaknessPolicy,
272  typename OtherLocationPolicy = LocationPolicy,
273  typename OtherCheckingPolicy = CheckingPolicy>
274  BasicCrossThreadPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
275  OtherCheckingPolicy>
276  To() const {
277  using OtherBasicCrossThreadPersistent =
278  BasicCrossThreadPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
279  OtherCheckingPolicy>;
280  PersistentRegionLock guard;
281  return OtherBasicCrossThreadPersistent(
282  typename OtherBasicCrossThreadPersistent::UnsafeCtorTag(),
283  static_cast<U*>(Get()));
284  }
285 
286  template <typename U = T,
287  typename = typename std::enable_if<!BasicCrossThreadPersistent<
288  U, WeaknessPolicy>::IsStrongPersistent::value>::type>
290  Lock() const {
293  }
294 
295  private:
296  static bool IsValid(const void* ptr) {
297  return ptr && ptr != kSentinelPointer;
298  }
299 
300  static void Trace(Visitor* v, const void* ptr) {
301  const auto* handle = static_cast<const BasicCrossThreadPersistent*>(ptr);
302  v->TraceRoot(*handle, handle->Location());
303  }
304 
305  void Assign(T* ptr) {
306  const void* old_value = GetValue();
307  if (IsValid(old_value)) {
308  PersistentRegionLock guard;
309  old_value = GetValue();
310  // The fast path check (IsValid()) does not acquire the lock. Reload
311  // the value to ensure the reference has not been cleared.
312  if (IsValid(old_value)) {
313  CrossThreadPersistentRegion& region =
314  this->GetPersistentRegion(old_value);
315  if (IsValid(ptr) && (&region == &this->GetPersistentRegion(ptr))) {
316  SetValue(ptr);
317  this->CheckPointer(ptr);
318  return;
319  }
320  region.FreeNode(GetNode());
321  SetNode(nullptr);
322  } else {
324  }
325  }
326  SetValue(ptr);
327  if (!IsValid(ptr)) return;
328  PersistentRegionLock guard;
329  SetNode(this->GetPersistentRegion(ptr).AllocateNode(this, &Trace));
330  this->CheckPointer(ptr);
331  }
332 
333  void AssignUnsafe(T* ptr) {
334  PersistentRegionLock::AssertLocked();
335  const void* old_value = GetValue();
336  if (IsValid(old_value)) {
337  CrossThreadPersistentRegion& region =
338  this->GetPersistentRegion(old_value);
339  if (IsValid(ptr) && (&region == &this->GetPersistentRegion(ptr))) {
340  SetValue(ptr);
341  this->CheckPointer(ptr);
342  return;
343  }
344  region.FreeNode(GetNode());
345  SetNode(nullptr);
346  }
347  SetValue(ptr);
348  if (!IsValid(ptr)) return;
349  SetNode(this->GetPersistentRegion(ptr).AllocateNode(this, &Trace));
350  this->CheckPointer(ptr);
351  }
352 
353  void ClearFromGC() const {
354  if (IsValid(GetValueFromGC())) {
355  WeaknessPolicy::GetPersistentRegion(GetValueFromGC())
356  .FreeNode(GetNodeFromGC());
358  }
359  }
360 
361  // See Get() for details.
362  V8_CLANG_NO_SANITIZE("cfi-unrelated-cast")
363  T* GetFromGC() const {
364  return static_cast<T*>(const_cast<void*>(GetValueFromGC()));
365  }
366 
367  friend class cppgc::Visitor;
368 };
369 
370 template <typename T, typename LocationPolicy, typename CheckingPolicy>
371 struct IsWeak<
373  LocationPolicy, CheckingPolicy>>
374  : std::true_type {};
375 
376 } // namespace internal
377 
378 namespace subtle {
379 
380 /**
381  * **DO NOT USE: Has known caveats, see below.**
382  *
383  * CrossThreadPersistent allows retaining objects from threads other than the
384  * thread the owning heap is operating on.
385  *
386  * Known caveats:
387  * - Does not protect the heap owning an object from terminating.
388  * - Reaching transitively through the graph is unsupported as objects may be
389  * moved concurrently on the thread owning the object.
390  */
391 template <typename T>
392 using CrossThreadPersistent = internal::BasicCrossThreadPersistent<
394 
395 /**
396  * **DO NOT USE: Has known caveats, see below.**
397  *
398  * CrossThreadPersistent allows weakly retaining objects from threads other than
399  * the thread the owning heap is operating on.
400  *
401  * Known caveats:
402  * - Does not protect the heap owning an object from terminating.
403  * - Reaching transitively through the graph is unsupported as objects may be
404  * moved concurrently on the thread owning the object.
405  */
406 template <typename T>
407 using WeakCrossThreadPersistent = internal::BasicCrossThreadPersistent<
409 
410 } // namespace subtle
411 } // namespace cppgc
412 
413 #endif // INCLUDE_CPPGC_CROSS_THREAD_PERSISTENT_H_