[K/N] Extract common part of gc runtime tests (1/1)

Move all the tests in a header file
    and make them instantiatable with different tracing GC implementations.
This commit is contained in:
Aleksei.Glushko
2023-11-10 17:57:04 +01:00
committed by Space Team
parent cf9bd44cf7
commit 8180400a36
6 changed files with 202 additions and 2511 deletions
+3 -3
View File
@@ -397,15 +397,15 @@ bitcode {
testSupportModules.addAll("main", "mm", "common_alloc", "common_gc", "common_gcScheduler", "manual_gcScheduler", "concurrent_ms_gc_custom", "objc") testSupportModules.addAll("main", "mm", "common_alloc", "common_gc", "common_gcScheduler", "manual_gcScheduler", "concurrent_ms_gc_custom", "objc")
} }
testsGroup("experimentalMM_mimalloc_runtime_tests") { testsGroup("experimentalMM_stms_mimalloc_runtime_tests") {
testedModules.addAll("main", "mm", "common_alloc", "common_gc", "common_gcScheduler", "manual_gcScheduler", "same_thread_ms_gc", "mimalloc", "mimalloc_alloc", "legacy_alloc", "objc") testedModules.addAll("main", "mm", "common_alloc", "common_gc", "common_gcScheduler", "manual_gcScheduler", "same_thread_ms_gc", "mimalloc", "mimalloc_alloc", "legacy_alloc", "objc")
} }
testsGroup("experimentalMM_std_alloc_runtime_tests") { testsGroup("experimentalMM_stms_std_alloc_runtime_tests") {
testedModules.addAll("main", "mm", "common_alloc", "common_gc", "common_gcScheduler", "manual_gcScheduler", "same_thread_ms_gc", "std_alloc", "legacy_alloc", "objc") testedModules.addAll("main", "mm", "common_alloc", "common_gc", "common_gcScheduler", "manual_gcScheduler", "same_thread_ms_gc", "std_alloc", "legacy_alloc", "objc")
} }
testsGroup("experimentalMM_custom_alloc_runtime_tests") { testsGroup("experimentalMM_stms_custom_alloc_runtime_tests") {
testedModules.addAll("mm", "same_thread_ms_gc_custom") testedModules.addAll("mm", "same_thread_ms_gc_custom")
testSupportModules.addAll("main", "common_alloc", "common_gc", "common_gcScheduler", "manual_gcScheduler", "custom_alloc", "objc") testSupportModules.addAll("main", "common_alloc", "common_gc", "common_gcScheduler", "manual_gcScheduler", "custom_alloc", "objc")
} }
File diff suppressed because it is too large Load Diff
@@ -1,9 +1,9 @@
/* /*
* Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license * Copyright 2010-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file. * that can be found in the LICENSE file.
*/ */
#include "ParallelMarkConcurrentSweep.hpp" #pragma once
#include <condition_variable> #include <condition_variable>
#include <future> #include <future>
@@ -17,7 +17,6 @@
#include "AllocatorTestSupport.hpp" #include "AllocatorTestSupport.hpp"
#include "ExtraObjectData.hpp" #include "ExtraObjectData.hpp"
#include "FinalizerHooksTestSupport.hpp" #include "FinalizerHooksTestSupport.hpp"
#include "GCImpl.hpp"
#include "GlobalData.hpp" #include "GlobalData.hpp"
#include "ObjectOps.hpp" #include "ObjectOps.hpp"
#include "ObjectTestSupport.hpp" #include "ObjectTestSupport.hpp"
@@ -30,10 +29,6 @@
using namespace kotlin; using namespace kotlin;
// These tests can only work if `GC` is `ParallelMarkConcurrentSweep`.
namespace {
struct Payload { struct Payload {
ObjHeader* field1; ObjHeader* field1;
ObjHeader* field2; ObjHeader* field2;
@@ -195,49 +190,23 @@ test_support::RegularWeakReferenceImpl& InstallWeakReference(mm::ThreadData& thr
return weakReference; return weakReference;
} }
struct ParallelismOptions {
std::size_t maxParallelism;
bool cooperativeMutators;
std::size_t auxGCThreads;
};
class ParallelMarkConcurrentSweepTest : public testing::TestWithParam<ParallelismOptions> { template <typename FixtureImpl>
class TracingGCTest : public testing::Test {
public: public:
ParallelMarkConcurrentSweepTest() {
if (supportedConfiguration()) {
mm::GlobalData::Instance().gc().impl().gc().reconfigure(GetParam().maxParallelism,
GetParam().cooperativeMutators,
GetParam().auxGCThreads);
}
}
void SetUp() override { void SetUp() override {
if (!supportedConfiguration()) { impl_.SetUp();
GTEST_SKIP() << "Unsupported parallelism configuration";
}
}
~ParallelMarkConcurrentSweepTest() {
mm::GlobalsRegistry::Instance().ClearForTests();
mm::SpecialRefRegistry::instance().clearForTests();
mm::GlobalData::Instance().gc().ClearForTests();
mm::GlobalData::Instance().allocator().clearForTests();
} }
testing::MockFunction<void(ObjHeader*)>& finalizerHook() { return finalizerHooks_.finalizerHook(); } testing::MockFunction<void(ObjHeader*)>& finalizerHook() { return finalizerHooks_.finalizerHook(); }
private: private:
bool supportedConfiguration() const { FixtureImpl impl_{};
return !compiler::gcMarkSingleThreaded() || (!GetParam().cooperativeMutators && GetParam().auxGCThreads == 0);
}
FinalizerHooksTestSupport finalizerHooks_; FinalizerHooksTestSupport finalizerHooks_;
}; };
} // namespace TYPED_TEST_SUITE_P(TracingGCTest);
TEST_P(ParallelMarkConcurrentSweepTest, RootSet) { TYPED_TEST_P(TracingGCTest, RootSet) {
RunInNewThread([](mm::ThreadData& threadData) { RunInNewThread([](mm::ThreadData& threadData) {
GlobalObjectHolder global1{threadData}; GlobalObjectHolder global1{threadData};
GlobalObjectArrayHolder global2{threadData}; GlobalObjectArrayHolder global2{threadData};
@@ -272,7 +241,7 @@ TEST_P(ParallelMarkConcurrentSweepTest, RootSet) {
}); });
} }
TEST_P(ParallelMarkConcurrentSweepTest, InterconnectedRootSet) { TYPED_TEST_P(TracingGCTest, InterconnectedRootSet) {
RunInNewThread([](mm::ThreadData& threadData) { RunInNewThread([](mm::ThreadData& threadData) {
GlobalObjectHolder global1{threadData}; GlobalObjectHolder global1{threadData};
GlobalObjectArrayHolder global2{threadData}; GlobalObjectArrayHolder global2{threadData};
@@ -318,7 +287,7 @@ TEST_P(ParallelMarkConcurrentSweepTest, InterconnectedRootSet) {
}); });
} }
TEST_P(ParallelMarkConcurrentSweepTest, FreeObjects) { TYPED_TEST_P(TracingGCTest, FreeObjects) {
RunInNewThread([](mm::ThreadData& threadData) { RunInNewThread([](mm::ThreadData& threadData) {
auto& object1 = AllocateObject(threadData); auto& object1 = AllocateObject(threadData);
auto& object2 = AllocateObject(threadData); auto& object2 = AllocateObject(threadData);
@@ -333,7 +302,7 @@ TEST_P(ParallelMarkConcurrentSweepTest, FreeObjects) {
}); });
} }
TEST_P(ParallelMarkConcurrentSweepTest, FreeObjectsWithFinalizers) { TYPED_TEST_P(TracingGCTest, FreeObjectsWithFinalizers) {
RunInNewThread([this](mm::ThreadData& threadData) { RunInNewThread([this](mm::ThreadData& threadData) {
auto& object1 = AllocateObjectWithFinalizer(threadData); auto& object1 = AllocateObjectWithFinalizer(threadData);
auto& object2 = AllocateObjectWithFinalizer(threadData); auto& object2 = AllocateObjectWithFinalizer(threadData);
@@ -342,15 +311,15 @@ TEST_P(ParallelMarkConcurrentSweepTest, FreeObjectsWithFinalizers) {
ASSERT_THAT(gc::isMarked(object1.header()), false); ASSERT_THAT(gc::isMarked(object1.header()), false);
ASSERT_THAT(gc::isMarked(object2.header()), false); ASSERT_THAT(gc::isMarked(object2.header()), false);
EXPECT_CALL(finalizerHook(), Call(object1.header())); EXPECT_CALL(this->finalizerHook(), Call(object1.header()));
EXPECT_CALL(finalizerHook(), Call(object2.header())); EXPECT_CALL(this->finalizerHook(), Call(object2.header()));
mm::GlobalData::Instance().gcScheduler().scheduleAndWaitFinalized(); mm::GlobalData::Instance().gcScheduler().scheduleAndWaitFinalized();
EXPECT_THAT(Alive(threadData), testing::UnorderedElementsAre()); EXPECT_THAT(Alive(threadData), testing::UnorderedElementsAre());
}); });
} }
TEST_P(ParallelMarkConcurrentSweepTest, FreeObjectWithFreeWeak) { TYPED_TEST_P(TracingGCTest, FreeObjectWithFreeWeak) {
RunInNewThread([this](mm::ThreadData& threadData) { RunInNewThread([this](mm::ThreadData& threadData) {
auto& object1 = AllocateObject(threadData); auto& object1 = AllocateObject(threadData);
auto& weak1 = ([&threadData, &object1]() -> test_support::RegularWeakReferenceImpl& { auto& weak1 = ([&threadData, &object1]() -> test_support::RegularWeakReferenceImpl& {
@@ -363,14 +332,14 @@ TEST_P(ParallelMarkConcurrentSweepTest, FreeObjectWithFreeWeak) {
ASSERT_THAT(gc::isMarked(weak1.header()), false); ASSERT_THAT(gc::isMarked(weak1.header()), false);
ASSERT_THAT(weak1.get(), object1.header()); ASSERT_THAT(weak1.get(), object1.header());
EXPECT_CALL(finalizerHook(), Call(weak1.header())); EXPECT_CALL(this->finalizerHook(), Call(weak1.header()));
mm::GlobalData::Instance().gcScheduler().scheduleAndWaitFinalized(); mm::GlobalData::Instance().gcScheduler().scheduleAndWaitFinalized();
EXPECT_THAT(Alive(threadData), testing::UnorderedElementsAre()); EXPECT_THAT(Alive(threadData), testing::UnorderedElementsAre());
}); });
} }
TEST_P(ParallelMarkConcurrentSweepTest, FreeObjectWithHoldedWeak) { TYPED_TEST_P(TracingGCTest, FreeObjectWithHoldedWeak) {
RunInNewThread([](mm::ThreadData& threadData) { RunInNewThread([](mm::ThreadData& threadData) {
auto& object1 = AllocateObject(threadData); auto& object1 = AllocateObject(threadData);
StackObjectHolder stack{threadData}; StackObjectHolder stack{threadData};
@@ -389,7 +358,7 @@ TEST_P(ParallelMarkConcurrentSweepTest, FreeObjectWithHoldedWeak) {
}); });
} }
TEST_P(ParallelMarkConcurrentSweepTest, ObjectReferencedFromRootSet) { TYPED_TEST_P(TracingGCTest, ObjectReferencedFromRootSet) {
RunInNewThread([](mm::ThreadData& threadData) { RunInNewThread([](mm::ThreadData& threadData) {
GlobalObjectHolder global{threadData}; GlobalObjectHolder global{threadData};
StackObjectHolder stack{threadData}; StackObjectHolder stack{threadData};
@@ -429,7 +398,7 @@ TEST_P(ParallelMarkConcurrentSweepTest, ObjectReferencedFromRootSet) {
}); });
} }
TEST_P(ParallelMarkConcurrentSweepTest, ObjectsWithCycles) { TYPED_TEST_P(TracingGCTest, ObjectsWithCycles) {
RunInNewThread([](mm::ThreadData& threadData) { RunInNewThread([](mm::ThreadData& threadData) {
GlobalObjectHolder global{threadData}; GlobalObjectHolder global{threadData};
StackObjectHolder stack{threadData}; StackObjectHolder stack{threadData};
@@ -478,7 +447,7 @@ TEST_P(ParallelMarkConcurrentSweepTest, ObjectsWithCycles) {
}); });
} }
TEST_P(ParallelMarkConcurrentSweepTest, ObjectsWithCyclesAndFinalizers) { TYPED_TEST_P(TracingGCTest, ObjectsWithCyclesAndFinalizers) {
RunInNewThread([this](mm::ThreadData& threadData) { RunInNewThread([this](mm::ThreadData& threadData) {
GlobalObjectHolder global{threadData}; GlobalObjectHolder global{threadData};
StackObjectHolder stack{threadData}; StackObjectHolder stack{threadData};
@@ -512,8 +481,8 @@ TEST_P(ParallelMarkConcurrentSweepTest, ObjectsWithCyclesAndFinalizers) {
ASSERT_THAT(gc::isMarked(object5.header()), false); ASSERT_THAT(gc::isMarked(object5.header()), false);
ASSERT_THAT(gc::isMarked(object6.header()), false); ASSERT_THAT(gc::isMarked(object6.header()), false);
EXPECT_CALL(finalizerHook(), Call(object5.header())); EXPECT_CALL(this->finalizerHook(), Call(object5.header()));
EXPECT_CALL(finalizerHook(), Call(object6.header())); EXPECT_CALL(this->finalizerHook(), Call(object6.header()));
mm::GlobalData::Instance().gcScheduler().scheduleAndWaitFinalized(); mm::GlobalData::Instance().gcScheduler().scheduleAndWaitFinalized();
EXPECT_THAT( EXPECT_THAT(
@@ -529,7 +498,7 @@ TEST_P(ParallelMarkConcurrentSweepTest, ObjectsWithCyclesAndFinalizers) {
}); });
} }
TEST_P(ParallelMarkConcurrentSweepTest, ObjectsWithCyclesIntoRootSet) { TYPED_TEST_P(TracingGCTest, ObjectsWithCyclesIntoRootSet) {
RunInNewThread([](mm::ThreadData& threadData) { RunInNewThread([](mm::ThreadData& threadData) {
GlobalObjectHolder global{threadData}; GlobalObjectHolder global{threadData};
StackObjectHolder stack{threadData}; StackObjectHolder stack{threadData};
@@ -557,7 +526,7 @@ TEST_P(ParallelMarkConcurrentSweepTest, ObjectsWithCyclesIntoRootSet) {
}); });
} }
TEST_P(ParallelMarkConcurrentSweepTest, RunGCTwice) { TYPED_TEST_P(TracingGCTest, RunGCTwice) {
RunInNewThread([](mm::ThreadData& threadData) { RunInNewThread([](mm::ThreadData& threadData) {
GlobalObjectHolder global{threadData}; GlobalObjectHolder global{threadData};
StackObjectHolder stack{threadData}; StackObjectHolder stack{threadData};
@@ -607,7 +576,7 @@ TEST_P(ParallelMarkConcurrentSweepTest, RunGCTwice) {
}); });
} }
TEST_P(ParallelMarkConcurrentSweepTest, PermanentObjects) { TYPED_TEST_P(TracingGCTest, PermanentObjects) {
RunInNewThread([](mm::ThreadData& threadData) { RunInNewThread([](mm::ThreadData& threadData) {
GlobalPermanentObjectHolder global1{threadData}; GlobalPermanentObjectHolder global1{threadData};
GlobalObjectHolder global2{threadData}; GlobalObjectHolder global2{threadData};
@@ -629,7 +598,7 @@ TEST_P(ParallelMarkConcurrentSweepTest, PermanentObjects) {
}); });
} }
TEST_P(ParallelMarkConcurrentSweepTest, SameObjectInRootSet) { TYPED_TEST_P(TracingGCTest, SameObjectInRootSet) {
RunInNewThread([](mm::ThreadData& threadData) { RunInNewThread([](mm::ThreadData& threadData) {
GlobalObjectHolder global{threadData}; GlobalObjectHolder global{threadData};
StackObjectHolder stack(*global); StackObjectHolder stack(*global);
@@ -715,7 +684,7 @@ private:
} // namespace } // namespace
TEST_P(ParallelMarkConcurrentSweepTest, MultipleMutatorsCollect) { TYPED_TEST_P(TracingGCTest, MultipleMutatorsCollect) {
std::vector<Mutator> mutators(kDefaultThreadCount); std::vector<Mutator> mutators(kDefaultThreadCount);
std::vector<ObjHeader*> globals(kDefaultThreadCount); std::vector<ObjHeader*> globals(kDefaultThreadCount);
std::vector<ObjHeader*> locals(kDefaultThreadCount); std::vector<ObjHeader*> locals(kDefaultThreadCount);
@@ -772,7 +741,7 @@ TEST_P(ParallelMarkConcurrentSweepTest, MultipleMutatorsCollect) {
} }
} }
TEST_P(ParallelMarkConcurrentSweepTest, MultipleMutatorsAllCollect) { TYPED_TEST_P(TracingGCTest, MultipleMutatorsAllCollect) {
std::vector<Mutator> mutators(kDefaultThreadCount); std::vector<Mutator> mutators(kDefaultThreadCount);
std::vector<ObjHeader*> globals(kDefaultThreadCount); std::vector<ObjHeader*> globals(kDefaultThreadCount);
std::vector<ObjHeader*> locals(kDefaultThreadCount); std::vector<ObjHeader*> locals(kDefaultThreadCount);
@@ -801,7 +770,7 @@ TEST_P(ParallelMarkConcurrentSweepTest, MultipleMutatorsAllCollect) {
gcFutures.emplace_back(mutator.Execute([](mm::ThreadData& threadData, Mutator& mutator) { gcFutures.emplace_back(mutator.Execute([](mm::ThreadData& threadData, Mutator& mutator) {
mm::GlobalData::Instance().gcScheduler().scheduleAndWaitFinalized(); mm::GlobalData::Instance().gcScheduler().scheduleAndWaitFinalized();
// If GC starts before all thread executed line above, two gc will be run // If GC starts before all thread executed line above, two gc will be run
// So we temporary switch threads to native state and then return them back after all GC runs are done // So we temporarily switch threads to native state and then return them back after all GC runs are done
SwitchThreadState(mm::GetMemoryState(), kotlin::ThreadState::kNative); SwitchThreadState(mm::GetMemoryState(), kotlin::ThreadState::kNative);
})); }));
} }
@@ -812,10 +781,10 @@ TEST_P(ParallelMarkConcurrentSweepTest, MultipleMutatorsAllCollect) {
for (int i = 0; i < kDefaultThreadCount; ++i) { for (int i = 0; i < kDefaultThreadCount; ++i) {
mutators[i] mutators[i]
.Execute([](mm::ThreadData& threadData, Mutator& mutator) { .Execute([](mm::ThreadData& threadData, Mutator& mutator) {
SwitchThreadState(mm::GetMemoryState(), kotlin::ThreadState::kRunnable); SwitchThreadState(mm::GetMemoryState(), kotlin::ThreadState::kRunnable);
}) })
.wait(); .wait();
} }
std::vector<ObjHeader*> expectedAlive; std::vector<ObjHeader*> expectedAlive;
@@ -834,7 +803,7 @@ TEST_P(ParallelMarkConcurrentSweepTest, MultipleMutatorsAllCollect) {
} }
} }
TEST_P(ParallelMarkConcurrentSweepTest, MultipleMutatorsAddToRootSetAfterCollectionRequested) { TYPED_TEST_P(TracingGCTest, MultipleMutatorsAddToRootSetAfterCollectionRequested) {
std::vector<Mutator> mutators(kDefaultThreadCount); std::vector<Mutator> mutators(kDefaultThreadCount);
std::vector<ObjHeader*> globals(kDefaultThreadCount); std::vector<ObjHeader*> globals(kDefaultThreadCount);
std::vector<ObjHeader*> locals(kDefaultThreadCount); std::vector<ObjHeader*> locals(kDefaultThreadCount);
@@ -905,7 +874,7 @@ TEST_P(ParallelMarkConcurrentSweepTest, MultipleMutatorsAddToRootSetAfterCollect
} }
} }
TEST_P(ParallelMarkConcurrentSweepTest, CrossThreadReference) { TYPED_TEST_P(TracingGCTest, CrossThreadReference) {
std::vector<Mutator> mutators(kDefaultThreadCount); std::vector<Mutator> mutators(kDefaultThreadCount);
std::vector<ObjHeader*> globals(kDefaultThreadCount); std::vector<ObjHeader*> globals(kDefaultThreadCount);
std::vector<ObjHeader*> locals(kDefaultThreadCount); std::vector<ObjHeader*> locals(kDefaultThreadCount);
@@ -972,107 +941,7 @@ TEST_P(ParallelMarkConcurrentSweepTest, CrossThreadReference) {
} }
} }
TEST_P(ParallelMarkConcurrentSweepTest, MultipleMutatorsWeaks) { TYPED_TEST_P(TracingGCTest, NewThreadsWhileRequestingCollection) {
std::vector<Mutator> mutators(kDefaultThreadCount);
ObjHeader* globalRoot = nullptr;
test_support::RegularWeakReferenceImpl* weak = nullptr;
mutators[0]
.Execute([&weak, &globalRoot](mm::ThreadData& threadData, Mutator& mutator) {
auto& global = mutator.AddGlobalRoot();
auto& object = AllocateObject(threadData);
auto& objectWeak = ([&threadData, &object]() -> test_support::RegularWeakReferenceImpl& {
ObjHolder holder;
return InstallWeakReference(threadData, object.header(), holder.slot());
})();
global->field1 = objectWeak.header();
weak = &objectWeak;
globalRoot = global.header();
})
.wait();
// Make sure all mutators are initialized.
for (int i = 1; i < kDefaultThreadCount; ++i) {
mutators[i].Execute([](mm::ThreadData& threadData, Mutator& mutator) {}).wait();
}
std::vector<std::future<void>> gcFutures;
auto epoch = mm::GlobalData::Instance().gc().Schedule();
std::atomic<bool> gcDone = false;
// Spin until thread suspension is requested.
while (!mm::IsThreadSuspensionRequested()) {
}
for (auto& mutator : mutators) {
gcFutures.emplace_back(mutator.Execute([&](mm::ThreadData& threadData, Mutator& mutator) noexcept {
while (!gcDone.load(std::memory_order_relaxed)) {
mm::safePoint(threadData);
EXPECT_THAT(weak->get(), nullptr);
}
}));
}
mm::GlobalData::Instance().gc().WaitFinalizers(epoch);
gcDone.store(true, std::memory_order_relaxed);
for (auto& future : gcFutures) {
future.wait();
}
for (auto& mutator : mutators) {
EXPECT_THAT(mutator.Alive(), testing::UnorderedElementsAre(globalRoot, weak->header()));
}
}
TEST_P(ParallelMarkConcurrentSweepTest, MultipleMutatorsWeakNewObj) {
std::vector<Mutator> mutators(kDefaultThreadCount);
// Make sure all mutators are initialized.
for (int i = 0; i < kDefaultThreadCount; ++i) {
mutators[i].Execute([](mm::ThreadData& threadData, Mutator& mutator) {}).wait();
}
std::vector<std::future<void>> gcFutures;
auto epoch = mm::GlobalData::Instance().gc().Schedule();
std::atomic<bool> gcDone = false;
// Spin until thread suspension is requested.
while (!mm::IsThreadSuspensionRequested()) {
}
for (auto& mutator : mutators) {
gcFutures.emplace_back(mutator.Execute([&](mm::ThreadData& threadData, Mutator& mutator) noexcept {
mm::safePoint(threadData);
auto& object = AllocateObject(threadData);
auto& objectWeak = ([&threadData, &object]() -> test_support::RegularWeakReferenceImpl& {
ObjHolder holder;
return InstallWeakReference(threadData, object.header(), holder.slot());
})();
EXPECT_NE(objectWeak.get(), nullptr);
auto& extraObj = *mm::ExtraObjectData::Get(object.header());
extraObj.ClearRegularWeakReferenceImpl();
extraObj.Uninstall();
alloc::destroyExtraObjectData(extraObj);
while (!gcDone.load(std::memory_order_relaxed)) {
mm::safePoint(threadData);
}
}));
}
mm::GlobalData::Instance().gc().WaitFinalizers(epoch);
gcDone.store(true, std::memory_order_relaxed);
for (auto& future : gcFutures) {
future.wait();
}
}
TEST_P(ParallelMarkConcurrentSweepTest, NewThreadsWhileRequestingCollection) {
std::vector<Mutator> mutators(kDefaultThreadCount); std::vector<Mutator> mutators(kDefaultThreadCount);
std::vector<ObjHeader*> globals(2 * kDefaultThreadCount); std::vector<ObjHeader*> globals(2 * kDefaultThreadCount);
std::vector<ObjHeader*> locals(2 * kDefaultThreadCount); std::vector<ObjHeader*> locals(2 * kDefaultThreadCount);
@@ -1156,11 +1025,11 @@ TEST_P(ParallelMarkConcurrentSweepTest, NewThreadsWhileRequestingCollection) {
// Force mutators to publish their internal heaps // Force mutators to publish their internal heaps
// Really only needed for legacy allocators. // Really only needed for legacy allocators.
std::vector<std::future<void>> publishFutures; std::vector<std::future<void>> publishFutures;
for (auto& mutator: mutators) { for (auto& mutator : mutators) {
publishFutures.emplace_back( publishFutures.emplace_back(
mutator.Execute([](mm::ThreadData& threadData, Mutator& mutator) { threadData.allocator().prepareForGC(); })); mutator.Execute([](mm::ThreadData& threadData, Mutator& mutator) { threadData.allocator().prepareForGC(); }));
} }
for (auto& mutator: newMutators) { for (auto& mutator : newMutators) {
publishFutures.emplace_back( publishFutures.emplace_back(
mutator.Execute([](mm::ThreadData& threadData, Mutator& mutator) { threadData.allocator().prepareForGC(); })); mutator.Execute([](mm::ThreadData& threadData, Mutator& mutator) { threadData.allocator().prepareForGC(); }));
} }
@@ -1172,12 +1041,12 @@ TEST_P(ParallelMarkConcurrentSweepTest, NewThreadsWhileRequestingCollection) {
EXPECT_THAT(mutators[0].Alive(), testing::UnorderedElementsAreArray(expectedAlive)); EXPECT_THAT(mutators[0].Alive(), testing::UnorderedElementsAreArray(expectedAlive));
} }
TEST_P(ParallelMarkConcurrentSweepTest, FreeObjectWithFreeWeakReversedOrder) { TYPED_TEST_P(TracingGCTest, FreeObjectWithFreeWeakReversedOrder) {
std::vector<Mutator> mutators(2); std::vector<Mutator> mutators(2);
std::atomic<test_support::Object<Payload>*> object1 = nullptr; std::atomic<test_support::Object<Payload>*> object1 = nullptr;
std::atomic<test_support::RegularWeakReferenceImpl*> weak = nullptr; std::atomic<test_support::RegularWeakReferenceImpl*> weak = nullptr;
std::atomic<bool> done = false; std::atomic<bool> done = false;
auto f0 = mutators[0].Execute([&](mm::ThreadData& threadData, Mutator &) { auto f0 = mutators[0].Execute([&](mm::ThreadData& threadData, Mutator&) {
GlobalObjectHolder global1{threadData}; GlobalObjectHolder global1{threadData};
auto& object1_local = AllocateObject(threadData); auto& object1_local = AllocateObject(threadData);
object1 = &object1_local; object1 = &object1_local;
@@ -1194,15 +1063,16 @@ TEST_P(ParallelMarkConcurrentSweepTest, FreeObjectWithFreeWeakReversedOrder) {
global1->field1 = nullptr; global1->field1 = nullptr;
EXPECT_CALL(finalizerHook(), Call(weak.load()->header())); EXPECT_CALL(this->finalizerHook(), Call(weak.load()->header()));
mm::GlobalData::Instance().gcScheduler().scheduleAndWaitFinalized(); mm::GlobalData::Instance().gcScheduler().scheduleAndWaitFinalized();
EXPECT_THAT(Alive(threadData), testing::UnorderedElementsAre(global1.header())); EXPECT_THAT(Alive(threadData), testing::UnorderedElementsAre(global1.header()));
done = true; done = true;
}); });
auto f1 = mutators[1].Execute([&](mm::ThreadData& threadData, Mutator &) { auto f1 = mutators[1].Execute([&](mm::ThreadData& threadData, Mutator&) {
while (object1.load() == nullptr) {} while (object1.load() == nullptr) {
}
ObjHolder holder; ObjHolder holder;
auto& weak_local = InstallWeakReference(threadData, object1.load()->header(), holder.slot()); auto& weak_local = InstallWeakReference(threadData, object1.load()->header(), holder.slot());
weak = &weak_local; weak = &weak_local;
@@ -1214,25 +1084,135 @@ TEST_P(ParallelMarkConcurrentSweepTest, FreeObjectWithFreeWeakReversedOrder) {
f1.wait(); f1.wait();
} }
INSTANTIATE_TEST_SUITE_P(, #define TRACING_GC_TEST_LIST \
ParallelMarkConcurrentSweepTest, RootSet, \
testing::Values( InterconnectedRootSet, \
ParallelismOptions{kDefaultThreadCount * 3, false, 0}, FreeObjects, \
ParallelismOptions{kDefaultThreadCount * 3, true, 0} FreeObjectsWithFinalizers, \
#if !__has_feature(thread_sanitizer) // TODO: Fix auxilary threads with tsan. FreeObjectWithFreeWeak, \
, ParallelismOptions{kDefaultThreadCount * 3, false, kDefaultThreadCount}, FreeObjectWithHoldedWeak, \
ParallelismOptions{kDefaultThreadCount * 3, true, kDefaultThreadCount}, ObjectReferencedFromRootSet, \
ObjectsWithCycles, \
ObjectsWithCyclesAndFinalizers, \
ObjectsWithCyclesIntoRootSet, \
RunGCTwice, \
PermanentObjects, \
SameObjectInRootSet, \
MultipleMutatorsCollect, \
MultipleMutatorsAllCollect, \
MultipleMutatorsAddToRootSetAfterCollectionRequested, \
CrossThreadReference, \
NewThreadsWhileRequestingCollection, \
FreeObjectWithFreeWeakReversedOrder
ParallelismOptions{kDefaultThreadCount / 2, true, kDefaultThreadCount}, template <typename FixtureImpl>
ParallelismOptions{kDefaultThreadCount / 2 * 3, true, kDefaultThreadCount} class STWMarkGCTest : public TracingGCTest<FixtureImpl> {};
#endif
),
[] (const testing::TestParamInfo<ParallelismOptions>& paramInfo) {
using namespace std::string_literals;
auto base = "Mark"s;
auto parallelism = std::to_string(paramInfo.param.maxParallelism) + "Parallel";
auto withMutators = paramInfo.param.cooperativeMutators ? "WithMutators" : "";
auto withAux = paramInfo.param.auxGCThreads > 0 ? "WithGCThreads" : "";
return base + parallelism + withMutators + withAux;
});
TYPED_TEST_SUITE_P(STWMarkGCTest);
TYPED_TEST_P(STWMarkGCTest, MultipleMutatorsWeaks) {
std::vector<Mutator> mutators(kDefaultThreadCount);
ObjHeader* globalRoot = nullptr;
test_support::RegularWeakReferenceImpl* weak = nullptr;
mutators[0]
.Execute([&weak, &globalRoot](mm::ThreadData& threadData, Mutator& mutator) {
auto& global = mutator.AddGlobalRoot();
auto& object = AllocateObject(threadData);
auto& objectWeak = ([&threadData, &object]() -> test_support::RegularWeakReferenceImpl& {
ObjHolder holder;
return InstallWeakReference(threadData, object.header(), holder.slot());
})();
global->field1 = objectWeak.header();
weak = &objectWeak;
globalRoot = global.header();
})
.wait();
// Make sure all mutators are initialized.
for (int i = 1; i < kDefaultThreadCount; ++i) {
mutators[i].Execute([](mm::ThreadData& threadData, Mutator& mutator) {}).wait();
}
std::vector<std::future<void>> gcFutures;
auto epoch = mm::GlobalData::Instance().gc().Schedule();
std::atomic<bool> gcDone = false;
// Spin until thread suspension is requested.
while (!mm::IsThreadSuspensionRequested()) {
}
for (auto& mutator : mutators) {
gcFutures.emplace_back(mutator.Execute([&](mm::ThreadData& threadData, Mutator& mutator) noexcept {
while (!gcDone.load(std::memory_order_relaxed)) {
mm::safePoint(threadData);
EXPECT_THAT(weak->get(), nullptr);
}
}));
}
mm::GlobalData::Instance().gc().WaitFinalizers(epoch);
gcDone.store(true, std::memory_order_relaxed);
for (auto& future : gcFutures) {
future.wait();
}
for (auto& mutator : mutators) {
EXPECT_THAT(mutator.Alive(), testing::UnorderedElementsAre(globalRoot, weak->header()));
}
}
TYPED_TEST_P(STWMarkGCTest, MultipleMutatorsWeakNewObj) {
std::vector<Mutator> mutators(kDefaultThreadCount);
// Make sure all mutators are initialized.
for (int i = 0; i < kDefaultThreadCount; ++i) {
mutators[i].Execute([](mm::ThreadData& threadData, Mutator& mutator) {}).wait();
}
std::vector<std::future<void>> gcFutures;
auto epoch = mm::GlobalData::Instance().gc().Schedule();
std::atomic<bool> gcDone = false;
// Spin until thread suspension is requested.
while (!mm::IsThreadSuspensionRequested()) {
}
for (auto& mutator : mutators) {
gcFutures.emplace_back(mutator.Execute([&](mm::ThreadData& threadData, Mutator& mutator) noexcept {
mm::safePoint(threadData);
auto& object = AllocateObject(threadData);
auto& objectWeak = ([&threadData, &object]() -> test_support::RegularWeakReferenceImpl& {
ObjHolder holder;
return InstallWeakReference(threadData, object.header(), holder.slot());
})();
EXPECT_NE(objectWeak.get(), nullptr);
auto& extraObj = *mm::ExtraObjectData::Get(object.header());
extraObj.ClearRegularWeakReferenceImpl();
extraObj.Uninstall();
alloc::destroyExtraObjectData(extraObj);
while (!gcDone.load(std::memory_order_relaxed)) {
mm::safePoint(threadData);
}
}));
}
mm::GlobalData::Instance().gc().WaitFinalizers(epoch);
gcDone.store(true, std::memory_order_relaxed);
for (auto& future : gcFutures) {
future.wait();
}
}
#define STW_MARK_GC_TEST_LIST \
MultipleMutatorsWeaks, \
MultipleMutatorsWeakNewObj
// expand lists before stringization in REGISTER_TYPED_TEST_SUITE_P
#define REGISTER_TYPED_TEST_SUITE_WITH_LISTS(SuiteName, ...) REGISTER_TYPED_TEST_SUITE_P(SuiteName, __VA_ARGS__)
File diff suppressed because it is too large Load Diff
@@ -3,6 +3,8 @@
* that can be found in the LICENSE file. * that can be found in the LICENSE file.
*/ */
#pragma once
#include <array> #include <array>
#include <type_traits> #include <type_traits>
#include <vector> #include <vector>
@@ -3,6 +3,8 @@
* that can be found in the LICENSE file. * that can be found in the LICENSE file.
*/ */
#pragma once
#include "../../main/cpp/TestSupport.hpp" #include "../../main/cpp/TestSupport.hpp"
#include <ostream> #include <ostream>