| // 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<GCed> { |
| 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 <typename Callback> |
| explicit Runner(Callback callback) |
| : Thread(v8::base::Thread::Options("CrossThreadPersistent Thread")), |
| callback_(callback) {} |
| |
| void Run() final { callback_(); } |
| |
| private: |
| std::function<void()> callback_; |
| }; |
| |
| } // namespace |
| |
| class CrossThreadPersistentTest : public testing::TestWithHeap {}; |
| |
| TEST_F(CrossThreadPersistentTest, RetainStronglyOnDifferentThread) { |
| subtle::CrossThreadPersistent<GCed> holder = |
| MakeGarbageCollected<GCed>(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<GCed> in = |
| MakeGarbageCollected<GCed>(GetAllocationHandle()); |
| // Set up |out| with an object that is always retained to ensure that the |
| // different thread indeed moves back an empty handle. |
| Persistent<GCed> out_holder = |
| MakeGarbageCollected<GCed>(GetAllocationHandle()); |
| subtle::WeakCrossThreadPersistent<GCed> out = *out_holder; |
| { |
| Persistent<GCed> 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<GCed> holder = |
| MakeGarbageCollected<GCed>(GetAllocationHandle()); |
| Runner runner([&obj = holder]() { obj.Clear(); }); |
| EXPECT_TRUE(holder); |
| runner.StartSynchronously(); |
| PreciseGC(); |
| runner.Join(); |
| EXPECT_FALSE(holder); |
| } |
| |
| } // namespace internal |
| } // namespace cppgc |