// 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/cross-thread-persistent.h" #include "include/cppgc/allocation.h" #include "src/base/platform/condition-variable.h" #include "src/base/platform/mutex.h" #include "src/base/platform/platform.h" #include "test/unittests/heap/cppgc/tests.h" #include "testing/gtest/include/gtest/gtest.h" namespace cppgc { namespace internal { namespace { struct GCed final : GarbageCollected { static size_t destructor_call_count; GCed() { destructor_call_count = 0; } ~GCed() { destructor_call_count++; } virtual void Trace(cppgc::Visitor*) const {} int a = 0; }; size_t GCed::destructor_call_count = 0; class Runner final : public v8::base::Thread { public: template explicit Runner(Callback callback) : Thread(v8::base::Thread::Options("CrossThreadPersistent Thread")), callback_(callback) {} void Run() final { callback_(); } private: std::function callback_; }; } // namespace class CrossThreadPersistentTest : public testing::TestWithHeap {}; TEST_F(CrossThreadPersistentTest, RetainStronglyOnDifferentThread) { subtle::CrossThreadPersistent holder = MakeGarbageCollected(GetAllocationHandle()); { Runner runner([obj = std::move(holder)]() {}); EXPECT_FALSE(holder); EXPECT_EQ(0u, GCed::destructor_call_count); PreciseGC(); EXPECT_EQ(0u, GCed::destructor_call_count); runner.StartSynchronously(); runner.Join(); } EXPECT_EQ(0u, GCed::destructor_call_count); PreciseGC(); EXPECT_EQ(1u, GCed::destructor_call_count); } TEST_F(CrossThreadPersistentTest, RetainWeaklyOnDifferentThread) { subtle::WeakCrossThreadPersistent in = MakeGarbageCollected(GetAllocationHandle()); // Set up |out| with an object that is always retained to ensure that the // different thread indeed moves back an empty handle. Persistent out_holder = MakeGarbageCollected(GetAllocationHandle()); subtle::WeakCrossThreadPersistent out = *out_holder; { Persistent temporary_holder = *in; Runner runner([obj = std::move(in), &out]() { out = std::move(obj); }); EXPECT_FALSE(in); EXPECT_TRUE(out); EXPECT_EQ(0u, GCed::destructor_call_count); PreciseGC(); EXPECT_EQ(0u, GCed::destructor_call_count); temporary_holder.Clear(); PreciseGC(); EXPECT_EQ(1u, GCed::destructor_call_count); runner.StartSynchronously(); runner.Join(); } EXPECT_FALSE(out); } TEST_F(CrossThreadPersistentTest, DestroyRacingWithGC) { // Destroy a handle on a different thread while at the same time invoking a // garbage collection on the original thread. subtle::CrossThreadPersistent holder = MakeGarbageCollected(GetAllocationHandle()); Runner runner([&obj = holder]() { obj.Clear(); }); EXPECT_TRUE(holder); runner.StartSynchronously(); PreciseGC(); runner.Join(); EXPECT_FALSE(holder); } } // namespace internal } // namespace cppgc