Commit 3902ffbb authored by Michael Lippautz's avatar Michael Lippautz Committed by V8 LUCI CQ

cppgc: Persistent: Check thread usage on slow path

Checks whether a Persistent is used from the creation thread on slow
path allocations. In practice, these currently happen every 256
Persistent allocations. This is a best effort check that may help to
flush out issues that are missed with DCHECK builds.

Bug: chromium:1276570
Change-Id: Ia868ca436341b1b5ef427d5b3ec04926c1394e41
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3318658
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: 's avatarAnton Bikineev <bikineev@chromium.org>
Cr-Commit-Position: refs/heads/main@{#78276}
parent 649c9805
...@@ -80,23 +80,31 @@ class V8_EXPORT PersistentRegionBase { ...@@ -80,23 +80,31 @@ class V8_EXPORT PersistentRegionBase {
using PersistentNodeSlots = std::array<PersistentNode, 256u>; using PersistentNodeSlots = std::array<PersistentNode, 256u>;
public: public:
explicit PersistentRegionBase(const FatalOutOfMemoryHandler& oom_handler);
// Clears Persistent fields to avoid stale pointers after heap teardown. // Clears Persistent fields to avoid stale pointers after heap teardown.
~PersistentRegionBase(); ~PersistentRegionBase();
PersistentRegionBase(const PersistentRegionBase&) = delete; PersistentRegionBase(const PersistentRegionBase&) = delete;
PersistentRegionBase& operator=(const PersistentRegionBase&) = delete; PersistentRegionBase& operator=(const PersistentRegionBase&) = delete;
PersistentNode* AllocateNode(void* owner, TraceCallback trace) { void Trace(Visitor*);
if (!free_list_head_) {
EnsureNodeSlots(); size_t NodesInUse() const;
CPPGC_DCHECK(free_list_head_);
} void ClearAllUsedNodes();
PersistentNode* node = free_list_head_;
protected:
explicit PersistentRegionBase(const FatalOutOfMemoryHandler& oom_handler);
PersistentNode* TryAllocateNodeFromFreeList(void* owner,
TraceCallback trace) {
PersistentNode* node = nullptr;
if (V8_LIKELY(free_list_head_)) {
node = free_list_head_;
free_list_head_ = free_list_head_->FreeListNext(); free_list_head_ = free_list_head_->FreeListNext();
CPPGC_DCHECK(!node->IsUsed()); CPPGC_DCHECK(!node->IsUsed());
node->InitializeAsUsedNode(owner, trace); node->InitializeAsUsedNode(owner, trace);
nodes_in_use_++; nodes_in_use_++;
}
return node; return node;
} }
...@@ -109,18 +117,15 @@ class V8_EXPORT PersistentRegionBase { ...@@ -109,18 +117,15 @@ class V8_EXPORT PersistentRegionBase {
nodes_in_use_--; nodes_in_use_--;
} }
void Trace(Visitor*); PersistentNode* RefillFreeListAndAllocateNode(void* owner,
TraceCallback trace);
size_t NodesInUse() const;
void ClearAllUsedNodes();
private: private:
void EnsureNodeSlots();
template <typename PersistentBaseClass> template <typename PersistentBaseClass>
void ClearAllUsedNodes(); void ClearAllUsedNodes();
void RefillFreeList();
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;
...@@ -142,7 +147,12 @@ class V8_EXPORT PersistentRegion final : public PersistentRegionBase { ...@@ -142,7 +147,12 @@ class V8_EXPORT PersistentRegion final : public PersistentRegionBase {
V8_INLINE PersistentNode* AllocateNode(void* owner, TraceCallback trace) { V8_INLINE PersistentNode* AllocateNode(void* owner, TraceCallback trace) {
CPPGC_DCHECK(IsCreationThread()); CPPGC_DCHECK(IsCreationThread());
return PersistentRegionBase::AllocateNode(owner, trace); auto* node = TryAllocateNodeFromFreeList(owner, trace);
if (V8_LIKELY(node)) return node;
// Slow path allocation allows for checking thread correspondence.
CPPGC_CHECK(IsCreationThread());
return RefillFreeListAndAllocateNode(owner, trace);
} }
V8_INLINE void FreeNode(PersistentNode* node) { V8_INLINE void FreeNode(PersistentNode* node) {
...@@ -181,7 +191,10 @@ class V8_EXPORT CrossThreadPersistentRegion final ...@@ -181,7 +191,10 @@ class V8_EXPORT CrossThreadPersistentRegion final
V8_INLINE PersistentNode* AllocateNode(void* owner, TraceCallback trace) { V8_INLINE PersistentNode* AllocateNode(void* owner, TraceCallback trace) {
PersistentRegionLock::AssertLocked(); PersistentRegionLock::AssertLocked();
return PersistentRegionBase::AllocateNode(owner, trace); auto* node = TryAllocateNodeFromFreeList(owner, trace);
if (V8_LIKELY(node)) return node;
return RefillFreeListAndAllocateNode(owner, trace);
} }
V8_INLINE void FreeNode(PersistentNode* node) { V8_INLINE void FreeNode(PersistentNode* node) {
......
...@@ -63,10 +63,10 @@ size_t PersistentRegionBase::NodesInUse() const { ...@@ -63,10 +63,10 @@ size_t PersistentRegionBase::NodesInUse() const {
return nodes_in_use_; return nodes_in_use_;
} }
void PersistentRegionBase::EnsureNodeSlots() { void PersistentRegionBase::RefillFreeList() {
auto node_slots = std::make_unique<PersistentNodeSlots>(); auto node_slots = std::make_unique<PersistentNodeSlots>();
if (!node_slots.get()) { if (!node_slots.get()) {
oom_handler_("Oilpan: PersistentRegionBase::EnsureNodeSlots()"); oom_handler_("Oilpan: PersistentRegionBase::RefillFreeList()");
} }
nodes_.push_back(std::move(node_slots)); nodes_.push_back(std::move(node_slots));
for (auto& node : *nodes_.back()) { for (auto& node : *nodes_.back()) {
...@@ -75,6 +75,14 @@ void PersistentRegionBase::EnsureNodeSlots() { ...@@ -75,6 +75,14 @@ void PersistentRegionBase::EnsureNodeSlots() {
} }
} }
PersistentNode* PersistentRegionBase::RefillFreeListAndAllocateNode(
void* owner, TraceCallback trace) {
RefillFreeList();
auto* node = TryAllocateNodeFromFreeList(owner, trace);
CPPGC_DCHECK(node);
return node;
}
void PersistentRegionBase::Trace(Visitor* visitor) { void PersistentRegionBase::Trace(Visitor* visitor) {
free_list_head_ = nullptr; free_list_head_ = nullptr;
for (auto& slots : nodes_) { for (auto& slots : nodes_) {
......
...@@ -7,12 +7,15 @@ ...@@ -7,12 +7,15 @@
#include "include/cppgc/allocation.h" #include "include/cppgc/allocation.h"
#include "include/cppgc/cross-thread-persistent.h" #include "include/cppgc/cross-thread-persistent.h"
#include "include/cppgc/garbage-collected.h" #include "include/cppgc/garbage-collected.h"
#include "include/cppgc/internal/persistent-node.h"
#include "include/cppgc/internal/pointer-policies.h" #include "include/cppgc/internal/pointer-policies.h"
#include "include/cppgc/member.h" #include "include/cppgc/member.h"
#include "include/cppgc/persistent.h" #include "include/cppgc/persistent.h"
#include "include/cppgc/source-location.h" #include "include/cppgc/source-location.h"
#include "include/cppgc/type-traits.h" #include "include/cppgc/type-traits.h"
#include "src/base/logging.h" #include "src/base/logging.h"
#include "src/base/platform/platform.h"
#include "src/heap/cppgc/globals.h"
#include "src/heap/cppgc/heap.h" #include "src/heap/cppgc/heap.h"
#include "src/heap/cppgc/liveness-broker.h" #include "src/heap/cppgc/liveness-broker.h"
#include "src/heap/cppgc/visitor.h" #include "src/heap/cppgc/visitor.h"
...@@ -110,6 +113,7 @@ class RootVisitor final : public VisitorBase { ...@@ -110,6 +113,7 @@ class RootVisitor final : public VisitorBase {
}; };
class PersistentTest : public testing::TestWithHeap {}; class PersistentTest : public testing::TestWithHeap {};
class PersistentDeathTest : public testing::TestWithHeap {};
} // namespace } // namespace
...@@ -1042,5 +1046,39 @@ TEST_F(PersistentTest, ObjectReclaimedAfterClearedPersistent) { ...@@ -1042,5 +1046,39 @@ TEST_F(PersistentTest, ObjectReclaimedAfterClearedPersistent) {
EXPECT_FALSE(weak_finalized); EXPECT_FALSE(weak_finalized);
} }
namespace {
class PersistentAccessOnBackgroundThread : public v8::base::Thread {
public:
explicit PersistentAccessOnBackgroundThread(GCed* raw_gced)
: v8::base::Thread(v8::base::Thread::Options(
"PersistentAccessOnBackgroundThread", 2 * kMB)),
raw_gced_(raw_gced) {}
void Run() override {
EXPECT_DEATH_IF_SUPPORTED(
Persistent<GCed> gced(static_cast<GCed*>(raw_gced_)), "");
}
private:
void* raw_gced_;
};
} // namespace
TEST_F(PersistentDeathTest, CheckCreationThread) {
#ifdef DEBUG
// In DEBUG mode, every Persistent creation should check whether the handle
// is created on the right thread. In release mode, this check is only
// performed on slow path allocations.
Persistent<GCed> first_persistent_triggers_slow_path(
MakeGarbageCollected<GCed>(GetAllocationHandle()));
#endif // DEBUG
PersistentAccessOnBackgroundThread thread(
MakeGarbageCollected<GCed>(GetAllocationHandle()));
CHECK(thread.StartSynchronously());
thread.Join();
}
} // namespace internal } // namespace internal
} // namespace cppgc } // namespace cppgc
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