4569ffae0b
Adds a cross-thread reference for strongly and weakly retaining objects on a thread other than the thread that owns the object. The intended use of the reference is by setting it up on the originating thread, holding the object alive from another thread, and ultimately accessing the object again on the originating thread. The reference has known caveats: - It's unsafe to use when the heap may terminate; - It's unsafe to transitively reach through the graph because of compaction; Change-Id: I84fbdde69a099eb54af5b93c34e2169915b17e64 Bug: chromium:1056170 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2436449 Commit-Queue: Michael Lippautz <mlippautz@chromium.org> Reviewed-by: Omer Katz <omerkatz@chromium.org> Reviewed-by: Anton Bikineev <bikineev@chromium.org> Cr-Commit-Position: refs/heads/master@{#70428}
102 lines
3.1 KiB
C++
102 lines
3.1 KiB
C++
// 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
|