• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_MEMBER_H_
6 #define INCLUDE_CPPGC_MEMBER_H_
7 
8 #include <atomic>
9 #include <cstddef>
10 #include <type_traits>
11 
12 #include "cppgc/internal/pointer-policies.h"
13 #include "cppgc/sentinel-pointer.h"
14 #include "cppgc/type-traits.h"
15 #include "v8config.h"  // NOLINT(build/include_directory)
16 
17 namespace cppgc {
18 
19 class Visitor;
20 
21 namespace internal {
22 
23 // MemberBase always refers to the object as const object and defers to
24 // BasicMember on casting to the right type as needed.
25 class MemberBase {
26  protected:
27   struct AtomicInitializerTag {};
28 
MemberBase()29   MemberBase() : raw_(nullptr) {}
MemberBase(const void * value)30   explicit MemberBase(const void* value) : raw_(value) {}
MemberBase(const void * value,AtomicInitializerTag)31   MemberBase(const void* value, AtomicInitializerTag) { SetRawAtomic(value); }
32 
GetRawSlot()33   const void** GetRawSlot() const { return &raw_; }
GetRaw()34   const void* GetRaw() const { return raw_; }
SetRaw(void * value)35   void SetRaw(void* value) { raw_ = value; }
36 
GetRawAtomic()37   const void* GetRawAtomic() const {
38     return reinterpret_cast<const std::atomic<const void*>*>(&raw_)->load(
39         std::memory_order_relaxed);
40   }
SetRawAtomic(const void * value)41   void SetRawAtomic(const void* value) {
42     reinterpret_cast<std::atomic<const void*>*>(&raw_)->store(
43         value, std::memory_order_relaxed);
44   }
45 
ClearFromGC()46   void ClearFromGC() const { raw_ = nullptr; }
47 
48  private:
49   // All constructors initialize `raw_`. Do not add a default value here as it
50   // results in a non-atomic write on some builds, even when the atomic version
51   // of the constructor is used.
52   mutable const void* raw_;
53 };
54 
55 // The basic class from which all Member classes are 'generated'.
56 template <typename T, typename WeaknessTag, typename WriteBarrierPolicy,
57           typename CheckingPolicy>
58 class BasicMember final : private MemberBase, private CheckingPolicy {
59  public:
60   using PointeeType = T;
61 
62   constexpr BasicMember() = default;
BasicMember(std::nullptr_t)63   constexpr BasicMember(std::nullptr_t) {}     // NOLINT
BasicMember(SentinelPointer s)64   BasicMember(SentinelPointer s) : MemberBase(s) {}  // NOLINT
BasicMember(T * raw)65   BasicMember(T* raw) : MemberBase(raw) {            // NOLINT
66     InitializingWriteBarrier();
67     this->CheckPointer(Get());
68   }
BasicMember(T & raw)69   BasicMember(T& raw) : BasicMember(&raw) {}  // NOLINT
70   // Atomic ctor. Using the AtomicInitializerTag forces BasicMember to
71   // initialize using atomic assignments. This is required for preventing
72   // data races with concurrent marking.
73   using AtomicInitializerTag = MemberBase::AtomicInitializerTag;
BasicMember(std::nullptr_t,AtomicInitializerTag atomic)74   BasicMember(std::nullptr_t, AtomicInitializerTag atomic)
75       : MemberBase(nullptr, atomic) {}
BasicMember(SentinelPointer s,AtomicInitializerTag atomic)76   BasicMember(SentinelPointer s, AtomicInitializerTag atomic)
77       : MemberBase(s, atomic) {}
BasicMember(T * raw,AtomicInitializerTag atomic)78   BasicMember(T* raw, AtomicInitializerTag atomic) : MemberBase(raw, atomic) {
79     InitializingWriteBarrier();
80     this->CheckPointer(Get());
81   }
BasicMember(T & raw,AtomicInitializerTag atomic)82   BasicMember(T& raw, AtomicInitializerTag atomic)
83       : BasicMember(&raw, atomic) {}
84   // Copy ctor.
BasicMember(const BasicMember & other)85   BasicMember(const BasicMember& other) : BasicMember(other.Get()) {}
86   // Allow heterogeneous construction.
87   template <typename U, typename OtherBarrierPolicy, typename OtherWeaknessTag,
88             typename OtherCheckingPolicy,
89             typename = std::enable_if_t<std::is_base_of<T, U>::value>>
BasicMember(const BasicMember<U,OtherWeaknessTag,OtherBarrierPolicy,OtherCheckingPolicy> & other)90   BasicMember(  // NOLINT
91       const BasicMember<U, OtherWeaknessTag, OtherBarrierPolicy,
92                         OtherCheckingPolicy>& other)
93       : BasicMember(other.Get()) {}
94   // Move ctor.
BasicMember(BasicMember && other)95   BasicMember(BasicMember&& other) noexcept : BasicMember(other.Get()) {
96     other.Clear();
97   }
98   // Allow heterogeneous move construction.
99   template <typename U, typename OtherBarrierPolicy, typename OtherWeaknessTag,
100             typename OtherCheckingPolicy,
101             typename = std::enable_if_t<std::is_base_of<T, U>::value>>
BasicMember(BasicMember<U,OtherWeaknessTag,OtherBarrierPolicy,OtherCheckingPolicy> && other)102   BasicMember(BasicMember<U, OtherWeaknessTag, OtherBarrierPolicy,
103                           OtherCheckingPolicy>&& other) noexcept
104       : BasicMember(other.Get()) {
105     other.Clear();
106   }
107   // Construction from Persistent.
108   template <typename U, typename PersistentWeaknessPolicy,
109             typename PersistentLocationPolicy,
110             typename PersistentCheckingPolicy,
111             typename = std::enable_if_t<std::is_base_of<T, U>::value>>
BasicMember(const BasicPersistent<U,PersistentWeaknessPolicy,PersistentLocationPolicy,PersistentCheckingPolicy> & p)112   BasicMember(const BasicPersistent<U, PersistentWeaknessPolicy,
113                                     PersistentLocationPolicy,
114                                     PersistentCheckingPolicy>& p)
115       : BasicMember(p.Get()) {}
116 
117   // Copy assignment.
118   BasicMember& operator=(const BasicMember& other) {
119     return operator=(other.Get());
120   }
121   // Allow heterogeneous copy assignment.
122   template <typename U, typename OtherWeaknessTag, typename OtherBarrierPolicy,
123             typename OtherCheckingPolicy,
124             typename = std::enable_if_t<std::is_base_of<T, U>::value>>
125   BasicMember& operator=(
126       const BasicMember<U, OtherWeaknessTag, OtherBarrierPolicy,
127                         OtherCheckingPolicy>& other) {
128     return operator=(other.Get());
129   }
130   // Move assignment.
131   BasicMember& operator=(BasicMember&& other) noexcept {
132     operator=(other.Get());
133     other.Clear();
134     return *this;
135   }
136   // Heterogeneous move assignment.
137   template <typename U, typename OtherWeaknessTag, typename OtherBarrierPolicy,
138             typename OtherCheckingPolicy,
139             typename = std::enable_if_t<std::is_base_of<T, U>::value>>
140   BasicMember& operator=(BasicMember<U, OtherWeaknessTag, OtherBarrierPolicy,
141                                      OtherCheckingPolicy>&& other) noexcept {
142     operator=(other.Get());
143     other.Clear();
144     return *this;
145   }
146   // Assignment from Persistent.
147   template <typename U, typename PersistentWeaknessPolicy,
148             typename PersistentLocationPolicy,
149             typename PersistentCheckingPolicy,
150             typename = std::enable_if_t<std::is_base_of<T, U>::value>>
151   BasicMember& operator=(
152       const BasicPersistent<U, PersistentWeaknessPolicy,
153                             PersistentLocationPolicy, PersistentCheckingPolicy>&
154           other) {
155     return operator=(other.Get());
156   }
157   BasicMember& operator=(T* other) {
158     SetRawAtomic(other);
159     AssigningWriteBarrier();
160     this->CheckPointer(Get());
161     return *this;
162   }
163   BasicMember& operator=(std::nullptr_t) {
164     Clear();
165     return *this;
166   }
167   BasicMember& operator=(SentinelPointer s) {
168     SetRawAtomic(s);
169     return *this;
170   }
171 
172   template <typename OtherWeaknessTag, typename OtherBarrierPolicy,
173             typename OtherCheckingPolicy>
Swap(BasicMember<T,OtherWeaknessTag,OtherBarrierPolicy,OtherCheckingPolicy> & other)174   void Swap(BasicMember<T, OtherWeaknessTag, OtherBarrierPolicy,
175                         OtherCheckingPolicy>& other) {
176     T* tmp = Get();
177     *this = other;
178     other = tmp;
179   }
180 
181   explicit operator bool() const { return Get(); }
182   operator T*() const { return Get(); }
183   T* operator->() const { return Get(); }
184   T& operator*() const { return *Get(); }
185 
186   // CFI cast exemption to allow passing SentinelPointer through T* and support
187   // heterogeneous assignments between different Member and Persistent handles
188   // based on their actual types.
Get()189   V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") T* Get() const {
190     // Executed by the mutator, hence non atomic load.
191     //
192     // The const_cast below removes the constness from MemberBase storage. The
193     // following static_cast re-adds any constness if specified through the
194     // user-visible template parameter T.
195     return static_cast<T*>(const_cast<void*>(MemberBase::GetRaw()));
196   }
197 
Clear()198   void Clear() { SetRawAtomic(nullptr); }
199 
Release()200   T* Release() {
201     T* result = Get();
202     Clear();
203     return result;
204   }
205 
GetSlotForTesting()206   const T** GetSlotForTesting() const {
207     return reinterpret_cast<const T**>(GetRawSlot());
208   }
209 
210  private:
GetRawAtomic()211   const T* GetRawAtomic() const {
212     return static_cast<const T*>(MemberBase::GetRawAtomic());
213   }
214 
InitializingWriteBarrier()215   void InitializingWriteBarrier() const {
216     WriteBarrierPolicy::InitializingBarrier(GetRawSlot(), GetRaw());
217   }
AssigningWriteBarrier()218   void AssigningWriteBarrier() const {
219     WriteBarrierPolicy::AssigningBarrier(GetRawSlot(), GetRaw());
220   }
221 
ClearFromGC()222   void ClearFromGC() const { MemberBase::ClearFromGC(); }
223 
GetFromGC()224   T* GetFromGC() const { return Get(); }
225 
226   friend class cppgc::Visitor;
227   template <typename U>
228   friend struct cppgc::TraceTrait;
229 };
230 
231 template <typename T1, typename WeaknessTag1, typename WriteBarrierPolicy1,
232           typename CheckingPolicy1, typename T2, typename WeaknessTag2,
233           typename WriteBarrierPolicy2, typename CheckingPolicy2>
234 bool operator==(const BasicMember<T1, WeaknessTag1, WriteBarrierPolicy1,
235                                   CheckingPolicy1>& member1,
236                 const BasicMember<T2, WeaknessTag2, WriteBarrierPolicy2,
237                                   CheckingPolicy2>& member2) {
238   return member1.Get() == member2.Get();
239 }
240 
241 template <typename T1, typename WeaknessTag1, typename WriteBarrierPolicy1,
242           typename CheckingPolicy1, typename T2, typename WeaknessTag2,
243           typename WriteBarrierPolicy2, typename CheckingPolicy2>
244 bool operator!=(const BasicMember<T1, WeaknessTag1, WriteBarrierPolicy1,
245                                   CheckingPolicy1>& member1,
246                 const BasicMember<T2, WeaknessTag2, WriteBarrierPolicy2,
247                                   CheckingPolicy2>& member2) {
248   return !(member1 == member2);
249 }
250 
251 template <typename T, typename WriteBarrierPolicy, typename CheckingPolicy>
252 struct IsWeak<
253     internal::BasicMember<T, WeakMemberTag, WriteBarrierPolicy, CheckingPolicy>>
254     : std::true_type {};
255 
256 }  // namespace internal
257 
258 /**
259  * Members are used in classes to contain strong pointers to other garbage
260  * collected objects. All Member fields of a class must be traced in the class'
261  * trace method.
262  */
263 template <typename T>
264 using Member = internal::BasicMember<T, internal::StrongMemberTag,
265                                      internal::DijkstraWriteBarrierPolicy>;
266 
267 /**
268  * WeakMember is similar to Member in that it is used to point to other garbage
269  * collected objects. However instead of creating a strong pointer to the
270  * object, the WeakMember creates a weak pointer, which does not keep the
271  * pointee alive. Hence if all pointers to to a heap allocated object are weak
272  * the object will be garbage collected. At the time of GC the weak pointers
273  * will automatically be set to null.
274  */
275 template <typename T>
276 using WeakMember = internal::BasicMember<T, internal::WeakMemberTag,
277                                          internal::DijkstraWriteBarrierPolicy>;
278 
279 /**
280  * UntracedMember is a pointer to an on-heap object that is not traced for some
281  * reason. Do not use this unless you know what you are doing. Keeping raw
282  * pointers to on-heap objects is prohibited unless used from stack. Pointee
283  * must be kept alive through other means.
284  */
285 template <typename T>
286 using UntracedMember = internal::BasicMember<T, internal::UntracedMemberTag,
287                                              internal::NoWriteBarrierPolicy>;
288 
289 }  // namespace cppgc
290 
291 #endif  // INCLUDE_CPPGC_MEMBER_H_
292