cppgc: Port Member

This CL introduces
 - Member
 - WeakMember
 - UntracedMember
interfaces. Remaining work is to add pointer verifier and write barrier
implementation.

Bug: chromium:1056170
Change-Id: Iddb8e4d002db0b1d1652f2946ddfa08a98a889c7
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2124323
Commit-Queue: Anton Bikineev <bikineev@chromium.org>
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Omer Katz <omerkatz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66927}
This commit is contained in:
Anton Bikineev 2020-03-31 19:19:02 +02:00 committed by Commit Bot
parent 04a7a680a2
commit d8936aac8f
6 changed files with 513 additions and 0 deletions

View File

@ -3933,7 +3933,9 @@ v8_source_set("cppgc_base") {
"include/cppgc/garbage-collected.h",
"include/cppgc/gc-info.h",
"include/cppgc/heap.h",
"include/cppgc/member.h",
"include/cppgc/platform.h",
"include/cppgc/type_traits.h",
"include/v8config.h",
"src/heap/cppgc/allocation.cc",
"src/heap/cppgc/gc-info-table.cc",
@ -3945,6 +3947,7 @@ v8_source_set("cppgc_base") {
"src/heap/cppgc/heap-object-header.h",
"src/heap/cppgc/heap.cc",
"src/heap/cppgc/heap.h",
"src/heap/cppgc/member.cc",
"src/heap/cppgc/platform.cc",
"src/heap/cppgc/sanitizers.h",
"src/heap/cppgc/stack.cc",

216
include/cppgc/member.h Normal file
View File

@ -0,0 +1,216 @@
// 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_MEMBER_H_
#define INCLUDE_CPPGC_MEMBER_H_
#include <atomic>
#include <cstddef>
#include "include/cppgc/garbage-collected.h"
#include "include/v8config.h"
namespace cppgc {
namespace internal {
struct DijkstraWriteBarrierPolicy {
static void InitializingBarrier(const void*, const void*) {
// Since in initializing writes the source object is always white, having no
// barrier doesn't break the tri-color invariant.
}
static void AssigningBarrier(const void*, const void*) {
// TODO(chromium:1056170): Add actual implementation.
}
};
struct NoWriteBarrierPolicy {
static void InitializingBarrier(const void*, const void*) {}
static void AssigningBarrier(const void*, const void*) {}
};
class V8_EXPORT EnabledCheckingPolicy {
public:
EnabledCheckingPolicy();
void CheckPointer(const void* ptr);
private:
void* impl_;
};
class DisabledCheckingPolicy {
public:
void CheckPointer(const void* raw) {}
};
#if V8_ENABLE_CHECKS
using DefaultCheckingPolicy = EnabledCheckingPolicy;
#else
using DefaultCheckingPolicy = DisabledCheckingPolicy;
#endif
// Special tag type used to denote some sentinel member. The semantics of the
// sentinel is defined by the embedder.
struct MemberSentinel {};
constexpr MemberSentinel kMemberSentinel;
// The basic class from which all Member classes are 'generated'.
template <typename T, class WeaknessTag, class WriteBarrierPolicy,
class CheckingPolicy = DefaultCheckingPolicy>
class BasicMember : private CheckingPolicy {
public:
constexpr BasicMember() = default;
constexpr BasicMember(std::nullptr_t) {} // NOLINT
constexpr BasicMember(MemberSentinel) // NOLINT
: raw_(reinterpret_cast<T*>(kSentinelValue)) {}
BasicMember(T* raw) : raw_(raw) { // NOLINT
InitializingWriteBarrier();
this->CheckPointer(raw_);
}
// TODO(chromium:1056170): Unfortunately, this overload is used ubiquitously
// in Blink. Reeavalute the possibility to remove it.
BasicMember(T& raw) : BasicMember(&raw) {} // NOLINT
BasicMember(const BasicMember& other) : BasicMember(other.Get()) {}
// Allow heterogeneous construction.
template <typename U, typename OtherBarrierPolicy, typename OtherWeaknessTag,
typename OtherCheckingPolicy>
BasicMember(const BasicMember<U, OtherWeaknessTag, OtherBarrierPolicy,
OtherCheckingPolicy>& other)
: BasicMember(other.Get()) {}
BasicMember& operator=(const BasicMember& other) {
return operator=(other.Get());
}
// Allow heterogeneous assignment.
template <typename U, typename OtherWeaknessTag, typename OtherBarrierPolicy,
typename OtherCheckingPolicy>
BasicMember& operator=(
const BasicMember<U, OtherWeaknessTag, OtherBarrierPolicy,
OtherCheckingPolicy>& other) {
return operator=(other.Get());
}
BasicMember& operator=(T* other) {
SetRawAtomic(other);
AssigningWriteBarrier();
this->CheckPointer(Get());
return *this;
}
BasicMember& operator=(std::nullptr_t) {
Clear();
return *this;
}
BasicMember& operator=(MemberSentinel) {
SetRawAtomic(reinterpret_cast<T*>(kSentinelValue));
return *this;
}
template <typename U, typename OtherWeaknessTag, typename OtherBarrierPolicy,
typename OtherCheckingPolicy>
void Swap(BasicMember<U, OtherWeaknessTag, OtherBarrierPolicy,
OtherCheckingPolicy>& other) {
T* tmp = Get();
*this = other;
other = tmp;
}
explicit operator bool() const { return Get(); }
operator T*() const { return Get(); }
T* operator->() const { return Get(); }
T& operator*() const { return *Get(); }
T* Get() const {
// Executed by the mutator, hence non atomic load.
return raw_;
}
void Clear() { SetRawAtomic(nullptr); }
T* Release() {
T* result = Get();
Clear();
return result;
}
private:
// Must not be odr-used.
static constexpr intptr_t kSentinelValue = -1;
void SetRawAtomic(T* raw) {
reinterpret_cast<std::atomic<T*>*>(&raw_)->store(raw,
std::memory_order_relaxed);
}
T* GetRawAtomic() const {
return reinterpret_cast<const std::atomic<T*>*>(&raw_)->load(
std::memory_order_relaxed);
}
void InitializingWriteBarrier() const {
WriteBarrierPolicy::InitializingBarrier(
reinterpret_cast<const void*>(&raw_), static_cast<const void*>(raw_));
}
void AssigningWriteBarrier() const {
WriteBarrierPolicy::AssigningBarrier(reinterpret_cast<const void*>(&raw_),
static_cast<const void*>(raw_));
}
T* raw_ = nullptr;
};
template <typename T1, typename WeaknessTag1, typename WriteBarrierPolicy1,
typename CheckingPolicy1, typename T2, typename WeaknessTag2,
typename WriteBarrierPolicy2, typename CheckingPolicy2>
bool operator==(
BasicMember<T1, WeaknessTag1, WriteBarrierPolicy1, CheckingPolicy1> member1,
BasicMember<T2, WeaknessTag2, WriteBarrierPolicy2, CheckingPolicy2>
member2) {
return member1.Get() == member2.Get();
}
template <typename T1, typename WeaknessTag1, typename WriteBarrierPolicy1,
typename CheckingPolicy1, typename T2, typename WeaknessTag2,
typename WriteBarrierPolicy2, typename CheckingPolicy2>
bool operator!=(
BasicMember<T1, WeaknessTag1, WriteBarrierPolicy1, CheckingPolicy1> member1,
BasicMember<T2, WeaknessTag2, WriteBarrierPolicy2, CheckingPolicy2>
member2) {
return !(member1 == member2);
}
template <typename T, typename WriteBarrierPolicy>
using BasicStrongMember =
BasicMember<T, class StrongMemberTag, WriteBarrierPolicy>;
template <typename T, typename WriteBarrierPolicy>
using BasicWeakMember = BasicMember<T, class WeakMemberTag, WriteBarrierPolicy>;
} // namespace internal
// Members are used in classes to contain strong pointers to other garbage
// collected objects. All Member fields of a class must be traced in the class'
// trace method.
template <typename T>
using Member =
internal::BasicStrongMember<T, internal::DijkstraWriteBarrierPolicy>;
// WeakMember is similar to Member in that it is used to point to other garbage
// collected objects. However instead of creating a strong pointer to the
// object, the WeakMember creates a weak pointer, which does not keep the
// pointee alive. Hence if all pointers to to a heap allocated object are weak
// the object will be garbage collected. At the time of GC the weak pointers
// will automatically be set to null.
template <typename T>
using WeakMember =
internal::BasicWeakMember<T, internal::DijkstraWriteBarrierPolicy>;
// UntracedMember is a pointer to an on-heap object that is not traced for some
// reason. Do not use this unless you know what you are doing. Keeping raw
// pointers to on-heap objects is prohibited unless used from stack. Pointee
// must be kept alive through other means.
template <typename T>
using UntracedMember = internal::BasicMember<T, class UntracedMemberTag,
internal::NoWriteBarrierPolicy>;
} // namespace cppgc
#endif // INCLUDE_CPPGC_MEMBER_H_

View File

@ -0,0 +1,31 @@
// 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_TYPE_TRAITS_H_
#define INCLUDE_CPPGC_TYPE_TRAITS_H_
#include <type_traits>
#include "include/cppgc/member.h"
namespace cppgc {
namespace internal {
// Not supposed to be specialized by the user.
template <typename T>
struct IsWeak : std::false_type {};
template <typename T, typename WriteBarrierPolicy>
struct IsWeak<internal::BasicWeakMember<T, WriteBarrierPolicy>>
: std::true_type {};
} // namespace internal
template <typename T>
constexpr bool IsWeakV = internal::IsWeak<T>::value;
} // namespace cppgc
#endif // INCLUDE_CPPGC_TYPE_TRAITS_H_

22
src/heap/cppgc/member.cc Normal file
View File

@ -0,0 +1,22 @@
// 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.
#include "include/cppgc/member.h"
#include "src/base/macros.h"
namespace cppgc {
namespace internal {
EnabledCheckingPolicy::EnabledCheckingPolicy() {
USE(impl_);
// TODO(chromium:1056170): Save creating heap state.
}
void EnabledCheckingPolicy::CheckPointer(const void* ptr) {
// TODO(chromium:1056170): Provide implementation.
}
} // namespace internal
} // namespace cppgc

View File

@ -49,6 +49,7 @@ v8_source_set("cppgc_unittests_sources") {
"heap/cppgc/garbage-collected_unittest.cc",
"heap/cppgc/gc-info_unittest.cc",
"heap/cppgc/heap-object-header_unittest.cc",
"heap/cppgc/member_unittests.cc",
"heap/cppgc/stack_unittest.cc",
"heap/cppgc/tests.cc",
"heap/cppgc/tests.h",

View File

@ -0,0 +1,240 @@
// 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.
#include <array>
#include "include/cppgc/member.h"
#include "include/cppgc/type_traits.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cppgc {
namespace internal {
struct GCed : GarbageCollected<GCed> {};
struct DerivedGCed : GCed {};
// Compile tests.
static_assert(!IsWeakV<Member<GCed>>, "Member is always strong.");
static_assert(IsWeakV<WeakMember<GCed>>, "WeakMember is always weak.");
struct CustomWriteBarrierPolicy {
static size_t InitializingWriteBarriersTriggered;
static size_t AssigningWriteBarriersTriggered;
static void InitializingBarrier(const void* slot, const void* value) {
++InitializingWriteBarriersTriggered;
}
static void AssigningBarrier(const void* slot, const void* value) {
++AssigningWriteBarriersTriggered;
}
};
size_t CustomWriteBarrierPolicy::InitializingWriteBarriersTriggered = 0;
size_t CustomWriteBarrierPolicy::AssigningWriteBarriersTriggered = 0;
using MemberWithCustomBarrier =
BasicStrongMember<GCed, CustomWriteBarrierPolicy>;
struct CustomCheckingPolicy {
static std::array<GCed, 10> Array;
static size_t ChecksTriggered;
void CheckPointer(const void* ptr) {
EXPECT_LE(Array.data(), ptr);
EXPECT_GT(Array.data() + Array.size(), ptr);
++ChecksTriggered;
}
};
std::array<GCed, 10> CustomCheckingPolicy::Array;
size_t CustomCheckingPolicy::ChecksTriggered = 0;
using MemberWithCustomChecking =
internal::BasicMember<GCed, class StrongMemberTag,
DijkstraWriteBarrierPolicy, CustomCheckingPolicy>;
template <template <typename> class Member>
void EmptyTest() {
{
Member<GCed> empty;
EXPECT_EQ(nullptr, empty.Get());
EXPECT_EQ(nullptr, empty.Release());
}
{
Member<GCed> empty = nullptr;
EXPECT_EQ(nullptr, empty.Get());
EXPECT_EQ(nullptr, empty.Release());
}
}
TEST(MemberTest, Empty) {
EmptyTest<Member>();
EmptyTest<WeakMember>();
EmptyTest<UntracedMember>();
}
template <template <typename> class Member>
void ClearTest() {
GCed gced;
Member<GCed> member = &gced;
EXPECT_NE(nullptr, member.Get());
member.Clear();
EXPECT_EQ(nullptr, member.Get());
}
TEST(MemberTest, Clear) {
ClearTest<Member>();
ClearTest<WeakMember>();
ClearTest<UntracedMember>();
}
template <template <typename> class Member>
void ReleaseTest() {
GCed gced;
Member<GCed> member = &gced;
EXPECT_NE(nullptr, member.Get());
GCed* raw = member.Release();
EXPECT_EQ(&gced, raw);
EXPECT_EQ(nullptr, member.Get());
}
TEST(MemberTest, Release) {
ReleaseTest<Member>();
ReleaseTest<WeakMember>();
ReleaseTest<UntracedMember>();
}
template <template <typename> class Member1, template <typename> class Member2>
void SwapTest() {
GCed gced1, gced2;
Member1<GCed> member1 = &gced1;
Member2<GCed> member2 = &gced2;
EXPECT_EQ(&gced1, member1.Get());
EXPECT_EQ(&gced2, member2.Get());
member1.Swap(member2);
EXPECT_EQ(&gced2, member1.Get());
EXPECT_EQ(&gced1, member2.Get());
}
TEST(MemberTest, Swap) {
SwapTest<Member, Member>();
SwapTest<Member, WeakMember>();
SwapTest<Member, UntracedMember>();
SwapTest<WeakMember, Member>();
SwapTest<WeakMember, WeakMember>();
SwapTest<WeakMember, UntracedMember>();
SwapTest<UntracedMember, Member>();
SwapTest<UntracedMember, WeakMember>();
SwapTest<UntracedMember, UntracedMember>();
}
template <template <typename> class Member1, template <typename> class Member2>
void HeterogeneousConversionTest() {
{
GCed gced;
Member1<GCed> member1 = &gced;
Member2<GCed> member2 = member1;
EXPECT_EQ(member1.Get(), member2.Get());
}
{
DerivedGCed gced;
Member1<DerivedGCed> member1 = &gced;
Member2<GCed> member2 = member1;
EXPECT_EQ(member1.Get(), member2.Get());
}
{
GCed gced;
Member1<GCed> member1 = &gced;
Member2<GCed> member2;
member2 = member1;
EXPECT_EQ(member1.Get(), member2.Get());
}
{
DerivedGCed gced;
Member1<DerivedGCed> member1 = &gced;
Member2<GCed> member2;
member2 = member1;
EXPECT_EQ(member1.Get(), member2.Get());
}
}
TEST(MemberTest, HeterogeneousInterface) {
HeterogeneousConversionTest<Member, Member>();
HeterogeneousConversionTest<Member, WeakMember>();
HeterogeneousConversionTest<Member, UntracedMember>();
HeterogeneousConversionTest<WeakMember, Member>();
HeterogeneousConversionTest<WeakMember, WeakMember>();
HeterogeneousConversionTest<WeakMember, UntracedMember>();
HeterogeneousConversionTest<UntracedMember, Member>();
HeterogeneousConversionTest<UntracedMember, WeakMember>();
HeterogeneousConversionTest<UntracedMember, UntracedMember>();
}
template <template <typename> class Member1, template <typename> class Member2>
void EqualityTest() {
{
GCed gced;
Member1<GCed> member1 = &gced;
Member2<GCed> member2 = &gced;
EXPECT_TRUE(member1 == member2);
EXPECT_FALSE(member1 != member2);
member2 = member1;
EXPECT_TRUE(member1 == member2);
EXPECT_FALSE(member1 != member2);
}
{
GCed gced1;
GCed gced2;
Member1<GCed> member1 = &gced1;
Member2<GCed> member2 = &gced2;
EXPECT_TRUE(member1 != member2);
EXPECT_FALSE(member1 == member2);
}
}
TEST(MemberTest, EqualityTest) {
EqualityTest<Member, Member>();
EqualityTest<Member, WeakMember>();
EqualityTest<Member, UntracedMember>();
EqualityTest<WeakMember, Member>();
EqualityTest<WeakMember, WeakMember>();
EqualityTest<WeakMember, UntracedMember>();
EqualityTest<UntracedMember, Member>();
EqualityTest<UntracedMember, WeakMember>();
EqualityTest<UntracedMember, UntracedMember>();
}
TEST(MemberTest, WriteBarrierTriggered) {
CustomWriteBarrierPolicy::InitializingWriteBarriersTriggered = 0;
CustomWriteBarrierPolicy::AssigningWriteBarriersTriggered = 0;
GCed gced;
MemberWithCustomBarrier member1 = &gced;
EXPECT_EQ(1u, CustomWriteBarrierPolicy::InitializingWriteBarriersTriggered);
EXPECT_EQ(0u, CustomWriteBarrierPolicy::AssigningWriteBarriersTriggered);
member1 = &gced;
EXPECT_EQ(1u, CustomWriteBarrierPolicy::InitializingWriteBarriersTriggered);
EXPECT_EQ(1u, CustomWriteBarrierPolicy::AssigningWriteBarriersTriggered);
member1 = nullptr;
EXPECT_EQ(1u, CustomWriteBarrierPolicy::InitializingWriteBarriersTriggered);
EXPECT_EQ(1u, CustomWriteBarrierPolicy::AssigningWriteBarriersTriggered);
MemberWithCustomBarrier member2 = nullptr;
// No initializing barriers for std::nullptr_t.
EXPECT_EQ(1u, CustomWriteBarrierPolicy::InitializingWriteBarriersTriggered);
EXPECT_EQ(1u, CustomWriteBarrierPolicy::AssigningWriteBarriersTriggered);
member2 = kMemberSentinel;
// No initializing barriers for member sentinel.
EXPECT_EQ(1u, CustomWriteBarrierPolicy::InitializingWriteBarriersTriggered);
EXPECT_EQ(1u, CustomWriteBarrierPolicy::AssigningWriteBarriersTriggered);
member2.Swap(member1);
EXPECT_EQ(3u, CustomWriteBarrierPolicy::AssigningWriteBarriersTriggered);
}
TEST(MemberTest, CheckingPolocy) {
CustomCheckingPolicy::ChecksTriggered = 0u;
MemberWithCustomChecking member;
for (GCed& item : CustomCheckingPolicy::Array) {
member = &item;
}
EXPECT_EQ(CustomCheckingPolicy::Array.size(),
CustomCheckingPolicy::ChecksTriggered);
}
} // namespace internal
} // namespace cppgc