Commit fe5f67e9 authored by Omer Katz's avatar Omer Katz Committed by Commit Bot

cppgc: Add checks and locks to (Weak)CrossThreadPersistents

This CL adds missing locks to the PersistentRegions for
(Weak)CrossThreadPersistents.
To make sure no locks are missed in the future, this CL also splits
PersistentRegion and introduces CrossThreadPersistentRegion that checks
whether a lock is taken whenever it is accessed.

Bug: chromium:1056170
Change-Id: Iaaef4a28af0f02bcb896706e9abf1ee5ad2ee1e1
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2737299
Commit-Queue: Omer Katz <omerkatz@chromium.org>
Reviewed-by: 's avatarMichael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#73264}
parent c249669c
...@@ -45,7 +45,7 @@ class BasicCrossThreadPersistent final : public PersistentBase, ...@@ -45,7 +45,7 @@ class BasicCrossThreadPersistent final : public PersistentBase,
: PersistentBase(raw), LocationPolicy(loc) { : PersistentBase(raw), LocationPolicy(loc) {
if (!IsValid(raw)) return; if (!IsValid(raw)) return;
PersistentRegionLock guard; PersistentRegionLock guard;
PersistentRegion& region = this->GetPersistentRegion(raw); CrossThreadPersistentRegion& region = this->GetPersistentRegion(raw);
SetNode(region.AllocateNode(this, &Trace)); SetNode(region.AllocateNode(this, &Trace));
this->CheckPointer(raw); this->CheckPointer(raw);
} }
...@@ -63,7 +63,7 @@ class BasicCrossThreadPersistent final : public PersistentBase, ...@@ -63,7 +63,7 @@ class BasicCrossThreadPersistent final : public PersistentBase,
const SourceLocation& loc = SourceLocation::Current()) const SourceLocation& loc = SourceLocation::Current())
: PersistentBase(raw), LocationPolicy(loc) { : PersistentBase(raw), LocationPolicy(loc) {
if (!IsValid(raw)) return; if (!IsValid(raw)) return;
PersistentRegion& region = this->GetPersistentRegion(raw); CrossThreadPersistentRegion& region = this->GetPersistentRegion(raw);
SetNode(region.AllocateNode(this, &Trace)); SetNode(region.AllocateNode(this, &Trace));
this->CheckPointer(raw); this->CheckPointer(raw);
} }
...@@ -192,7 +192,8 @@ class BasicCrossThreadPersistent final : public PersistentBase, ...@@ -192,7 +192,8 @@ class BasicCrossThreadPersistent final : public PersistentBase,
const void* old_value = GetValue(); const void* old_value = GetValue();
if (IsValid(old_value)) { if (IsValid(old_value)) {
PersistentRegionLock guard; PersistentRegionLock guard;
PersistentRegion& region = this->GetPersistentRegion(old_value); CrossThreadPersistentRegion& region =
this->GetPersistentRegion(old_value);
region.FreeNode(GetNode()); region.FreeNode(GetNode());
SetNode(nullptr); SetNode(nullptr);
} }
...@@ -276,7 +277,8 @@ class BasicCrossThreadPersistent final : public PersistentBase, ...@@ -276,7 +277,8 @@ class BasicCrossThreadPersistent final : public PersistentBase,
const void* old_value = GetValue(); const void* old_value = GetValue();
if (IsValid(old_value)) { if (IsValid(old_value)) {
PersistentRegionLock guard; PersistentRegionLock guard;
PersistentRegion& region = this->GetPersistentRegion(old_value); CrossThreadPersistentRegion& region =
this->GetPersistentRegion(old_value);
if (IsValid(ptr) && (&region == &this->GetPersistentRegion(ptr))) { if (IsValid(ptr) && (&region == &this->GetPersistentRegion(ptr))) {
SetValue(ptr); SetValue(ptr);
this->CheckPointer(ptr); this->CheckPointer(ptr);
...@@ -296,7 +298,8 @@ class BasicCrossThreadPersistent final : public PersistentBase, ...@@ -296,7 +298,8 @@ class BasicCrossThreadPersistent final : public PersistentBase,
PersistentRegionLock::AssertLocked(); PersistentRegionLock::AssertLocked();
const void* old_value = GetValue(); const void* old_value = GetValue();
if (IsValid(old_value)) { if (IsValid(old_value)) {
PersistentRegion& region = this->GetPersistentRegion(old_value); CrossThreadPersistentRegion& region =
this->GetPersistentRegion(old_value);
if (IsValid(ptr) && (&region == &this->GetPersistentRegion(ptr))) { if (IsValid(ptr) && (&region == &this->GetPersistentRegion(ptr))) {
SetValue(ptr); SetValue(ptr);
this->CheckPointer(ptr); this->CheckPointer(ptr);
......
...@@ -19,6 +19,8 @@ class Visitor; ...@@ -19,6 +19,8 @@ class Visitor;
namespace internal { namespace internal {
class CrossThreadPersistentRegion;
// PersistentNode represents a variant of two states: // PersistentNode represents a variant of two states:
// 1) traceable node with a back pointer to the Persistent object; // 1) traceable node with a back pointer to the Persistent object;
// 2) freelist entry. // 2) freelist entry.
...@@ -116,6 +118,8 @@ class V8_EXPORT PersistentRegion final { ...@@ -116,6 +118,8 @@ class V8_EXPORT PersistentRegion final {
std::vector<std::unique_ptr<PersistentNodeSlots>> nodes_; std::vector<std::unique_ptr<PersistentNodeSlots>> nodes_;
PersistentNode* free_list_head_ = nullptr; PersistentNode* free_list_head_ = nullptr;
size_t nodes_in_use_ = 0; size_t nodes_in_use_ = 0;
friend class CrossThreadPersistentRegion;
}; };
// CrossThreadPersistent uses PersistentRegion but protects it using this lock // CrossThreadPersistent uses PersistentRegion but protects it using this lock
...@@ -128,6 +132,38 @@ class V8_EXPORT PersistentRegionLock final { ...@@ -128,6 +132,38 @@ class V8_EXPORT PersistentRegionLock final {
static void AssertLocked(); static void AssertLocked();
}; };
// Variant of PersistentRegion that checks whether the PersistentRegionLock is
// locked.
class V8_EXPORT CrossThreadPersistentRegion final {
public:
CrossThreadPersistentRegion() = default;
// Clears Persistent fields to avoid stale pointers after heap teardown.
~CrossThreadPersistentRegion();
CrossThreadPersistentRegion(const CrossThreadPersistentRegion&) = delete;
CrossThreadPersistentRegion& operator=(const CrossThreadPersistentRegion&) =
delete;
V8_INLINE PersistentNode* AllocateNode(void* owner, TraceCallback trace) {
PersistentRegionLock::AssertLocked();
return persistent_region_.AllocateNode(owner, trace);
}
V8_INLINE void FreeNode(PersistentNode* node) {
PersistentRegionLock::AssertLocked();
persistent_region_.FreeNode(node);
}
void Trace(Visitor*);
size_t NodesInUse() const;
void ClearAllUsedNodes();
private:
PersistentRegion persistent_region_;
};
} // namespace internal } // namespace internal
} // namespace cppgc } // namespace cppgc
......
...@@ -16,6 +16,7 @@ namespace cppgc { ...@@ -16,6 +16,7 @@ namespace cppgc {
namespace internal { namespace internal {
class PersistentRegion; class PersistentRegion;
class CrossThreadPersistentRegion;
// Tags to distinguish between strong and weak member types. // Tags to distinguish between strong and weak member types.
class StrongMemberTag; class StrongMemberTag;
...@@ -115,12 +116,14 @@ struct WeakPersistentPolicy { ...@@ -115,12 +116,14 @@ struct WeakPersistentPolicy {
struct StrongCrossThreadPersistentPolicy { struct StrongCrossThreadPersistentPolicy {
using IsStrongPersistent = std::true_type; using IsStrongPersistent = std::true_type;
static V8_EXPORT PersistentRegion& GetPersistentRegion(const void* object); static V8_EXPORT CrossThreadPersistentRegion& GetPersistentRegion(
const void* object);
}; };
struct WeakCrossThreadPersistentPolicy { struct WeakCrossThreadPersistentPolicy {
using IsStrongPersistent = std::false_type; using IsStrongPersistent = std::false_type;
static V8_EXPORT PersistentRegion& GetPersistentRegion(const void* object); static V8_EXPORT CrossThreadPersistentRegion& GetPersistentRegion(
const void* object);
}; };
// Forward declarations setting up the default policies. // Forward declarations setting up the default policies.
......
...@@ -704,6 +704,7 @@ void CppGraphBuilderImpl::Run() { ...@@ -704,6 +704,7 @@ void CppGraphBuilderImpl::Run() {
ParentScope parent_scope( ParentScope parent_scope(
states_.CreateRootState(AddRootNode("C++ cross-thread roots"))); states_.CreateRootState(AddRootNode("C++ cross-thread roots")));
GraphBuildingVisitor object_visitor(*this, parent_scope); GraphBuildingVisitor object_visitor(*this, parent_scope);
cppgc::internal::PersistentRegionLock guard;
cpp_heap_.GetStrongCrossThreadPersistentRegion().Trace(&object_visitor); cpp_heap_.GetStrongCrossThreadPersistentRegion().Trace(&object_visitor);
} }
} }
......
...@@ -111,11 +111,12 @@ void HeapBase::Terminate() { ...@@ -111,11 +111,12 @@ void HeapBase::Terminate() {
// Clear root sets. // Clear root sets.
strong_persistent_region_.ClearAllUsedNodes(); strong_persistent_region_.ClearAllUsedNodes();
strong_cross_thread_persistent_region_.ClearAllUsedNodes();
// Clear weak root sets, as the GC below does not execute weakness
// callbacks.
weak_persistent_region_.ClearAllUsedNodes(); weak_persistent_region_.ClearAllUsedNodes();
weak_cross_thread_persistent_region_.ClearAllUsedNodes(); {
PersistentRegionLock guard;
strong_cross_thread_persistent_region_.ClearAllUsedNodes();
weak_cross_thread_persistent_region_.ClearAllUsedNodes();
}
stats_collector()->NotifyMarkingStarted( stats_collector()->NotifyMarkingStarted(
GarbageCollector::Config::CollectionType::kMajor, GarbageCollector::Config::CollectionType::kMajor,
...@@ -131,6 +132,11 @@ void HeapBase::Terminate() { ...@@ -131,6 +132,11 @@ void HeapBase::Terminate() {
object_allocator().Terminate(); object_allocator().Terminate();
disallow_gc_scope_++; disallow_gc_scope_++;
CHECK_EQ(0u, strong_persistent_region_.NodesInUse());
CHECK_EQ(0u, weak_persistent_region_.NodesInUse());
CHECK_EQ(0u, strong_cross_thread_persistent_region_.NodesInUse());
CHECK_EQ(0u, weak_cross_thread_persistent_region_.NodesInUse());
} }
HeapStatistics HeapBase::CollectStatistics( HeapStatistics HeapBase::CollectStatistics(
......
...@@ -130,16 +130,18 @@ class V8_EXPORT_PRIVATE HeapBase : public cppgc::HeapHandle { ...@@ -130,16 +130,18 @@ class V8_EXPORT_PRIVATE HeapBase : public cppgc::HeapHandle {
const PersistentRegion& GetWeakPersistentRegion() const { const PersistentRegion& GetWeakPersistentRegion() const {
return weak_persistent_region_; return weak_persistent_region_;
} }
PersistentRegion& GetStrongCrossThreadPersistentRegion() { CrossThreadPersistentRegion& GetStrongCrossThreadPersistentRegion() {
return strong_cross_thread_persistent_region_; return strong_cross_thread_persistent_region_;
} }
const PersistentRegion& GetStrongCrossThreadPersistentRegion() const { const CrossThreadPersistentRegion& GetStrongCrossThreadPersistentRegion()
const {
return strong_cross_thread_persistent_region_; return strong_cross_thread_persistent_region_;
} }
PersistentRegion& GetWeakCrossThreadPersistentRegion() { CrossThreadPersistentRegion& GetWeakCrossThreadPersistentRegion() {
return weak_cross_thread_persistent_region_; return weak_cross_thread_persistent_region_;
} }
const PersistentRegion& GetWeakCrossThreadPersistentRegion() const { const CrossThreadPersistentRegion& GetWeakCrossThreadPersistentRegion()
const {
return weak_cross_thread_persistent_region_; return weak_cross_thread_persistent_region_;
} }
...@@ -201,8 +203,8 @@ class V8_EXPORT_PRIVATE HeapBase : public cppgc::HeapHandle { ...@@ -201,8 +203,8 @@ class V8_EXPORT_PRIVATE HeapBase : public cppgc::HeapHandle {
PersistentRegion strong_persistent_region_; PersistentRegion strong_persistent_region_;
PersistentRegion weak_persistent_region_; PersistentRegion weak_persistent_region_;
PersistentRegion strong_cross_thread_persistent_region_; CrossThreadPersistentRegion strong_cross_thread_persistent_region_;
PersistentRegion weak_cross_thread_persistent_region_; CrossThreadPersistentRegion weak_cross_thread_persistent_region_;
ProcessHeapStatisticsUpdater::AllocationObserverImpl ProcessHeapStatisticsUpdater::AllocationObserverImpl
allocation_observer_for_PROCESS_HEAP_STATISTICS_; allocation_observer_for_PROCESS_HEAP_STATISTICS_;
......
...@@ -95,5 +95,26 @@ void PersistentRegionLock::AssertLocked() { ...@@ -95,5 +95,26 @@ void PersistentRegionLock::AssertLocked() {
return g_process_mutex.Pointer()->AssertHeld(); return g_process_mutex.Pointer()->AssertHeld();
} }
CrossThreadPersistentRegion::~CrossThreadPersistentRegion() {
PersistentRegionLock guard;
persistent_region_.ClearAllUsedNodes();
persistent_region_.nodes_.clear();
}
void CrossThreadPersistentRegion::Trace(Visitor* visitor) {
PersistentRegionLock::AssertLocked();
return persistent_region_.Trace(visitor);
}
size_t CrossThreadPersistentRegion::NodesInUse() const {
// This method does not require a lock.
return persistent_region_.NodesInUse();
}
void CrossThreadPersistentRegion::ClearAllUsedNodes() {
PersistentRegionLock::AssertLocked();
return persistent_region_.ClearAllUsedNodes();
}
} // namespace internal } // namespace internal
} // namespace cppgc } // namespace cppgc
...@@ -33,14 +33,14 @@ PersistentRegion& WeakPersistentPolicy::GetPersistentRegion( ...@@ -33,14 +33,14 @@ PersistentRegion& WeakPersistentPolicy::GetPersistentRegion(
return heap->GetWeakPersistentRegion(); return heap->GetWeakPersistentRegion();
} }
PersistentRegion& StrongCrossThreadPersistentPolicy::GetPersistentRegion( CrossThreadPersistentRegion&
const void* object) { StrongCrossThreadPersistentPolicy::GetPersistentRegion(const void* object) {
auto* heap = BasePage::FromPayload(object)->heap(); auto* heap = BasePage::FromPayload(object)->heap();
return heap->GetStrongCrossThreadPersistentRegion(); return heap->GetStrongCrossThreadPersistentRegion();
} }
PersistentRegion& WeakCrossThreadPersistentPolicy::GetPersistentRegion( CrossThreadPersistentRegion&
const void* object) { WeakCrossThreadPersistentPolicy::GetPersistentRegion(const void* object) {
auto* heap = BasePage::FromPayload(object)->heap(); auto* heap = BasePage::FromPayload(object)->heap();
return heap->GetWeakCrossThreadPersistentRegion(); return heap->GetWeakCrossThreadPersistentRegion();
} }
......
...@@ -52,20 +52,20 @@ struct PersistentRegionTrait<WeakPersistent> { ...@@ -52,20 +52,20 @@ struct PersistentRegionTrait<WeakPersistent> {
template <> template <>
struct PersistentRegionTrait<subtle::CrossThreadPersistent> { struct PersistentRegionTrait<subtle::CrossThreadPersistent> {
static PersistentRegion& Get(cppgc::Heap* heap) { static CrossThreadPersistentRegion& Get(cppgc::Heap* heap) {
return internal::Heap::From(heap)->GetStrongCrossThreadPersistentRegion(); return internal::Heap::From(heap)->GetStrongCrossThreadPersistentRegion();
} }
}; };
template <> template <>
struct PersistentRegionTrait<subtle::WeakCrossThreadPersistent> { struct PersistentRegionTrait<subtle::WeakCrossThreadPersistent> {
static PersistentRegion& Get(cppgc::Heap* heap) { static CrossThreadPersistentRegion& Get(cppgc::Heap* heap) {
return internal::Heap::From(heap)->GetWeakCrossThreadPersistentRegion(); return internal::Heap::From(heap)->GetWeakCrossThreadPersistentRegion();
} }
}; };
template <template <typename> class PersistentType> template <template <typename> class PersistentType>
PersistentRegion& GetRegion(cppgc::Heap* heap) { auto& GetRegion(cppgc::Heap* heap) {
return PersistentRegionTrait<PersistentType>::Get(heap); return PersistentRegionTrait<PersistentType>::Get(heap);
} }
...@@ -114,31 +114,31 @@ class PersistentTest : public testing::TestSupportingAllocationOnly {}; ...@@ -114,31 +114,31 @@ class PersistentTest : public testing::TestSupportingAllocationOnly {};
template <template <typename> class PersistentType> template <template <typename> class PersistentType>
void NullStateCtor(cppgc::Heap* heap) { void NullStateCtor(cppgc::Heap* heap) {
EXPECT_EQ(0u, GetRegion<Persistent>(heap).NodesInUse()); EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
{ {
PersistentType<GCed> empty; PersistentType<GCed> empty;
EXPECT_EQ(nullptr, empty.Get()); EXPECT_EQ(nullptr, empty.Get());
EXPECT_EQ(nullptr, empty.Release()); EXPECT_EQ(nullptr, empty.Release());
EXPECT_EQ(0u, GetRegion<Persistent>(heap).NodesInUse()); EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
} }
{ {
PersistentType<GCed> empty = nullptr; PersistentType<GCed> empty = nullptr;
EXPECT_EQ(nullptr, empty.Get()); EXPECT_EQ(nullptr, empty.Get());
EXPECT_EQ(nullptr, empty.Release()); EXPECT_EQ(nullptr, empty.Release());
EXPECT_EQ(0u, GetRegion<Persistent>(heap).NodesInUse()); EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
} }
{ {
PersistentType<GCed> empty = kSentinelPointer; PersistentType<GCed> empty = kSentinelPointer;
EXPECT_EQ(kSentinelPointer, empty); EXPECT_EQ(kSentinelPointer, empty);
EXPECT_EQ(kSentinelPointer, empty.Release()); EXPECT_EQ(kSentinelPointer, empty.Release());
EXPECT_EQ(0u, GetRegion<Persistent>(heap).NodesInUse()); EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
} }
{ {
// Runtime null must not allocated associated node. // Runtime null must not allocated associated node.
PersistentType<GCed> empty = static_cast<GCed*>(nullptr); PersistentType<GCed> empty = static_cast<GCed*>(nullptr);
EXPECT_EQ(nullptr, empty.Get()); EXPECT_EQ(nullptr, empty.Get());
EXPECT_EQ(nullptr, empty.Release()); EXPECT_EQ(nullptr, empty.Release());
EXPECT_EQ(0u, GetRegion<Persistent>(heap).NodesInUse()); EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
} }
EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse()); EXPECT_EQ(0u, GetRegion<PersistentType>(heap).NodesInUse());
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment