persistent.h 13.2 KB
Newer Older
1 2 3 4 5 6 7 8 9
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef INCLUDE_CPPGC_PERSISTENT_H_
#define INCLUDE_CPPGC_PERSISTENT_H_

#include <type_traits>

10 11
#include "cppgc/internal/persistent-node.h"
#include "cppgc/internal/pointer-policies.h"
12
#include "cppgc/sentinel-pointer.h"
13 14 15
#include "cppgc/source-location.h"
#include "cppgc/type-traits.h"
#include "cppgc/visitor.h"
16
#include "v8config.h"  // NOLINT(build/include_directory)
17 18

namespace cppgc {
19 20 21

class Visitor;

22 23
namespace internal {

24 25
// PersistentBase always refers to the object as const object and defers to
// BasicPersistent on casting to the right type as needed.
26 27 28
class PersistentBase {
 protected:
  PersistentBase() = default;
29
  explicit PersistentBase(const void* raw) : raw_(raw) {}
30

31 32
  const void* GetValue() const { return raw_; }
  void SetValue(const void* value) { raw_ = value; }
33 34 35 36 37 38 39 40 41 42 43

  PersistentNode* GetNode() const { return node_; }
  void SetNode(PersistentNode* node) { node_ = node; }

  // Performs a shallow clear which assumes that internal persistent nodes are
  // destroyed elsewhere.
  void ClearFromGC() const {
    raw_ = nullptr;
    node_ = nullptr;
  }

44
 protected:
45
  mutable const void* raw_ = nullptr;
46 47
  mutable PersistentNode* node_ = nullptr;

48
  friend class PersistentRegionBase;
49 50
};

51 52 53
// The basic class from which all Persistent classes are generated.
template <typename T, typename WeaknessPolicy, typename LocationPolicy,
          typename CheckingPolicy>
54 55 56 57
class BasicPersistent final : public PersistentBase,
                              public LocationPolicy,
                              private WeaknessPolicy,
                              private CheckingPolicy {
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
 public:
  using typename WeaknessPolicy::IsStrongPersistent;
  using PointeeType = T;

  // Null-state/sentinel constructors.
  BasicPersistent(  // NOLINT
      const SourceLocation& loc = SourceLocation::Current())
      : LocationPolicy(loc) {}

  BasicPersistent(std::nullptr_t,  // NOLINT
                  const SourceLocation& loc = SourceLocation::Current())
      : LocationPolicy(loc) {}

  BasicPersistent(  // NOLINT
      SentinelPointer s, const SourceLocation& loc = SourceLocation::Current())
73
      : PersistentBase(s), LocationPolicy(loc) {}
74

75
  // Raw value constructors.
76 77
  BasicPersistent(T* raw,  // NOLINT
                  const SourceLocation& loc = SourceLocation::Current())
78
      : PersistentBase(raw), LocationPolicy(loc) {
79
    if (!IsValid()) return;
80 81
    SetNode(WeaknessPolicy::GetPersistentRegion(GetValue())
                .AllocateNode(this, &BasicPersistent::Trace));
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
    this->CheckPointer(Get());
  }

  BasicPersistent(T& raw,  // NOLINT
                  const SourceLocation& loc = SourceLocation::Current())
      : BasicPersistent(&raw, loc) {}

  // Copy ctor.
  BasicPersistent(const BasicPersistent& other,
                  const SourceLocation& loc = SourceLocation::Current())
      : BasicPersistent(other.Get(), loc) {}

  // Heterogeneous ctor.
  template <typename U, typename OtherWeaknessPolicy,
            typename OtherLocationPolicy, typename OtherCheckingPolicy,
            typename = std::enable_if_t<std::is_base_of<T, U>::value>>
98
  BasicPersistent(
99 100 101 102 103 104 105 106 107 108
      const BasicPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
                            OtherCheckingPolicy>& other,
      const SourceLocation& loc = SourceLocation::Current())
      : BasicPersistent(other.Get(), loc) {}

  // Move ctor. The heterogeneous move ctor is not supported since e.g.
  // persistent can't reuse persistent node from weak persistent.
  BasicPersistent(
      BasicPersistent&& other,
      const SourceLocation& loc = SourceLocation::Current()) noexcept
109
      : PersistentBase(std::move(other)), LocationPolicy(std::move(other)) {
110
    if (!IsValid()) return;
111 112 113
    GetNode()->UpdateOwner(this);
    other.SetValue(nullptr);
    other.SetNode(nullptr);
114 115 116 117 118 119 120
    this->CheckPointer(Get());
  }

  // Constructor from member.
  template <typename U, typename MemberBarrierPolicy,
            typename MemberWeaknessTag, typename MemberCheckingPolicy,
            typename = std::enable_if_t<std::is_base_of<T, U>::value>>
121 122 123 124
  BasicPersistent(
      const internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag,
                                  MemberCheckingPolicy>& member,
      const SourceLocation& loc = SourceLocation::Current())
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
      : BasicPersistent(member.Get(), loc) {}

  ~BasicPersistent() { Clear(); }

  // Copy assignment.
  BasicPersistent& operator=(const BasicPersistent& other) {
    return operator=(other.Get());
  }

  template <typename U, typename OtherWeaknessPolicy,
            typename OtherLocationPolicy, typename OtherCheckingPolicy,
            typename = std::enable_if_t<std::is_base_of<T, U>::value>>
  BasicPersistent& operator=(
      const BasicPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
                            OtherCheckingPolicy>& other) {
    return operator=(other.Get());
  }

  // Move assignment.
144
  BasicPersistent& operator=(BasicPersistent&& other) noexcept {
145 146
    if (this == &other) return *this;
    Clear();
147
    PersistentBase::operator=(std::move(other));
148 149
    LocationPolicy::operator=(std::move(other));
    if (!IsValid()) return *this;
150 151 152
    GetNode()->UpdateOwner(this);
    other.SetValue(nullptr);
    other.SetNode(nullptr);
153 154 155 156 157 158 159 160 161
    this->CheckPointer(Get());
    return *this;
  }

  // Assignment from member.
  template <typename U, typename MemberBarrierPolicy,
            typename MemberWeaknessTag, typename MemberCheckingPolicy,
            typename = std::enable_if_t<std::is_base_of<T, U>::value>>
  BasicPersistent& operator=(
162 163
      const internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag,
                                  MemberCheckingPolicy>& member) {
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
    return operator=(member.Get());
  }

  BasicPersistent& operator=(T* other) {
    Assign(other);
    return *this;
  }

  BasicPersistent& operator=(std::nullptr_t) {
    Clear();
    return *this;
  }

  BasicPersistent& operator=(SentinelPointer s) {
    Assign(s);
    return *this;
  }

  explicit operator bool() const { return Get(); }
183
  operator T*() const { return Get(); }
184 185 186
  T* operator->() const { return Get(); }
  T& operator*() const { return *Get(); }

187 188 189 190
  // CFI cast exemption to allow passing SentinelPointer through T* and support
  // heterogeneous assignments between different Member and Persistent handles
  // based on their actual types.
  V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") T* Get() const {
191 192 193 194
    // The const_cast below removes the constness from PersistentBase storage.
    // The following static_cast re-adds any constness if specified through the
    // user-visible template parameter T.
    return static_cast<T*>(const_cast<void*>(GetValue()));
195
  }
196

197 198 199 200 201 202 203 204 205
  void Clear() {
    // Simplified version of `Assign()` to allow calling without a complete type
    // `T`.
    if (IsValid()) {
      WeaknessPolicy::GetPersistentRegion(GetValue()).FreeNode(GetNode());
      SetNode(nullptr);
    }
    SetValue(nullptr);
  }
206 207 208 209 210 211 212

  T* Release() {
    T* result = Get();
    Clear();
    return result;
  }

213 214 215 216 217 218 219 220 221 222
  template <typename U, typename OtherWeaknessPolicy = WeaknessPolicy,
            typename OtherLocationPolicy = LocationPolicy,
            typename OtherCheckingPolicy = CheckingPolicy>
  BasicPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
                  OtherCheckingPolicy>
  To() const {
    return BasicPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
                           OtherCheckingPolicy>(static_cast<U*>(Get()));
  }

223 224 225 226 227 228 229 230 231 232
 private:
  static void Trace(Visitor* v, const void* ptr) {
    const auto* persistent = static_cast<const BasicPersistent*>(ptr);
    v->TraceRoot(*persistent, persistent->Location());
  }

  bool IsValid() const {
    // Ideally, handling kSentinelPointer would be done by the embedder. On the
    // other hand, having Persistent aware of it is beneficial since no node
    // gets wasted.
233
    return GetValue() != nullptr && GetValue() != kSentinelPointer;
234 235 236 237 238 239
  }

  void Assign(T* ptr) {
    if (IsValid()) {
      if (ptr && ptr != kSentinelPointer) {
        // Simply assign the pointer reusing the existing node.
240
        SetValue(ptr);
241 242 243
        this->CheckPointer(ptr);
        return;
      }
244 245
      WeaknessPolicy::GetPersistentRegion(GetValue()).FreeNode(GetNode());
      SetNode(nullptr);
246
    }
247
    SetValue(ptr);
248
    if (!IsValid()) return;
249 250
    SetNode(WeaknessPolicy::GetPersistentRegion(GetValue())
                .AllocateNode(this, &BasicPersistent::Trace));
251 252 253
    this->CheckPointer(Get());
  }

254 255 256 257 258 259 260
  void ClearFromGC() const {
    if (IsValid()) {
      WeaknessPolicy::GetPersistentRegion(GetValue()).FreeNode(GetNode());
      PersistentBase::ClearFromGC();
    }
  }

261 262 263 264 265 266
  // Set Get() for details.
  V8_CLANG_NO_SANITIZE("cfi-unrelated-cast")
  T* GetFromGC() const {
    return static_cast<T*>(const_cast<void*>(GetValue()));
  }

267
  friend class cppgc::Visitor;
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
};

template <typename T1, typename WeaknessPolicy1, typename LocationPolicy1,
          typename CheckingPolicy1, typename T2, typename WeaknessPolicy2,
          typename LocationPolicy2, typename CheckingPolicy2>
bool operator==(const BasicPersistent<T1, WeaknessPolicy1, LocationPolicy1,
                                      CheckingPolicy1>& p1,
                const BasicPersistent<T2, WeaknessPolicy2, LocationPolicy2,
                                      CheckingPolicy2>& p2) {
  return p1.Get() == p2.Get();
}

template <typename T1, typename WeaknessPolicy1, typename LocationPolicy1,
          typename CheckingPolicy1, typename T2, typename WeaknessPolicy2,
          typename LocationPolicy2, typename CheckingPolicy2>
bool operator!=(const BasicPersistent<T1, WeaknessPolicy1, LocationPolicy1,
                                      CheckingPolicy1>& p1,
                const BasicPersistent<T2, WeaknessPolicy2, LocationPolicy2,
                                      CheckingPolicy2>& p2) {
  return !(p1 == p2);
}

template <typename T1, typename PersistentWeaknessPolicy,
          typename PersistentLocationPolicy, typename PersistentCheckingPolicy,
          typename T2, typename MemberWriteBarrierPolicy,
          typename MemberWeaknessTag, typename MemberCheckingPolicy>
294 295 296 297 298 299
bool operator==(
    const BasicPersistent<T1, PersistentWeaknessPolicy,
                          PersistentLocationPolicy, PersistentCheckingPolicy>&
        p,
    const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy,
                      MemberCheckingPolicy>& m) {
300 301 302 303 304 305 306
  return p.Get() == m.Get();
}

template <typename T1, typename PersistentWeaknessPolicy,
          typename PersistentLocationPolicy, typename PersistentCheckingPolicy,
          typename T2, typename MemberWriteBarrierPolicy,
          typename MemberWeaknessTag, typename MemberCheckingPolicy>
307 308 309 310 311 312
bool operator!=(
    const BasicPersistent<T1, PersistentWeaknessPolicy,
                          PersistentLocationPolicy, PersistentCheckingPolicy>&
        p,
    const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy,
                      MemberCheckingPolicy>& m) {
313 314 315 316 317 318 319
  return !(p == m);
}

template <typename T1, typename MemberWriteBarrierPolicy,
          typename MemberWeaknessTag, typename MemberCheckingPolicy,
          typename T2, typename PersistentWeaknessPolicy,
          typename PersistentLocationPolicy, typename PersistentCheckingPolicy>
320 321 322 323 324 325
bool operator==(
    const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy,
                      MemberCheckingPolicy>& m,
    const BasicPersistent<T1, PersistentWeaknessPolicy,
                          PersistentLocationPolicy, PersistentCheckingPolicy>&
        p) {
326 327 328 329 330 331 332
  return m.Get() == p.Get();
}

template <typename T1, typename MemberWriteBarrierPolicy,
          typename MemberWeaknessTag, typename MemberCheckingPolicy,
          typename T2, typename PersistentWeaknessPolicy,
          typename PersistentLocationPolicy, typename PersistentCheckingPolicy>
333 334 335 336 337 338
bool operator!=(
    const BasicMember<T2, MemberWeaknessTag, MemberWriteBarrierPolicy,
                      MemberCheckingPolicy>& m,
    const BasicPersistent<T1, PersistentWeaknessPolicy,
                          PersistentLocationPolicy, PersistentCheckingPolicy>&
        p) {
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
  return !(m == p);
}

template <typename T, typename LocationPolicy, typename CheckingPolicy>
struct IsWeak<BasicPersistent<T, internal::WeakPersistentPolicy, LocationPolicy,
                              CheckingPolicy>> : std::true_type {};
}  // namespace internal

/**
 * Persistent is a way to create a strong pointer from an off-heap object to
 * another on-heap object. As long as the Persistent handle is alive the GC will
 * keep the object pointed to alive. The Persistent handle is always a GC root
 * from the point of view of the GC. Persistent must be constructed and
 * destructed in the same thread.
 */
template <typename T>
using Persistent =
    internal::BasicPersistent<T, internal::StrongPersistentPolicy>;

/**
 * WeakPersistent is a way to create a weak pointer from an off-heap object to
 * an on-heap object. The pointer is automatically cleared when the pointee gets
 * collected. WeakPersistent must be constructed and destructed in the same
 * thread.
 */
template <typename T>
using WeakPersistent =
    internal::BasicPersistent<T, internal::WeakPersistentPolicy>;

}  // namespace cppgc

#endif  // INCLUDE_CPPGC_PERSISTENT_H_