2020-10-09 12:06:43 +00:00
|
|
|
// 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_CROSS_THREAD_PERSISTENT_H_
|
|
|
|
#define INCLUDE_CPPGC_CROSS_THREAD_PERSISTENT_H_
|
|
|
|
|
|
|
|
#include <atomic>
|
|
|
|
|
|
|
|
#include "cppgc/internal/persistent-node.h"
|
|
|
|
#include "cppgc/internal/pointer-policies.h"
|
|
|
|
#include "cppgc/persistent.h"
|
|
|
|
#include "cppgc/visitor.h"
|
|
|
|
|
|
|
|
namespace cppgc {
|
|
|
|
|
|
|
|
namespace internal {
|
|
|
|
|
|
|
|
template <typename T, typename WeaknessPolicy, typename LocationPolicy,
|
|
|
|
typename CheckingPolicy>
|
|
|
|
class BasicCrossThreadPersistent final : public PersistentBase,
|
|
|
|
public LocationPolicy,
|
|
|
|
private WeaknessPolicy,
|
|
|
|
private CheckingPolicy {
|
|
|
|
public:
|
|
|
|
using typename WeaknessPolicy::IsStrongPersistent;
|
|
|
|
using PointeeType = T;
|
|
|
|
|
|
|
|
~BasicCrossThreadPersistent() { Clear(); }
|
|
|
|
|
|
|
|
BasicCrossThreadPersistent( // NOLINT
|
|
|
|
const SourceLocation& loc = SourceLocation::Current())
|
|
|
|
: LocationPolicy(loc) {}
|
|
|
|
|
|
|
|
BasicCrossThreadPersistent( // NOLINT
|
|
|
|
std::nullptr_t, const SourceLocation& loc = SourceLocation::Current())
|
|
|
|
: LocationPolicy(loc) {}
|
|
|
|
|
|
|
|
BasicCrossThreadPersistent( // NOLINT
|
|
|
|
SentinelPointer s, const SourceLocation& loc = SourceLocation::Current())
|
|
|
|
: PersistentBase(s), LocationPolicy(loc) {}
|
|
|
|
|
|
|
|
BasicCrossThreadPersistent( // NOLINT
|
|
|
|
T* raw, const SourceLocation& loc = SourceLocation::Current())
|
|
|
|
: PersistentBase(raw), LocationPolicy(loc) {
|
|
|
|
if (!IsValid(raw)) return;
|
|
|
|
PersistentRegion& region = this->GetPersistentRegion(raw);
|
|
|
|
SetNode(region.AllocateNode(this, &Trace));
|
|
|
|
this->CheckPointer(raw);
|
|
|
|
}
|
|
|
|
|
|
|
|
BasicCrossThreadPersistent( // NOLINT
|
|
|
|
T& raw, const SourceLocation& loc = SourceLocation::Current())
|
|
|
|
: BasicCrossThreadPersistent(&raw, loc) {}
|
|
|
|
|
|
|
|
template <typename U, typename MemberBarrierPolicy,
|
|
|
|
typename MemberWeaknessTag, typename MemberCheckingPolicy,
|
|
|
|
typename = std::enable_if_t<std::is_base_of<T, U>::value>>
|
|
|
|
BasicCrossThreadPersistent( // NOLINT
|
|
|
|
internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag,
|
|
|
|
MemberCheckingPolicy>
|
|
|
|
member,
|
|
|
|
const SourceLocation& loc = SourceLocation::Current())
|
|
|
|
: BasicCrossThreadPersistent(member.Get(), loc) {}
|
|
|
|
|
|
|
|
BasicCrossThreadPersistent(
|
|
|
|
const BasicCrossThreadPersistent& other,
|
|
|
|
const SourceLocation& loc = SourceLocation::Current())
|
|
|
|
: BasicCrossThreadPersistent(loc) {
|
|
|
|
// Invoke operator=.
|
|
|
|
*this = other;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Heterogeneous ctor.
|
|
|
|
template <typename U, typename OtherWeaknessPolicy,
|
|
|
|
typename OtherLocationPolicy, typename OtherCheckingPolicy,
|
|
|
|
typename = std::enable_if_t<std::is_base_of<T, U>::value>>
|
|
|
|
BasicCrossThreadPersistent( // NOLINT
|
|
|
|
const BasicCrossThreadPersistent<U, OtherWeaknessPolicy,
|
|
|
|
OtherLocationPolicy,
|
|
|
|
OtherCheckingPolicy>& other,
|
|
|
|
const SourceLocation& loc = SourceLocation::Current())
|
|
|
|
: BasicCrossThreadPersistent(loc) {
|
|
|
|
*this = other;
|
|
|
|
}
|
|
|
|
|
|
|
|
BasicCrossThreadPersistent(
|
|
|
|
BasicCrossThreadPersistent&& other,
|
|
|
|
const SourceLocation& loc = SourceLocation::Current()) noexcept {
|
|
|
|
// Invoke operator=.
|
|
|
|
*this = std::move(other);
|
|
|
|
}
|
|
|
|
|
|
|
|
BasicCrossThreadPersistent& operator=(
|
|
|
|
const BasicCrossThreadPersistent& other) {
|
|
|
|
PersistentRegionLock guard;
|
|
|
|
AssignUnsafe(other.Get());
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename U, typename OtherWeaknessPolicy,
|
|
|
|
typename OtherLocationPolicy, typename OtherCheckingPolicy,
|
|
|
|
typename = std::enable_if_t<std::is_base_of<T, U>::value>>
|
|
|
|
BasicCrossThreadPersistent& operator=(
|
|
|
|
const BasicCrossThreadPersistent<U, OtherWeaknessPolicy,
|
|
|
|
OtherLocationPolicy,
|
|
|
|
OtherCheckingPolicy>& other) {
|
|
|
|
PersistentRegionLock guard;
|
|
|
|
AssignUnsafe(other.Get());
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
BasicCrossThreadPersistent& operator=(BasicCrossThreadPersistent&& other) {
|
|
|
|
if (this == &other) return *this;
|
|
|
|
Clear();
|
|
|
|
PersistentRegionLock guard;
|
|
|
|
PersistentBase::operator=(std::move(other));
|
|
|
|
LocationPolicy::operator=(std::move(other));
|
|
|
|
if (!IsValid(GetValue())) return *this;
|
|
|
|
GetNode()->UpdateOwner(this);
|
|
|
|
other.SetValue(nullptr);
|
|
|
|
other.SetNode(nullptr);
|
|
|
|
this->CheckPointer(GetValue());
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
BasicCrossThreadPersistent& operator=(T* other) {
|
|
|
|
Assign(other);
|
|
|
|
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>>
|
|
|
|
BasicCrossThreadPersistent& operator=(
|
|
|
|
internal::BasicMember<U, MemberBarrierPolicy, MemberWeaknessTag,
|
|
|
|
MemberCheckingPolicy>
|
|
|
|
member) {
|
|
|
|
return operator=(member.Get());
|
|
|
|
}
|
|
|
|
|
|
|
|
BasicCrossThreadPersistent& operator=(std::nullptr_t) {
|
|
|
|
Clear();
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
BasicCrossThreadPersistent& operator=(SentinelPointer s) {
|
|
|
|
Assign(s);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a pointer to the stored object.
|
|
|
|
*
|
|
|
|
* Note: **Not thread-safe.**
|
|
|
|
*
|
|
|
|
* \returns a pointer to the stored object.
|
|
|
|
*/
|
|
|
|
// 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 {
|
2021-01-13 11:56:06 +00:00
|
|
|
return static_cast<T*>(const_cast<void*>(GetValue()));
|
2020-10-09 12:06:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Clears the stored object.
|
|
|
|
*/
|
2021-01-28 11:03:59 +00:00
|
|
|
void Clear() {
|
|
|
|
// Simplified version of `Assign()` to allow calling without a complete type
|
|
|
|
// `T`.
|
|
|
|
const void* old_value = GetValue();
|
|
|
|
if (IsValid(old_value)) {
|
|
|
|
PersistentRegionLock guard;
|
|
|
|
PersistentRegion& region = this->GetPersistentRegion(old_value);
|
|
|
|
region.FreeNode(GetNode());
|
|
|
|
SetNode(nullptr);
|
|
|
|
}
|
|
|
|
SetValue(nullptr);
|
|
|
|
}
|
2020-10-09 12:06:43 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a pointer to the stored object and releases it.
|
|
|
|
*
|
|
|
|
* Note: **Not thread-safe.**
|
|
|
|
*
|
|
|
|
* \returns a pointer to the stored object.
|
|
|
|
*/
|
|
|
|
T* Release() {
|
|
|
|
T* result = Get();
|
|
|
|
Clear();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Conversio to boolean.
|
|
|
|
*
|
|
|
|
* Note: **Not thread-safe.**
|
|
|
|
*
|
|
|
|
* \returns true if an actual object has been stored and false otherwise.
|
|
|
|
*/
|
|
|
|
explicit operator bool() const { return Get(); }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Conversion to object of type T.
|
|
|
|
*
|
|
|
|
* Note: **Not thread-safe.**
|
|
|
|
*
|
|
|
|
* \returns the object.
|
|
|
|
*/
|
|
|
|
operator T*() const { return Get(); } // NOLINT
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Dereferences the stored object.
|
|
|
|
*
|
|
|
|
* Note: **Not thread-safe.**
|
|
|
|
*/
|
|
|
|
T* operator->() const { return Get(); }
|
|
|
|
T& operator*() const { return *Get(); }
|
|
|
|
|
2021-02-01 23:20:24 +00:00
|
|
|
template <typename U, typename OtherWeaknessPolicy = WeaknessPolicy,
|
|
|
|
typename OtherLocationPolicy = LocationPolicy,
|
|
|
|
typename OtherCheckingPolicy = CheckingPolicy>
|
|
|
|
BasicCrossThreadPersistent<U, OtherWeaknessPolicy, OtherLocationPolicy,
|
|
|
|
OtherCheckingPolicy>
|
|
|
|
To() const {
|
|
|
|
PersistentRegionLock guard;
|
|
|
|
return BasicCrossThreadPersistent<U, OtherWeaknessPolicy,
|
|
|
|
OtherLocationPolicy, OtherCheckingPolicy>(
|
|
|
|
static_cast<U*>(Get()));
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename U = T,
|
|
|
|
typename = typename std::enable_if<!BasicCrossThreadPersistent<
|
|
|
|
U, WeaknessPolicy>::IsStrongPersistent::value>::type>
|
|
|
|
BasicCrossThreadPersistent<U, internal::StrongCrossThreadPersistentPolicy>
|
|
|
|
Lock() const {
|
|
|
|
return BasicCrossThreadPersistent<
|
|
|
|
U, internal::StrongCrossThreadPersistentPolicy>(*this);
|
|
|
|
}
|
|
|
|
|
2020-10-09 12:06:43 +00:00
|
|
|
private:
|
2021-01-13 11:56:06 +00:00
|
|
|
static bool IsValid(const void* ptr) {
|
|
|
|
return ptr && ptr != kSentinelPointer;
|
|
|
|
}
|
2020-10-09 12:06:43 +00:00
|
|
|
|
|
|
|
static void Trace(Visitor* v, const void* ptr) {
|
|
|
|
const auto* handle = static_cast<const BasicCrossThreadPersistent*>(ptr);
|
|
|
|
v->TraceRoot(*handle, handle->Location());
|
|
|
|
}
|
|
|
|
|
|
|
|
void Assign(T* ptr) {
|
2021-01-13 11:56:06 +00:00
|
|
|
const void* old_value = GetValue();
|
2020-10-09 12:06:43 +00:00
|
|
|
if (IsValid(old_value)) {
|
|
|
|
PersistentRegionLock guard;
|
|
|
|
PersistentRegion& region = this->GetPersistentRegion(old_value);
|
|
|
|
if (IsValid(ptr) && (®ion == &this->GetPersistentRegion(ptr))) {
|
|
|
|
SetValue(ptr);
|
|
|
|
this->CheckPointer(ptr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
region.FreeNode(GetNode());
|
|
|
|
SetNode(nullptr);
|
|
|
|
}
|
|
|
|
SetValue(ptr);
|
|
|
|
if (!IsValid(ptr)) return;
|
|
|
|
PersistentRegionLock guard;
|
|
|
|
SetNode(this->GetPersistentRegion(ptr).AllocateNode(this, &Trace));
|
|
|
|
this->CheckPointer(ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void AssignUnsafe(T* ptr) {
|
2021-02-24 11:10:00 +00:00
|
|
|
PersistentRegionLock::AssertLocked();
|
2021-01-13 11:56:06 +00:00
|
|
|
const void* old_value = GetValue();
|
2020-10-09 12:06:43 +00:00
|
|
|
if (IsValid(old_value)) {
|
|
|
|
PersistentRegion& region = this->GetPersistentRegion(old_value);
|
|
|
|
if (IsValid(ptr) && (®ion == &this->GetPersistentRegion(ptr))) {
|
|
|
|
SetValue(ptr);
|
|
|
|
this->CheckPointer(ptr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
region.FreeNode(GetNode());
|
|
|
|
SetNode(nullptr);
|
|
|
|
}
|
|
|
|
SetValue(ptr);
|
|
|
|
if (!IsValid(ptr)) return;
|
|
|
|
SetNode(this->GetPersistentRegion(ptr).AllocateNode(this, &Trace));
|
|
|
|
this->CheckPointer(ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClearFromGC() const {
|
|
|
|
if (IsValid(GetValue())) {
|
|
|
|
WeaknessPolicy::GetPersistentRegion(GetValue()).FreeNode(GetNode());
|
|
|
|
PersistentBase::ClearFromGC();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
friend class cppgc::Visitor;
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename T, typename LocationPolicy, typename CheckingPolicy>
|
|
|
|
struct IsWeak<
|
|
|
|
BasicCrossThreadPersistent<T, internal::WeakCrossThreadPersistentPolicy,
|
|
|
|
LocationPolicy, CheckingPolicy>>
|
|
|
|
: std::true_type {};
|
|
|
|
|
|
|
|
} // namespace internal
|
|
|
|
|
|
|
|
namespace subtle {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* **DO NOT USE: Has known caveats, see below.**
|
|
|
|
*
|
|
|
|
* CrossThreadPersistent allows retaining objects from threads other than the
|
|
|
|
* thread the owning heap is operating on.
|
|
|
|
*
|
|
|
|
* Known caveats:
|
|
|
|
* - Does not protect the heap owning an object from terminating.
|
|
|
|
* - Reaching transitively through the graph is unsupported as objects may be
|
|
|
|
* moved concurrently on the thread owning the object.
|
|
|
|
*/
|
|
|
|
template <typename T>
|
|
|
|
using CrossThreadPersistent = internal::BasicCrossThreadPersistent<
|
|
|
|
T, internal::StrongCrossThreadPersistentPolicy>;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* **DO NOT USE: Has known caveats, see below.**
|
|
|
|
*
|
|
|
|
* CrossThreadPersistent allows weakly retaining objects from threads other than
|
|
|
|
* the thread the owning heap is operating on.
|
|
|
|
*
|
|
|
|
* Known caveats:
|
|
|
|
* - Does not protect the heap owning an object from terminating.
|
|
|
|
* - Reaching transitively through the graph is unsupported as objects may be
|
|
|
|
* moved concurrently on the thread owning the object.
|
|
|
|
*/
|
|
|
|
template <typename T>
|
|
|
|
using WeakCrossThreadPersistent = internal::BasicCrossThreadPersistent<
|
|
|
|
T, internal::WeakCrossThreadPersistentPolicy>;
|
|
|
|
|
|
|
|
} // namespace subtle
|
|
|
|
} // namespace cppgc
|
|
|
|
|
|
|
|
#endif // INCLUDE_CPPGC_CROSS_THREAD_PERSISTENT_H_
|