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() const33 const void** GetRawSlot() const { return &raw_; }
GetRaw() const34 const void* GetRaw() const { return raw_; }
SetRaw(void* value)35 void SetRaw(void* value) { raw_ = value; }
36
GetRawAtomic() const37 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() const46 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(other.Get())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>>
102 BasicMember(BasicMember<U, OtherWeaknessTag, OtherBarrierPolicy,
103 OtherCheckingPolicy>&& other) noexcept
BasicMember(other.Get())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.
operator =(const BasicMember& other)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>>
operator =( const BasicMember<U, OtherWeaknessTag, OtherBarrierPolicy, OtherCheckingPolicy>& other)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>>
operator =( const BasicPersistent<U, PersistentWeaknessPolicy, PersistentLocationPolicy, PersistentCheckingPolicy>& other)151 BasicMember& operator=(
152 const BasicPersistent<U, PersistentWeaknessPolicy,
153 PersistentLocationPolicy, PersistentCheckingPolicy>&
154 other) {
155 return operator=(other.Get());
156 }
operator =(T* other)157 BasicMember& operator=(T* other) {
158 SetRawAtomic(other);
159 AssigningWriteBarrier();
160 this->CheckPointer(Get());
161 return *this;
162 }
operator =(std::nullptr_t)163 BasicMember& operator=(std::nullptr_t) {
164 Clear();
165 return *this;
166 }
operator =(SentinelPointer s)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
operator bool() const181 explicit operator bool() const { return Get(); }
operator T*() const182 operator T*() const { return Get(); }
operator ->() const183 T* operator->() const { return Get(); }
operator *() const184 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() const189 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() const206 const T** GetSlotForTesting() const {
207 return reinterpret_cast<const T**>(GetRawSlot());
208 }
209
210 private:
GetRawAtomic() const211 const T* GetRawAtomic() const {
212 return static_cast<const T*>(MemberBase::GetRawAtomic());
213 }
214
InitializingWriteBarrier() const215 void InitializingWriteBarrier() const {
216 WriteBarrierPolicy::InitializingBarrier(GetRawSlot(), GetRaw());
217 }
AssigningWriteBarrier() const218 void AssigningWriteBarrier() const {
219 WriteBarrierPolicy::AssigningBarrier(GetRawSlot(), GetRaw());
220 }
221
ClearFromGC() const222 void ClearFromGC() const { MemberBase::ClearFromGC(); }
223
GetFromGC() const224 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>
operator ==(const BasicMember<T1, WeaknessTag1, WriteBarrierPolicy1, CheckingPolicy1>& member1, const BasicMember<T2, WeaknessTag2, WriteBarrierPolicy2, CheckingPolicy2>& member2)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>
operator !=(const BasicMember<T1, WeaknessTag1, WriteBarrierPolicy1, CheckingPolicy1>& member1, const BasicMember<T2, WeaknessTag2, WriteBarrierPolicy2, CheckingPolicy2>& member2)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