[K/N] Separate MM from GC implementations

^KT-50291

Merge-request: KT-MR-5191
Merged-by: Alexander Shabalin <Alexander.Shabalin@jetbrains.com>
This commit is contained in:
Alexander Shabalin
2021-12-21 08:33:51 +00:00
committed by Space
parent ea33da3a8d
commit 43b8683b9c
33 changed files with 650 additions and 214 deletions
@@ -234,20 +234,16 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
add("legacy_memory_manager.bc") add("legacy_memory_manager.bc")
} }
MemoryModel.EXPERIMENTAL -> { MemoryModel.EXPERIMENTAL -> {
add("common_gc.bc")
add("experimental_memory_manager.bc")
when (gc) { when (gc) {
GC.SAME_THREAD_MARK_AND_SWEEP -> { GC.SAME_THREAD_MARK_AND_SWEEP -> {
add("common_gc_stms.bc")
add("experimental_memory_manager_stms.bc")
add("same_thread_ms_gc.bc") add("same_thread_ms_gc.bc")
} }
GC.NOOP -> { GC.NOOP -> {
add("common_gc_noop.bc")
add("experimental_memory_manager_noop.bc")
add("noop_gc.bc") add("noop_gc.bc")
} }
GC.CONCURRENT_MARK_AND_SWEEP -> { GC.CONCURRENT_MARK_AND_SWEEP -> {
add("common_gc_cms.bc")
add("experimental_memory_manager_cms.bc")
add("concurrent_ms_gc.bc") add("concurrent_ms_gc.bc")
} }
} }
+19 -47
View File
@@ -58,12 +58,8 @@ bitcode {
"${target}Objc", "${target}Objc",
"${target}ExceptionsSupport", "${target}ExceptionsSupport",
"${target}LegacyMemoryManager", "${target}LegacyMemoryManager",
"${target}ExperimentalMemoryManagerNoop", "${target}ExperimentalMemoryManager",
"${target}ExperimentalMemoryManagerStms", "${target}CommonGc",
"${target}ExperimentalMemoryManagerCms",
"${target}CommonGcNoop",
"${target}CommonGcStms",
"${target}CommonGcCms",
"${target}SameThreadMsGc", "${target}SameThreadMsGc",
"${target}ConcurrentMsGc", "${target}ConcurrentMsGc",
"${target}NoopGc" "${target}NoopGc"
@@ -175,40 +171,16 @@ bitcode {
includeRuntime() includeRuntime()
} }
create("experimental_memory_manager_noop", file("src/mm")) { create("experimental_memory_manager", file("src/mm")) {
headersDirs += files("src/gc/noop/cpp", "src/gc/common/cpp") headersDirs += files("src/gc/common/cpp")
includeRuntime() includeRuntime()
} }
create("experimental_memory_manager_stms", file("src/mm")) { create("common_gc", file("src/gc/common")) {
headersDirs += files("src/gc/stms/cpp", "src/gc/common/cpp") headersDirs += files("src/mm/cpp")
includeRuntime() includeRuntime()
} }
create("experimental_memory_manager_cms", file("src/mm")) {
headersDirs += files("src/gc/cms/cpp", "src/gc/common/cpp")
includeRuntime()
onlyIf { targetSupportsThreads(target) }
}
create("common_gc_noop", file("src/gc/common")) {
headersDirs += files("src/gc/noop/cpp", "src/mm/cpp")
includeRuntime()
}
create("common_gc_stms", file("src/gc/common")) {
headersDirs += files("src/gc/stms/cpp", "src/mm/cpp")
includeRuntime()
}
create("common_gc_cms", file("src/gc/common")) {
headersDirs += files("src/gc/cms/cpp", "src/mm/cpp")
includeRuntime()
onlyIf { targetSupportsThreads(target) }
}
create("noop_gc", file("src/gc/noop")) { create("noop_gc", file("src/gc/noop")) {
headersDirs += files("src/gc/noop/cpp", "src/gc/common/cpp", "src/mm/cpp") headersDirs += files("src/gc/noop/cpp", "src/gc/common/cpp", "src/mm/cpp")
includeRuntime() includeRuntime()
@@ -267,8 +239,8 @@ targetList.forEach { targetName ->
"${targetName}ExperimentalMMMimallocRuntimeTests", "${targetName}ExperimentalMMMimallocRuntimeTests",
listOf( listOf(
"${targetName}Runtime", "${targetName}Runtime",
"${targetName}ExperimentalMemoryManagerStms", "${targetName}ExperimentalMemoryManager",
"${targetName}CommonGcStms", "${targetName}CommonGc",
"${targetName}SameThreadMsGc", "${targetName}SameThreadMsGc",
"${targetName}Mimalloc", "${targetName}Mimalloc",
"${targetName}OptAlloc", "${targetName}OptAlloc",
@@ -285,8 +257,8 @@ targetList.forEach { targetName ->
"${targetName}ExperimentalMMStdAllocRuntimeTests", "${targetName}ExperimentalMMStdAllocRuntimeTests",
listOf( listOf(
"${targetName}Runtime", "${targetName}Runtime",
"${targetName}ExperimentalMemoryManagerStms", "${targetName}ExperimentalMemoryManager",
"${targetName}CommonGcStms", "${targetName}CommonGc",
"${targetName}SameThreadMsGc", "${targetName}SameThreadMsGc",
"${targetName}StdAlloc", "${targetName}StdAlloc",
"${targetName}Objc" "${targetName}Objc"
@@ -302,8 +274,8 @@ targetList.forEach { targetName ->
"${targetName}ExperimentalMMCmsMimallocRuntimeTests", "${targetName}ExperimentalMMCmsMimallocRuntimeTests",
listOf( listOf(
"${targetName}Runtime", "${targetName}Runtime",
"${targetName}ExperimentalMemoryManagerCms", "${targetName}ExperimentalMemoryManager",
"${targetName}CommonGcCms", "${targetName}CommonGc",
"${targetName}ConcurrentMsGc", "${targetName}ConcurrentMsGc",
"${targetName}Mimalloc", "${targetName}Mimalloc",
"${targetName}OptAlloc", "${targetName}OptAlloc",
@@ -320,8 +292,8 @@ targetList.forEach { targetName ->
"${targetName}ExperimentalMMCmsStdAllocRuntimeTests", "${targetName}ExperimentalMMCmsStdAllocRuntimeTests",
listOf( listOf(
"${targetName}Runtime", "${targetName}Runtime",
"${targetName}ExperimentalMemoryManagerCms", "${targetName}ExperimentalMemoryManager",
"${targetName}CommonGcCms", "${targetName}CommonGc",
"${targetName}ConcurrentMsGc", "${targetName}ConcurrentMsGc",
"${targetName}StdAlloc", "${targetName}StdAlloc",
"${targetName}Objc" "${targetName}Objc"
@@ -337,8 +309,8 @@ targetList.forEach { targetName ->
"${targetName}ExperimentalMMNoOpMimallocRuntimeTests", "${targetName}ExperimentalMMNoOpMimallocRuntimeTests",
listOf( listOf(
"${targetName}Runtime", "${targetName}Runtime",
"${targetName}ExperimentalMemoryManagerNoop", "${targetName}ExperimentalMemoryManager",
"${targetName}CommonGcNoop", "${targetName}CommonGc",
"${targetName}NoopGc", "${targetName}NoopGc",
"${targetName}Mimalloc", "${targetName}Mimalloc",
"${targetName}OptAlloc", "${targetName}OptAlloc",
@@ -355,8 +327,8 @@ targetList.forEach { targetName ->
"${targetName}ExperimentalMMNoOpStdAllocRuntimeTests", "${targetName}ExperimentalMMNoOpStdAllocRuntimeTests",
listOf( listOf(
"${targetName}Runtime", "${targetName}Runtime",
"${targetName}ExperimentalMemoryManagerNoop", "${targetName}ExperimentalMemoryManager",
"${targetName}CommonGcNoop", "${targetName}CommonGc",
"${targetName}NoopGc", "${targetName}NoopGc",
"${targetName}StdAlloc", "${targetName}StdAlloc",
"${targetName}Objc" "${targetName}Objc"
@@ -548,4 +520,4 @@ targetList.forEach { targetName ->
} }
} }
// endregion // endregion
@@ -59,16 +59,8 @@ struct SweepTraits {
} // namespace } // namespace
ALWAYS_INLINE void gc::ConcurrentMarkAndSweep::ThreadData::SafePointFunctionPrologue() noexcept {
SafePointRegular(GCSchedulerThreadData::kFunctionPrologueWeight);
}
ALWAYS_INLINE void gc::ConcurrentMarkAndSweep::ThreadData::SafePointLoopBody() noexcept {
SafePointRegular(GCSchedulerThreadData::kLoopBodyWeight);
}
void gc::ConcurrentMarkAndSweep::ThreadData::SafePointAllocation(size_t size) noexcept { void gc::ConcurrentMarkAndSweep::ThreadData::SafePointAllocation(size_t size) noexcept {
threadData_.gcScheduler().OnSafePointAllocation(size); gcScheduler_.OnSafePointAllocation(size);
mm::SuspendIfRequested(); mm::SuspendIfRequested();
} }
void gc::ConcurrentMarkAndSweep::ThreadData::ScheduleAndWaitFullGC() noexcept { void gc::ConcurrentMarkAndSweep::ThreadData::ScheduleAndWaitFullGC() noexcept {
@@ -92,14 +84,12 @@ void gc::ConcurrentMarkAndSweep::ThreadData::OnOOM(size_t size) noexcept {
ScheduleAndWaitFullGC(); ScheduleAndWaitFullGC();
} }
ALWAYS_INLINE void gc::ConcurrentMarkAndSweep::ThreadData::SafePointRegular(size_t weight) noexcept { gc::ConcurrentMarkAndSweep::ConcurrentMarkAndSweep(
threadData_.gcScheduler().OnSafePointRegular(weight); mm::ObjectFactory<ConcurrentMarkAndSweep>& objectFactory, GCScheduler& gcScheduler) noexcept :
mm::SuspendIfRequested(); objectFactory_(objectFactory),
} gcScheduler_(gcScheduler),
finalizerProcessor_(make_unique<FinalizerProcessor>([this](int64_t epoch) { state_.finalized(epoch); })) {
gc::ConcurrentMarkAndSweep::ConcurrentMarkAndSweep() noexcept : gcScheduler_.SetScheduleGC([this]() NO_EXTERNAL_CALLS_CHECK NO_INLINE {
finalizerProcessor_(make_unique<FinalizerProcessor>([this](int64_t epoch) { state_.finalized(epoch);})) {
mm::GlobalData::Instance().gcScheduler().SetScheduleGC([this]() NO_EXTERNAL_CALLS_CHECK NO_INLINE {
RuntimeLogDebug({kTagGC}, "Scheduling GC by thread %d", konan::currentThreadId()); RuntimeLogDebug({kTagGC}, "Scheduling GC by thread %d", konan::currentThreadId());
state_.schedule(); state_.schedule();
}); });
@@ -115,7 +105,6 @@ gc::ConcurrentMarkAndSweep::ConcurrentMarkAndSweep() noexcept :
}); });
} }
gc::ConcurrentMarkAndSweep::~ConcurrentMarkAndSweep() { gc::ConcurrentMarkAndSweep::~ConcurrentMarkAndSweep() {
state_.shutdown(); state_.shutdown();
gcThread_.join(); gcThread_.join();
@@ -134,7 +123,7 @@ bool gc::ConcurrentMarkAndSweep::PerformFullGC(int64_t epoch) noexcept {
auto timeSuspendUs = konan::getTimeMicros(); auto timeSuspendUs = konan::getTimeMicros();
RuntimeLogDebug({kTagGC}, "Suspended all threads in %" PRIu64 " microseconds", timeSuspendUs - timeStartUs); RuntimeLogDebug({kTagGC}, "Suspended all threads in %" PRIu64 " microseconds", timeSuspendUs - timeStartUs);
auto& scheduler = mm::GlobalData::Instance().gcScheduler(); auto& scheduler = gcScheduler_;
scheduler.gcData().OnPerformFullGC(); scheduler.gcData().OnPerformFullGC();
state_.start(epoch); state_.start(epoch);
@@ -144,7 +133,7 @@ bool gc::ConcurrentMarkAndSweep::PerformFullGC(int64_t epoch) noexcept {
auto timeRootSetUs = konan::getTimeMicros(); auto timeRootSetUs = konan::getTimeMicros();
// Can be unsafe, because we've stopped the world. // Can be unsafe, because we've stopped the world.
auto objectsCountBefore = mm::GlobalData::Instance().objectFactory().GetSizeUnsafe(); auto objectsCountBefore = objectFactory_.GetSizeUnsafe();
RuntimeLogInfo( RuntimeLogInfo(
{kTagGC}, "Collected root set of size %zu in %" PRIu64 " microseconds", graySet.size(), {kTagGC}, "Collected root set of size %zu in %" PRIu64 " microseconds", graySet.size(),
timeRootSetUs - timeSuspendUs); timeRootSetUs - timeSuspendUs);
@@ -156,7 +145,7 @@ bool gc::ConcurrentMarkAndSweep::PerformFullGC(int64_t epoch) noexcept {
auto timeSweepExtraObjectsUs = konan::getTimeMicros(); auto timeSweepExtraObjectsUs = konan::getTimeMicros();
RuntimeLogDebug({kTagGC}, "Sweeped extra objects in %" PRIu64 " microseconds", timeSweepExtraObjectsUs - timeMarkUs); RuntimeLogDebug({kTagGC}, "Sweeped extra objects in %" PRIu64 " microseconds", timeSweepExtraObjectsUs - timeMarkUs);
auto objectFactoryIterable = mm::GlobalData::Instance().objectFactory().LockForIter(); auto objectFactoryIterable = objectFactory_.LockForIter();
mm::ResumeThreads(); mm::ResumeThreads();
auto timeResumeUs = konan::getTimeMicros(); auto timeResumeUs = konan::getTimeMicros();
@@ -170,7 +159,7 @@ bool gc::ConcurrentMarkAndSweep::PerformFullGC(int64_t epoch) noexcept {
RuntimeLogDebug({kTagGC}, "Swept in %" PRIu64 " microseconds", timeSweepUs - timeResumeUs); RuntimeLogDebug({kTagGC}, "Swept in %" PRIu64 " microseconds", timeSweepUs - timeResumeUs);
// Can be unsafe, because we have a lock in objectFactoryIterable // Can be unsafe, because we have a lock in objectFactoryIterable
auto objectsCountAfter = mm::GlobalData::Instance().objectFactory().GetSizeUnsafe(); auto objectsCountAfter = objectFactory_.GetSizeUnsafe();
auto extraObjectsCountAfter = mm::GlobalData::Instance().extraObjectDataFactory().GetSizeUnsafe(); auto extraObjectsCountAfter = mm::GlobalData::Instance().extraObjectDataFactory().GetSizeUnsafe();
auto finalizersCount = finalizerQueue.size(); auto finalizersCount = finalizerQueue.size();
@@ -49,12 +49,10 @@ public:
using ObjectData = ConcurrentMarkAndSweep::ObjectData; using ObjectData = ConcurrentMarkAndSweep::ObjectData;
using Allocator = AllocatorWithGC<AlignedAllocator, ThreadData>; using Allocator = AllocatorWithGC<AlignedAllocator, ThreadData>;
explicit ThreadData(ConcurrentMarkAndSweep& gc, mm::ThreadData& threadData) noexcept : gc_(gc), threadData_(threadData) {} explicit ThreadData(ConcurrentMarkAndSweep& gc, mm::ThreadData& threadData, GCSchedulerThreadData& gcScheduler) noexcept :
gc_(gc), gcScheduler_(gcScheduler) {}
~ThreadData() = default; ~ThreadData() = default;
void SafePointFunctionPrologue() noexcept;
void SafePointLoopBody() noexcept;
void SafePointExceptionUnwind() noexcept;
void SafePointAllocation(size_t size) noexcept; void SafePointAllocation(size_t size) noexcept;
void ScheduleAndWaitFullGC() noexcept; void ScheduleAndWaitFullGC() noexcept;
@@ -66,21 +64,22 @@ public:
Allocator CreateAllocator() noexcept { return Allocator(AlignedAllocator(), *this); } Allocator CreateAllocator() noexcept { return Allocator(AlignedAllocator(), *this); }
private: private:
void SafePointRegular(size_t weight) noexcept;
ConcurrentMarkAndSweep& gc_; ConcurrentMarkAndSweep& gc_;
mm::ThreadData& threadData_; GCSchedulerThreadData& gcScheduler_;
}; };
using Allocator = ThreadData::Allocator; using Allocator = ThreadData::Allocator;
ConcurrentMarkAndSweep() noexcept; ConcurrentMarkAndSweep(mm::ObjectFactory<ConcurrentMarkAndSweep>& objectFactory, GCScheduler& scheduler) noexcept;
~ConcurrentMarkAndSweep(); ~ConcurrentMarkAndSweep();
private: private:
// Returns `true` if GC has happened, and `false` if not (because someone else has suspended the threads). // Returns `true` if GC has happened, and `false` if not (because someone else has suspended the threads).
bool PerformFullGC(int64_t epoch) noexcept; bool PerformFullGC(int64_t epoch) noexcept;
mm::ObjectFactory<ConcurrentMarkAndSweep>& objectFactory_;
GCScheduler& gcScheduler_;
uint64_t lastGCTimestampUs_ = 0; uint64_t lastGCTimestampUs_ = 0;
GCStateHolder state_; GCStateHolder state_;
std::thread gcThread_; std::thread gcThread_;
@@ -15,6 +15,7 @@
#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"
@@ -187,10 +188,10 @@ test_support::Object<Payload>& AllocateObjectWithFinalizer(mm::ThreadData& threa
KStdVector<ObjHeader*> Alive(mm::ThreadData& threadData) { KStdVector<ObjHeader*> Alive(mm::ThreadData& threadData) {
KStdVector<ObjHeader*> objects; KStdVector<ObjHeader*> objects;
for (auto node : threadData.objectFactoryThreadQueue()) { for (auto node : threadData.gc().impl().objectFactoryThreadQueue()) {
objects.push_back(node.IsArray() ? node.GetArrayHeader()->obj() : node.GetObjHeader()); objects.push_back(node.IsArray() ? node.GetArrayHeader()->obj() : node.GetObjHeader());
} }
for (auto node : mm::GlobalData::Instance().objectFactory().LockForIter()) { for (auto node : mm::GlobalData::Instance().gc().impl().objectFactory().LockForIter()) {
objects.push_back(node.IsArray() ? node.GetArrayHeader()->obj() : node.GetObjHeader()); objects.push_back(node.IsArray() ? node.GetArrayHeader()->obj() : node.GetObjHeader());
} }
return objects; return objects;
@@ -219,7 +220,7 @@ public:
~ConcurrentMarkAndSweepTest() { ~ConcurrentMarkAndSweepTest() {
mm::GlobalsRegistry::Instance().ClearForTests(); mm::GlobalsRegistry::Instance().ClearForTests();
mm::GlobalData::Instance().extraObjectDataFactory().ClearForTests(); mm::GlobalData::Instance().extraObjectDataFactory().ClearForTests();
mm::GlobalData::Instance().objectFactory().ClearForTests(); mm::GlobalData::Instance().gc().ClearForTests();
} }
testing::MockFunction<void(ObjHeader*)>& finalizerHook() { return finalizerHooks_.finalizerHook(); } testing::MockFunction<void(ObjHeader*)>& finalizerHook() { return finalizerHooks_.finalizerHook(); }
@@ -338,7 +339,7 @@ TEST_F(ConcurrentMarkAndSweepTest, FreeObjectsWithFinalizers) {
EXPECT_CALL(finalizerHook(), Call(object1.header())); EXPECT_CALL(finalizerHook(), Call(object1.header()));
EXPECT_CALL(finalizerHook(), Call(object2.header())); EXPECT_CALL(finalizerHook(), Call(object2.header()));
threadData.gc().ScheduleAndWaitFullGCWithFinalizers(); threadData.gc().ScheduleAndWaitFullGCWithFinalizers();
threadData.gc().StopFinalizerThreadForTests(); threadData.gc().impl().gc().StopFinalizerThreadForTests();
EXPECT_THAT(Alive(threadData), testing::UnorderedElementsAre()); EXPECT_THAT(Alive(threadData), testing::UnorderedElementsAre());
}); });
@@ -358,7 +359,7 @@ TEST_F(ConcurrentMarkAndSweepTest, FreeObjectWithFreeWeak) {
ASSERT_THAT(weak1->referred, object1.header()); ASSERT_THAT(weak1->referred, object1.header());
threadData.gc().ScheduleAndWaitFullGCWithFinalizers(); threadData.gc().ScheduleAndWaitFullGCWithFinalizers();
threadData.gc().StopFinalizerThreadForTests(); threadData.gc().impl().gc().StopFinalizerThreadForTests();
EXPECT_THAT(Alive(threadData), testing::UnorderedElementsAre()); EXPECT_THAT(Alive(threadData), testing::UnorderedElementsAre());
}); });
@@ -509,7 +510,7 @@ TEST_F(ConcurrentMarkAndSweepTest, ObjectsWithCyclesAndFinalizers) {
EXPECT_CALL(finalizerHook(), Call(object5.header())); EXPECT_CALL(finalizerHook(), Call(object5.header()));
EXPECT_CALL(finalizerHook(), Call(object6.header())); EXPECT_CALL(finalizerHook(), Call(object6.header()));
threadData.gc().ScheduleAndWaitFullGCWithFinalizers(); threadData.gc().ScheduleAndWaitFullGCWithFinalizers();
threadData.gc().StopFinalizerThreadForTests(); threadData.gc().impl().gc().StopFinalizerThreadForTests();
EXPECT_THAT( EXPECT_THAT(
Alive(threadData), Alive(threadData),
@@ -15,6 +15,7 @@
#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"
@@ -55,7 +56,7 @@ public:
~FinalizerProcessorTest() { ~FinalizerProcessorTest() {
mm::GlobalsRegistry::Instance().ClearForTests(); mm::GlobalsRegistry::Instance().ClearForTests();
mm::GlobalData::Instance().extraObjectDataFactory().ClearForTests(); mm::GlobalData::Instance().extraObjectDataFactory().ClearForTests();
mm::GlobalData::Instance().objectFactory().ClearForTests(); mm::GlobalData::Instance().gc().ClearForTests();
} }
testing::MockFunction<void(ObjHeader*)>& finalizerHook() { return finalizerHooks_.finalizerHook(); } testing::MockFunction<void(ObjHeader*)>& finalizerHook() { return finalizerHooks_.finalizerHook(); }
@@ -94,7 +95,7 @@ TEST_F(FinalizerProcessorTest, RemoveObject) {
mm::ObjectFactory<kotlin::gc::ConcurrentMarkAndSweep>::FinalizerQueue queue; mm::ObjectFactory<kotlin::gc::ConcurrentMarkAndSweep>::FinalizerQueue queue;
auto &object = AllocateObjectWithFinalizer(*mm::ThreadRegistry::Instance().CurrentThreadData()); auto &object = AllocateObjectWithFinalizer(*mm::ThreadRegistry::Instance().CurrentThreadData());
mm::ThreadRegistry::Instance().CurrentThreadData()->Publish(); mm::ThreadRegistry::Instance().CurrentThreadData()->Publish();
auto &factory = mm::GlobalData::Instance().objectFactory(); auto& factory = mm::GlobalData::Instance().gc().impl().objectFactory();
auto iter = factory.LockForIter(); auto iter = factory.LockForIter();
auto iterator = iter.begin(); auto iterator = iter.begin();
iter.MoveAndAdvance(queue, iterator); iter.MoveAndAdvance(queue, iterator);
@@ -121,7 +122,7 @@ TEST_F(FinalizerProcessorTest, ScheduleTasksWhileFinalizing) {
auto& object = AllocateObjectWithFinalizer(*mm::ThreadRegistry::Instance().CurrentThreadData()); auto& object = AllocateObjectWithFinalizer(*mm::ThreadRegistry::Instance().CurrentThreadData());
headers.push_back(object.header()); headers.push_back(object.header());
} }
auto& factory = mm::GlobalData::Instance().objectFactory(); auto& factory = mm::GlobalData::Instance().gc().impl().objectFactory();
mm::ThreadRegistry::Instance().CurrentThreadData()->Publish(); mm::ThreadRegistry::Instance().CurrentThreadData()->Publish();
auto iter = factory.LockForIter(); auto iter = factory.LockForIter();
mm::ObjectFactory<kotlin::gc::ConcurrentMarkAndSweep>::FinalizerQueue queue; mm::ObjectFactory<kotlin::gc::ConcurrentMarkAndSweep>::FinalizerQueue queue;
@@ -0,0 +1,77 @@
/*
* Copyright 2010-2021 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.
*/
#include "GCImpl.hpp"
#include "GC.hpp"
#include "ThreadSuspension.hpp"
using namespace kotlin;
namespace {
ALWAYS_INLINE void SafePointRegular(gc::GC::ThreadData& threadData, size_t weight) noexcept {
threadData.impl().gcScheduler().OnSafePointRegular(weight);
mm::SuspendIfRequested();
}
} // namespace
gc::GC::ThreadData::ThreadData(GC& gc, mm::ThreadData& threadData) noexcept : impl_(make_unique<Impl>(gc, threadData)) {}
gc::GC::ThreadData::~ThreadData() = default;
ALWAYS_INLINE void gc::GC::ThreadData::SafePointFunctionPrologue() noexcept {
SafePointRegular(*this, GCSchedulerThreadData::kFunctionPrologueWeight);
}
ALWAYS_INLINE void gc::GC::ThreadData::SafePointLoopBody() noexcept {
SafePointRegular(*this, GCSchedulerThreadData::kLoopBodyWeight);
}
void gc::GC::ThreadData::ScheduleAndWaitFullGC() noexcept {
impl_->gc().ScheduleAndWaitFullGC();
}
void gc::GC::ThreadData::ScheduleAndWaitFullGCWithFinalizers() noexcept {
impl_->gc().ScheduleAndWaitFullGCWithFinalizers();
}
void gc::GC::ThreadData::Publish() noexcept {
impl_->objectFactoryThreadQueue().Publish();
}
void gc::GC::ThreadData::ClearForTests() noexcept {
impl_->objectFactoryThreadQueue().ClearForTests();
}
ALWAYS_INLINE ObjHeader* gc::GC::ThreadData::CreateObject(const TypeInfo* typeInfo) noexcept {
return impl_->objectFactoryThreadQueue().CreateObject(typeInfo);
}
ALWAYS_INLINE ArrayHeader* gc::GC::ThreadData::CreateArray(const TypeInfo* typeInfo, uint32_t elements) noexcept {
return impl_->objectFactoryThreadQueue().CreateArray(typeInfo, elements);
}
void gc::GC::ThreadData::OnStoppedForGC() noexcept {
impl_->gcScheduler().OnStoppedForGC();
}
gc::GC::GC() noexcept : impl_(make_unique<Impl>()) {}
gc::GC::~GC() = default;
// static
size_t gc::GC::GetAllocatedHeapSize(ObjHeader* object) noexcept {
return mm::ObjectFactory<GCImpl>::GetAllocatedHeapSize(object);
}
gc::GCSchedulerConfig& gc::GC::gcSchedulerConfig() noexcept {
return impl_->gcScheduler().config();
}
void gc::GC::ClearForTests() noexcept {
impl_->objectFactory().ClearForTests();
}
@@ -0,0 +1,49 @@
/*
* Copyright 2010-2021 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.
*/
#pragma once
#include "GC.hpp"
#include "ConcurrentMarkAndSweep.hpp"
namespace kotlin {
namespace gc {
using GCImpl = ConcurrentMarkAndSweep;
class GC::Impl : private Pinned {
public:
Impl() noexcept : gc_(objectFactory_, gcScheduler_) {}
mm::ObjectFactory<gc::GCImpl>& objectFactory() noexcept { return objectFactory_; }
GCScheduler& gcScheduler() noexcept { return gcScheduler_; }
GCImpl& gc() noexcept { return gc_; }
private:
mm::ObjectFactory<gc::GCImpl> objectFactory_;
GCScheduler gcScheduler_;
GCImpl gc_;
};
class GC::ThreadData::Impl : private Pinned {
public:
Impl(GC& gc, mm::ThreadData& threadData) noexcept :
gcScheduler_(gc.impl_->gcScheduler().NewThreadData()),
gc_(gc.impl_->gc(), threadData, gcScheduler_),
objectFactoryThreadQueue_(gc.impl_->objectFactory(), gc_) {}
GCSchedulerThreadData& gcScheduler() noexcept { return gcScheduler_; }
GCImpl::ThreadData& gc() noexcept { return gc_; }
mm::ObjectFactory<GCImpl>::ThreadQueue& objectFactoryThreadQueue() noexcept { return objectFactoryThreadQueue_; }
private:
GCSchedulerThreadData gcScheduler_;
GCImpl::ThreadData gc_;
mm::ObjectFactory<GCImpl>::ThreadQueue objectFactoryThreadQueue_;
};
} // namespace gc
} // namespace kotlin
@@ -0,0 +1,31 @@
/*
* Copyright 2010-2021 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.
*/
#include "GCTestSupport.hpp"
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include "GCImpl.hpp"
using namespace kotlin;
namespace {
template <typename T>
auto collectCopy(T& iterable) {
std::vector<std::remove_reference_t<decltype(*iterable.begin())>> result;
for (const auto& element : iterable) {
result.push_back(element);
}
return result;
}
} // namespace
void gc::AssertClear(GC& gc) noexcept {
auto objects = gc.impl().objectFactory().LockForIter();
EXPECT_THAT(collectCopy(objects), testing::UnorderedElementsAre());
}
@@ -0,0 +1,70 @@
/*
* Copyright 2010-2021 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.
*/
#pragma once
#include "GCScheduler.hpp"
#include "Memory.h"
#include "Types.h"
#include "Utils.hpp"
namespace kotlin {
namespace mm {
class ThreadData;
}
namespace gc {
class GC : private Pinned {
public:
class Impl;
class ThreadData : private Pinned {
public:
class Impl;
ThreadData(GC& gc, mm::ThreadData& threadData) noexcept;
~ThreadData();
Impl& impl() noexcept { return *impl_; }
void SafePointFunctionPrologue() noexcept;
void SafePointLoopBody() noexcept;
void ScheduleAndWaitFullGC() noexcept;
void ScheduleAndWaitFullGCWithFinalizers() noexcept;
void Publish() noexcept;
void ClearForTests() noexcept;
ObjHeader* CreateObject(const TypeInfo* typeInfo) noexcept;
ArrayHeader* CreateArray(const TypeInfo* typeInfo, uint32_t elements) noexcept;
void OnStoppedForGC() noexcept;
private:
KStdUniquePtr<Impl> impl_;
};
GC() noexcept;
~GC();
Impl& impl() noexcept { return *impl_; }
static size_t GetAllocatedHeapSize(ObjHeader* object) noexcept;
gc::GCSchedulerConfig& gcSchedulerConfig() noexcept;
void ClearForTests() noexcept;
private:
KStdUniquePtr<Impl> impl_;
};
inline constexpr bool kSupportsMultipleMutators = true;
} // namespace gc
} // namespace kotlin
@@ -5,15 +5,12 @@
#pragma once #pragma once
#include "ConcurrentMarkAndSweep.hpp" #include "GC.hpp"
namespace kotlin { namespace kotlin {
namespace gc { namespace gc {
using GC = kotlin::gc::ConcurrentMarkAndSweep; void AssertClear(GC& gc) noexcept;
inline constexpr bool kSupportsMultipleMutators = true; }
} // namespace gc
} // namespace kotlin } // namespace kotlin
@@ -18,7 +18,7 @@ KStdVector<ObjHeader*> kotlin::gc::collectRootSet() {
for (auto& thread : mm::GlobalData::Instance().threadRegistry().LockForIter()) { for (auto& thread : mm::GlobalData::Instance().threadRegistry().LockForIter()) {
// TODO: Maybe it's more efficient to do by the suspending thread? // TODO: Maybe it's more efficient to do by the suspending thread?
thread.Publish(); thread.Publish();
thread.gcScheduler().OnStoppedForGC(); thread.gc().OnStoppedForGC();
size_t stack = 0; size_t stack = 0;
size_t tls = 0; size_t tls = 0;
for (auto value : mm::ThreadRootSet(thread)) { for (auto value : mm::ThreadRootSet(thread)) {
@@ -9,6 +9,7 @@
#include "gmock/gmock.h" #include "gmock/gmock.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "Allocator.hpp"
#include "FinalizerHooksTestSupport.hpp" #include "FinalizerHooksTestSupport.hpp"
#include "ObjectFactory.hpp" #include "ObjectFactory.hpp"
#include "ObjectTestSupport.hpp" #include "ObjectTestSupport.hpp"
@@ -1,21 +0,0 @@
/*
* Copyright 2010-2021 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.
*/
#ifndef RUNTIME_GC_NOOP_GC_H
#define RUNTIME_GC_NOOP_GC_H
#include "NoOpGC.hpp"
namespace kotlin {
namespace gc {
using GC = kotlin::gc::NoOpGC;
inline constexpr bool kSupportsMultipleMutators = true;
} // namespace gc
} // namespace kotlin
#endif // RUNTIME_GC_NOOP_GC_H
@@ -0,0 +1,67 @@
/*
* Copyright 2010-2021 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.
*/
#include "GCImpl.hpp"
#include "GC.hpp"
using namespace kotlin;
gc::GC::ThreadData::ThreadData(GC& gc, mm::ThreadData& threadData) noexcept : impl_(make_unique<Impl>(gc, threadData)) {}
gc::GC::ThreadData::~ThreadData() = default;
ALWAYS_INLINE void gc::GC::ThreadData::SafePointFunctionPrologue() noexcept {
impl_->gc().SafePointFunctionPrologue();
}
ALWAYS_INLINE void gc::GC::ThreadData::SafePointLoopBody() noexcept {
impl_->gc().SafePointLoopBody();
}
void gc::GC::ThreadData::ScheduleAndWaitFullGC() noexcept {
impl_->gc().ScheduleAndWaitFullGC();
}
void gc::GC::ThreadData::ScheduleAndWaitFullGCWithFinalizers() noexcept {
impl_->gc().ScheduleAndWaitFullGCWithFinalizers();
}
void gc::GC::ThreadData::Publish() noexcept {
impl_->objectFactoryThreadQueue().Publish();
}
void gc::GC::ThreadData::ClearForTests() noexcept {
impl_->objectFactoryThreadQueue().ClearForTests();
}
ALWAYS_INLINE ObjHeader* gc::GC::ThreadData::CreateObject(const TypeInfo* typeInfo) noexcept {
return impl_->objectFactoryThreadQueue().CreateObject(typeInfo);
}
ALWAYS_INLINE ArrayHeader* gc::GC::ThreadData::CreateArray(const TypeInfo* typeInfo, uint32_t elements) noexcept {
return impl_->objectFactoryThreadQueue().CreateArray(typeInfo, elements);
}
void gc::GC::ThreadData::OnStoppedForGC() noexcept {
impl_->gcScheduler().OnStoppedForGC();
}
gc::GC::GC() noexcept : impl_(make_unique<Impl>()) {}
gc::GC::~GC() = default;
// static
size_t gc::GC::GetAllocatedHeapSize(ObjHeader* object) noexcept {
return mm::ObjectFactory<GCImpl>::GetAllocatedHeapSize(object);
}
gc::GCSchedulerConfig& gc::GC::gcSchedulerConfig() noexcept {
return impl_->gcScheduler().config();
}
void gc::GC::ClearForTests() noexcept {
impl_->objectFactory().ClearForTests();
}
@@ -0,0 +1,49 @@
/*
* Copyright 2010-2021 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.
*/
#pragma once
#include "GC.hpp"
#include "NoOpGC.hpp"
namespace kotlin {
namespace gc {
using GCImpl = NoOpGC;
class GC::Impl : private Pinned {
public:
Impl() noexcept : gc_(objectFactory_, gcScheduler_) {}
mm::ObjectFactory<gc::GCImpl>& objectFactory() noexcept { return objectFactory_; }
GCScheduler& gcScheduler() noexcept { return gcScheduler_; }
GCImpl& gc() noexcept { return gc_; }
private:
mm::ObjectFactory<gc::GCImpl> objectFactory_;
GCScheduler gcScheduler_;
GCImpl gc_;
};
class GC::ThreadData::Impl : private Pinned {
public:
Impl(GC& gc, mm::ThreadData& threadData) noexcept :
gcScheduler_(gc.impl_->gcScheduler().NewThreadData()),
gc_(gc.impl_->gc(), threadData, gcScheduler_),
objectFactoryThreadQueue_(gc.impl_->objectFactory(), gc_) {}
GCSchedulerThreadData& gcScheduler() noexcept { return gcScheduler_; }
GCImpl::ThreadData& gc() noexcept { return gc_; }
mm::ObjectFactory<GCImpl>::ThreadQueue& objectFactoryThreadQueue() noexcept { return objectFactoryThreadQueue_; }
private:
GCSchedulerThreadData gcScheduler_;
GCImpl::ThreadData gc_;
mm::ObjectFactory<GCImpl>::ThreadQueue objectFactoryThreadQueue_;
};
} // namespace gc
} // namespace kotlin
@@ -0,0 +1,31 @@
/*
* Copyright 2010-2021 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.
*/
#include "GCTestSupport.hpp"
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include "GCImpl.hpp"
using namespace kotlin;
namespace {
template <typename T>
auto collectCopy(T& iterable) {
std::vector<std::remove_reference_t<decltype(*iterable.begin())>> result;
for (const auto& element : iterable) {
result.push_back(element);
}
return result;
}
} // namespace
void gc::AssertClear(GC& gc) noexcept {
auto objects = gc.impl().objectFactory().LockForIter();
EXPECT_THAT(collectCopy(objects), testing::UnorderedElementsAre());
}
@@ -10,6 +10,7 @@
#include "Allocator.hpp" #include "Allocator.hpp"
#include "GCScheduler.hpp" #include "GCScheduler.hpp"
#include "ObjectFactory.hpp"
#include "Utils.hpp" #include "Utils.hpp"
#include "Types.h" #include "Types.h"
@@ -33,7 +34,7 @@ public:
public: public:
using ObjectData = NoOpGC::ObjectData; using ObjectData = NoOpGC::ObjectData;
explicit ThreadData(NoOpGC& gc, mm::ThreadData& threadData) noexcept {} ThreadData(NoOpGC& gc, mm::ThreadData& threadData, GCSchedulerThreadData&) noexcept {}
~ThreadData() = default; ~ThreadData() = default;
void SafePointFunctionPrologue() noexcept {} void SafePointFunctionPrologue() noexcept {}
@@ -50,7 +51,7 @@ public:
private: private:
}; };
NoOpGC() noexcept {} NoOpGC(mm::ObjectFactory<NoOpGC>&, GCScheduler&) noexcept {}
~NoOpGC() = default; ~NoOpGC() = default;
GCScheduler& scheduler() noexcept { return scheduler_; } GCScheduler& scheduler() noexcept { return scheduler_; }
@@ -1,21 +0,0 @@
/*
* Copyright 2010-2021 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.
*/
#ifndef RUNTIME_GC_STMS_GC_H
#define RUNTIME_GC_STMS_GC_H
#include "SameThreadMarkAndSweep.hpp"
namespace kotlin {
namespace gc {
using GC = kotlin::gc::SameThreadMarkAndSweep;
inline constexpr bool kSupportsMultipleMutators = true;
} // namespace gc
} // namespace kotlin
#endif // RUNTIME_GC_STMS_GC_H
@@ -0,0 +1,79 @@
/*
* Copyright 2010-2021 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.
*/
#include "GCImpl.hpp"
#include "GC.hpp"
using namespace kotlin;
namespace {
ALWAYS_INLINE void SafePointRegular(gc::GC::ThreadData& threadData, size_t weight) noexcept {
threadData.impl().gcScheduler().OnSafePointRegular(weight);
auto flag = gc::internal::loadSafepointFlag();
if (flag != gc::SameThreadMarkAndSweep::SafepointFlag::kNone) {
threadData.impl().gc().SafePointSlowPath(flag);
}
}
} // namespace
gc::GC::ThreadData::ThreadData(GC& gc, mm::ThreadData& threadData) noexcept : impl_(make_unique<Impl>(gc, threadData)) {}
gc::GC::ThreadData::~ThreadData() = default;
ALWAYS_INLINE void gc::GC::ThreadData::SafePointFunctionPrologue() noexcept {
SafePointRegular(*this, GCSchedulerThreadData::kFunctionPrologueWeight);
}
ALWAYS_INLINE void gc::GC::ThreadData::SafePointLoopBody() noexcept {
SafePointRegular(*this, GCSchedulerThreadData::kLoopBodyWeight);
}
void gc::GC::ThreadData::ScheduleAndWaitFullGC() noexcept {
impl_->gc().ScheduleAndWaitFullGC();
}
void gc::GC::ThreadData::ScheduleAndWaitFullGCWithFinalizers() noexcept {
impl_->gc().ScheduleAndWaitFullGCWithFinalizers();
}
void gc::GC::ThreadData::Publish() noexcept {
impl_->objectFactoryThreadQueue().Publish();
}
void gc::GC::ThreadData::ClearForTests() noexcept {
impl_->objectFactoryThreadQueue().ClearForTests();
}
ALWAYS_INLINE ObjHeader* gc::GC::ThreadData::CreateObject(const TypeInfo* typeInfo) noexcept {
return impl_->objectFactoryThreadQueue().CreateObject(typeInfo);
}
ALWAYS_INLINE ArrayHeader* gc::GC::ThreadData::CreateArray(const TypeInfo* typeInfo, uint32_t elements) noexcept {
return impl_->objectFactoryThreadQueue().CreateArray(typeInfo, elements);
}
void gc::GC::ThreadData::OnStoppedForGC() noexcept {
impl_->gcScheduler().OnStoppedForGC();
}
gc::GC::GC() noexcept : impl_(make_unique<Impl>()) {}
gc::GC::~GC() = default;
// static
size_t gc::GC::GetAllocatedHeapSize(ObjHeader* object) noexcept {
return mm::ObjectFactory<GCImpl>::GetAllocatedHeapSize(object);
}
gc::GCSchedulerConfig& gc::GC::gcSchedulerConfig() noexcept {
return impl_->gcScheduler().config();
}
void gc::GC::ClearForTests() noexcept {
impl_->objectFactory().ClearForTests();
}
@@ -0,0 +1,49 @@
/*
* Copyright 2010-2021 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.
*/
#pragma once
#include "GC.hpp"
#include "SameThreadMarkAndSweep.hpp"
namespace kotlin {
namespace gc {
using GCImpl = SameThreadMarkAndSweep;
class GC::Impl : private Pinned {
public:
Impl() noexcept : gc_(objectFactory_, gcScheduler_) {}
mm::ObjectFactory<gc::GCImpl>& objectFactory() noexcept { return objectFactory_; }
GCScheduler& gcScheduler() noexcept { return gcScheduler_; }
GCImpl& gc() noexcept { return gc_; }
private:
mm::ObjectFactory<gc::GCImpl> objectFactory_;
GCScheduler gcScheduler_;
GCImpl gc_;
};
class GC::ThreadData::Impl : private Pinned {
public:
Impl(GC& gc, mm::ThreadData& threadData) noexcept :
gcScheduler_(gc.impl_->gcScheduler().NewThreadData()),
gc_(gc.impl_->gc(), threadData, gcScheduler_),
objectFactoryThreadQueue_(gc.impl_->objectFactory(), gc_) {}
GCSchedulerThreadData& gcScheduler() noexcept { return gcScheduler_; }
GCImpl::ThreadData& gc() noexcept { return gc_; }
mm::ObjectFactory<GCImpl>::ThreadQueue& objectFactoryThreadQueue() noexcept { return objectFactoryThreadQueue_; }
private:
GCSchedulerThreadData gcScheduler_;
GCImpl::ThreadData gc_;
mm::ObjectFactory<GCImpl>::ThreadQueue objectFactoryThreadQueue_;
};
} // namespace gc
} // namespace kotlin
@@ -0,0 +1,31 @@
/*
* Copyright 2010-2021 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.
*/
#include "GCTestSupport.hpp"
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include "GCImpl.hpp"
using namespace kotlin;
namespace {
template <typename T>
auto collectCopy(T& iterable) {
std::vector<std::remove_reference_t<decltype(*iterable.begin())>> result;
for (const auto& element : iterable) {
result.push_back(element);
}
return result;
}
} // namespace
void gc::AssertClear(GC& gc) noexcept {
auto objects = gc.impl().objectFactory().LockForIter();
EXPECT_THAT(collectCopy(objects), testing::UnorderedElementsAre());
}
@@ -64,16 +64,8 @@ std::atomic<gc::SameThreadMarkAndSweep::SafepointFlag> gSafepointFlag = gc::Same
} // namespace } // namespace
ALWAYS_INLINE void gc::SameThreadMarkAndSweep::ThreadData::SafePointFunctionPrologue() noexcept {
SafePointRegular(GCSchedulerThreadData::kFunctionPrologueWeight);
}
ALWAYS_INLINE void gc::SameThreadMarkAndSweep::ThreadData::SafePointLoopBody() noexcept {
SafePointRegular(GCSchedulerThreadData::kLoopBodyWeight);
}
void gc::SameThreadMarkAndSweep::ThreadData::SafePointAllocation(size_t size) noexcept { void gc::SameThreadMarkAndSweep::ThreadData::SafePointAllocation(size_t size) noexcept {
threadData_.gcScheduler().OnSafePointAllocation(size); gcScheduler_.OnSafePointAllocation(size);
SafepointFlag flag = gSafepointFlag.load(); SafepointFlag flag = gSafepointFlag.load();
if (flag != SafepointFlag::kNone) { if (flag != SafepointFlag::kNone) {
SafePointSlowPath(flag); SafePointSlowPath(flag);
@@ -85,7 +77,7 @@ void gc::SameThreadMarkAndSweep::ThreadData::ScheduleAndWaitFullGC() noexcept {
if (!didGC) { if (!didGC) {
// If we failed to suspend threads, someone else might be asking to suspend them. // If we failed to suspend threads, someone else might be asking to suspend them.
threadData_.suspensionData().suspendIfRequested(); mm::SuspendIfRequested();
} }
} }
@@ -94,21 +86,13 @@ void gc::SameThreadMarkAndSweep::ThreadData::OnOOM(size_t size) noexcept {
ScheduleAndWaitFullGC(); ScheduleAndWaitFullGC();
} }
ALWAYS_INLINE void gc::SameThreadMarkAndSweep::ThreadData::SafePointRegular(size_t weight) noexcept {
threadData_.gcScheduler().OnSafePointRegular(weight);
SafepointFlag flag = gSafepointFlag.load();
if (flag != SafepointFlag::kNone) {
SafePointSlowPath(flag);
}
}
NO_INLINE void gc::SameThreadMarkAndSweep::ThreadData::SafePointSlowPath(SafepointFlag flag) noexcept { NO_INLINE void gc::SameThreadMarkAndSweep::ThreadData::SafePointSlowPath(SafepointFlag flag) noexcept {
switch (flag) { switch (flag) {
case SafepointFlag::kNone: case SafepointFlag::kNone:
RuntimeAssert(false, "Must've been handled by the caller"); RuntimeAssert(false, "Must've been handled by the caller");
return; return;
case SafepointFlag::kNeedsSuspend: case SafepointFlag::kNeedsSuspend:
threadData_.suspensionData().suspendIfRequested(); mm::SuspendIfRequested();
return; return;
case SafepointFlag::kNeedsGC: case SafepointFlag::kNeedsGC:
RuntimeLogDebug({kTagGC}, "Attempt to GC at SafePoint"); RuntimeLogDebug({kTagGC}, "Attempt to GC at SafePoint");
@@ -117,8 +101,10 @@ NO_INLINE void gc::SameThreadMarkAndSweep::ThreadData::SafePointSlowPath(Safepoi
} }
} }
gc::SameThreadMarkAndSweep::SameThreadMarkAndSweep() noexcept { gc::SameThreadMarkAndSweep::SameThreadMarkAndSweep(
mm::GlobalData::Instance().gcScheduler().SetScheduleGC([]() { mm::ObjectFactory<SameThreadMarkAndSweep>& objectFactory, GCScheduler& gcScheduler) noexcept :
objectFactory_(objectFactory), gcScheduler_(gcScheduler) {
gcScheduler_.SetScheduleGC([]() {
RuntimeLogDebug({kTagGC}, "Scheduling GC by thread %d", konan::currentThreadId()); RuntimeLogDebug({kTagGC}, "Scheduling GC by thread %d", konan::currentThreadId());
gSafepointFlag = SafepointFlag::kNeedsGC; gSafepointFlag = SafepointFlag::kNeedsGC;
}); });
@@ -146,7 +132,7 @@ bool gc::SameThreadMarkAndSweep::PerformFullGC() noexcept {
auto timeSuspendUs = konan::getTimeMicros(); auto timeSuspendUs = konan::getTimeMicros();
RuntimeLogDebug({kTagGC}, "Suspended all threads in %" PRIu64 " microseconds", timeSuspendUs - timeStartUs); RuntimeLogDebug({kTagGC}, "Suspended all threads in %" PRIu64 " microseconds", timeSuspendUs - timeStartUs);
auto& scheduler = mm::GlobalData::Instance().gcScheduler(); auto& scheduler = gcScheduler_;
scheduler.gcData().OnPerformFullGC(); scheduler.gcData().OnPerformFullGC();
RuntimeLogInfo( RuntimeLogInfo(
@@ -154,7 +140,7 @@ bool gc::SameThreadMarkAndSweep::PerformFullGC() noexcept {
auto graySet = collectRootSet(); auto graySet = collectRootSet();
auto timeRootSetUs = konan::getTimeMicros(); auto timeRootSetUs = konan::getTimeMicros();
// Can be unsafe, because we've stopped the world. // Can be unsafe, because we've stopped the world.
auto objectsCountBefore = mm::GlobalData::Instance().objectFactory().GetSizeUnsafe(); auto objectsCountBefore = objectFactory_.GetSizeUnsafe();
RuntimeLogInfo( RuntimeLogInfo(
{kTagGC}, "Collected root set of size %zu in %" PRIu64 " microseconds", graySet.size(), {kTagGC}, "Collected root set of size %zu in %" PRIu64 " microseconds", graySet.size(),
@@ -166,12 +152,12 @@ bool gc::SameThreadMarkAndSweep::PerformFullGC() noexcept {
gc::SweepExtraObjects<SweepTraits>(mm::GlobalData::Instance().extraObjectDataFactory()); gc::SweepExtraObjects<SweepTraits>(mm::GlobalData::Instance().extraObjectDataFactory());
auto timeSweepExtraObjectsUs = konan::getTimeMicros(); auto timeSweepExtraObjectsUs = konan::getTimeMicros();
RuntimeLogDebug({kTagGC}, "Sweeped extra objects in %" PRIu64 " microseconds", timeSweepExtraObjectsUs - timeMarkUs); RuntimeLogDebug({kTagGC}, "Sweeped extra objects in %" PRIu64 " microseconds", timeSweepExtraObjectsUs - timeMarkUs);
finalizerQueue = gc::Sweep<SweepTraits>(mm::GlobalData::Instance().objectFactory()); finalizerQueue = gc::Sweep<SweepTraits>(objectFactory_);
auto timeSweepUs = konan::getTimeMicros(); auto timeSweepUs = konan::getTimeMicros();
RuntimeLogDebug({kTagGC}, "Sweeped in %" PRIu64 " microseconds", timeSweepUs - timeSweepExtraObjectsUs); RuntimeLogDebug({kTagGC}, "Sweeped in %" PRIu64 " microseconds", timeSweepUs - timeSweepExtraObjectsUs);
// Can be unsafe, because we've stopped the world. // Can be unsafe, because we've stopped the world.
auto objectsCountAfter = mm::GlobalData::Instance().objectFactory().GetSizeUnsafe(); auto objectsCountAfter = objectFactory_.GetSizeUnsafe();
auto extraObjectsCountAfter = mm::GlobalData::Instance().extraObjectDataFactory().GetSizeUnsafe(); auto extraObjectsCountAfter = mm::GlobalData::Instance().extraObjectDataFactory().GetSizeUnsafe();
gSafepointFlag = SafepointFlag::kNone; gSafepointFlag = SafepointFlag::kNone;
@@ -206,3 +192,7 @@ bool gc::SameThreadMarkAndSweep::PerformFullGC() noexcept {
return true; return true;
} }
gc::SameThreadMarkAndSweep::SafepointFlag gc::internal::loadSafepointFlag() noexcept {
return gSafepointFlag.load();
}
@@ -51,12 +51,11 @@ public:
using ObjectData = SameThreadMarkAndSweep::ObjectData; using ObjectData = SameThreadMarkAndSweep::ObjectData;
using Allocator = AllocatorWithGC<AlignedAllocator, ThreadData>; using Allocator = AllocatorWithGC<AlignedAllocator, ThreadData>;
explicit ThreadData(SameThreadMarkAndSweep& gc, mm::ThreadData& threadData) noexcept : gc_(gc), threadData_(threadData) {} ThreadData(SameThreadMarkAndSweep& gc, mm::ThreadData& threadData, GCSchedulerThreadData& gcScheduler) noexcept :
gc_(gc), gcScheduler_(gcScheduler) {}
~ThreadData() = default; ~ThreadData() = default;
void SafePointFunctionPrologue() noexcept; void SafePointSlowPath(SafepointFlag flag) noexcept;
void SafePointLoopBody() noexcept;
void SafePointExceptionUnwind() noexcept;
void SafePointAllocation(size_t size) noexcept; void SafePointAllocation(size_t size) noexcept;
void ScheduleAndWaitFullGC() noexcept; void ScheduleAndWaitFullGC() noexcept;
@@ -67,16 +66,14 @@ public:
Allocator CreateAllocator() noexcept { return Allocator(AlignedAllocator(), *this); } Allocator CreateAllocator() noexcept { return Allocator(AlignedAllocator(), *this); }
private: private:
void SafePointRegular(size_t weight) noexcept;
void SafePointSlowPath(SafepointFlag flag) noexcept;
SameThreadMarkAndSweep& gc_; SameThreadMarkAndSweep& gc_;
mm::ThreadData& threadData_; GCSchedulerThreadData& gcScheduler_;
}; };
using Allocator = ThreadData::Allocator; using Allocator = ThreadData::Allocator;
SameThreadMarkAndSweep() noexcept; SameThreadMarkAndSweep(mm::ObjectFactory<SameThreadMarkAndSweep>& objectFactory, GCScheduler& gcScheduler) noexcept;
~SameThreadMarkAndSweep() = default; ~SameThreadMarkAndSweep() = default;
void StopFinalizerThreadForTests() noexcept {} void StopFinalizerThreadForTests() noexcept {}
@@ -86,8 +83,17 @@ private:
size_t epoch_ = 0; size_t epoch_ = 0;
uint64_t lastGCTimestampUs_ = 0; uint64_t lastGCTimestampUs_ = 0;
mm::ObjectFactory<SameThreadMarkAndSweep>& objectFactory_;
GCScheduler& gcScheduler_;
}; };
namespace internal {
SameThreadMarkAndSweep::SafepointFlag loadSafepointFlag() noexcept;
} // namespace internal
} // namespace gc } // namespace gc
} // namespace kotlin } // namespace kotlin
@@ -15,6 +15,7 @@
#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"
@@ -187,10 +188,10 @@ test_support::Object<Payload>& AllocateObjectWithFinalizer(mm::ThreadData& threa
KStdVector<ObjHeader*> Alive(mm::ThreadData& threadData) { KStdVector<ObjHeader*> Alive(mm::ThreadData& threadData) {
KStdVector<ObjHeader*> objects; KStdVector<ObjHeader*> objects;
for (auto node : threadData.objectFactoryThreadQueue()) { for (auto node : threadData.gc().impl().objectFactoryThreadQueue()) {
objects.push_back(node.IsArray() ? node.GetArrayHeader()->obj() : node.GetObjHeader()); objects.push_back(node.IsArray() ? node.GetArrayHeader()->obj() : node.GetObjHeader());
} }
for (auto node : mm::GlobalData::Instance().objectFactory().LockForIter()) { for (auto node : mm::GlobalData::Instance().gc().impl().objectFactory().LockForIter()) {
objects.push_back(node.IsArray() ? node.GetArrayHeader()->obj() : node.GetObjHeader()); objects.push_back(node.IsArray() ? node.GetArrayHeader()->obj() : node.GetObjHeader());
} }
return objects; return objects;
@@ -215,11 +216,10 @@ WeakCounter& InstallWeakCounter(mm::ThreadData& threadData, ObjHeader* objHeader
class SameThreadMarkAndSweepTest : public testing::Test { class SameThreadMarkAndSweepTest : public testing::Test {
public: public:
~SameThreadMarkAndSweepTest() { ~SameThreadMarkAndSweepTest() {
mm::GlobalsRegistry::Instance().ClearForTests(); mm::GlobalsRegistry::Instance().ClearForTests();
mm::GlobalData::Instance().extraObjectDataFactory().ClearForTests(); mm::GlobalData::Instance().extraObjectDataFactory().ClearForTests();
mm::GlobalData::Instance().objectFactory().ClearForTests(); mm::GlobalData::Instance().gc().impl().objectFactory().ClearForTests();
} }
testing::MockFunction<void(ObjHeader*)>& finalizerHook() { return finalizerHooks_.finalizerHook(); } testing::MockFunction<void(ObjHeader*)>& finalizerHook() { return finalizerHooks_.finalizerHook(); }
@@ -30,7 +30,7 @@ public:
~ExtraObjectDataTest() { ~ExtraObjectDataTest() {
mm::GlobalsRegistry::Instance().ClearForTests(); mm::GlobalsRegistry::Instance().ClearForTests();
mm::GlobalData::Instance().extraObjectDataFactory().ClearForTests(); mm::GlobalData::Instance().extraObjectDataFactory().ClearForTests();
mm::GlobalData::Instance().objectFactory().ClearForTests(); mm::GlobalData::Instance().gc().ClearForTests();
} }
}; };
@@ -27,8 +27,6 @@ public:
GlobalsRegistry& globalsRegistry() noexcept { return globalsRegistry_; } GlobalsRegistry& globalsRegistry() noexcept { return globalsRegistry_; }
StableRefRegistry& stableRefRegistry() noexcept { return stableRefRegistry_; } StableRefRegistry& stableRefRegistry() noexcept { return stableRefRegistry_; }
ExtraObjectDataFactory& extraObjectDataFactory() noexcept { return extraObjectDataFactory_; } ExtraObjectDataFactory& extraObjectDataFactory() noexcept { return extraObjectDataFactory_; }
ObjectFactory<gc::GC>& objectFactory() noexcept { return objectFactory_; }
gc::GCScheduler& gcScheduler() noexcept { return gcScheduler_; }
gc::GC& gc() noexcept { return gc_; } gc::GC& gc() noexcept { return gc_; }
private: private:
@@ -42,8 +40,6 @@ private:
GlobalsRegistry globalsRegistry_; GlobalsRegistry globalsRegistry_;
StableRefRegistry stableRefRegistry_; StableRefRegistry stableRefRegistry_;
ExtraObjectDataFactory extraObjectDataFactory_; ExtraObjectDataFactory extraObjectDataFactory_;
ObjectFactory<gc::GC> objectFactory_;
gc::GCScheduler gcScheduler_;
gc::GC gc_; gc::GC gc_;
}; };
@@ -35,7 +35,7 @@ public:
~InitSingletonTest() { ~InitSingletonTest() {
globalConstructor_ = nullptr; globalConstructor_ = nullptr;
mm::GlobalData::Instance().objectFactory().ClearForTests(); mm::GlobalData::Instance().gc().ClearForTests();
mm::GlobalData::Instance().globalsRegistry().ClearForTests(); mm::GlobalData::Instance().globalsRegistry().ClearForTests();
} }
+6 -6
View File
@@ -324,11 +324,11 @@ extern "C" void Kotlin_native_internal_GC_setThreshold(ObjHeader*, int32_t value
if (value < 0) { if (value < 0) {
ThrowIllegalArgumentException(); ThrowIllegalArgumentException();
} }
mm::GlobalData::Instance().gcScheduler().config().threshold = static_cast<size_t>(value); mm::GlobalData::Instance().gc().gcSchedulerConfig().threshold = static_cast<size_t>(value);
} }
extern "C" int32_t Kotlin_native_internal_GC_getThreshold(ObjHeader*) { extern "C" int32_t Kotlin_native_internal_GC_getThreshold(ObjHeader*) {
auto threshold = mm::GlobalData::Instance().gcScheduler().config().threshold.load(); auto threshold = mm::GlobalData::Instance().gc().gcSchedulerConfig().threshold.load();
auto maxValue = std::numeric_limits<int32_t>::max(); auto maxValue = std::numeric_limits<int32_t>::max();
if (threshold > static_cast<size_t>(maxValue)) { if (threshold > static_cast<size_t>(maxValue)) {
return maxValue; return maxValue;
@@ -350,11 +350,11 @@ extern "C" void Kotlin_native_internal_GC_setThresholdAllocations(ObjHeader*, in
if (value < 0) { if (value < 0) {
ThrowIllegalArgumentException(); ThrowIllegalArgumentException();
} }
mm::GlobalData::Instance().gcScheduler().config().allocationThresholdBytes = static_cast<size_t>(value); mm::GlobalData::Instance().gc().gcSchedulerConfig().allocationThresholdBytes = static_cast<size_t>(value);
} }
extern "C" int64_t Kotlin_native_internal_GC_getThresholdAllocations(ObjHeader*) { extern "C" int64_t Kotlin_native_internal_GC_getThresholdAllocations(ObjHeader*) {
auto threshold = mm::GlobalData::Instance().gcScheduler().config().allocationThresholdBytes.load(); auto threshold = mm::GlobalData::Instance().gc().gcSchedulerConfig().allocationThresholdBytes.load();
auto maxValue = std::numeric_limits<int64_t>::max(); auto maxValue = std::numeric_limits<int64_t>::max();
if (threshold > static_cast<size_t>(maxValue)) { if (threshold > static_cast<size_t>(maxValue)) {
return maxValue; return maxValue;
@@ -363,11 +363,11 @@ extern "C" int64_t Kotlin_native_internal_GC_getThresholdAllocations(ObjHeader*)
} }
extern "C" void Kotlin_native_internal_GC_setTuneThreshold(ObjHeader*, KBoolean value) { extern "C" void Kotlin_native_internal_GC_setTuneThreshold(ObjHeader*, KBoolean value) {
mm::GlobalData::Instance().gcScheduler().config().autoTune = value; mm::GlobalData::Instance().gc().gcSchedulerConfig().autoTune = value;
} }
extern "C" KBoolean Kotlin_native_internal_GC_getTuneThreshold(ObjHeader*) { extern "C" KBoolean Kotlin_native_internal_GC_getTuneThreshold(ObjHeader*) {
return mm::GlobalData::Instance().gcScheduler().config().autoTune.load(); return mm::GlobalData::Instance().gc().gcSchedulerConfig().autoTune.load();
} }
extern "C" OBJ_GETTER(Kotlin_native_internal_GC_detectCycles, ObjHeader*) { extern "C" OBJ_GETTER(Kotlin_native_internal_GC_detectCycles, ObjHeader*) {
@@ -691,6 +691,16 @@ public:
void ClearForTests() { storage_.ClearForTests(); } void ClearForTests() { storage_.ClearForTests(); }
static size_t GetAllocatedHeapSize(ObjHeader* object) noexcept {
RuntimeAssert(object->heap(), "Object must be a heap object");
const auto* typeInfo = object->type_info();
if (typeInfo->IsArray()) {
return ThreadQueue::ArrayAllocatedSize(typeInfo, object->array()->count_);
} else {
return ThreadQueue::ObjectAllocatedSize(typeInfo);
}
}
private: private:
Storage storage_; Storage storage_;
}; };
@@ -58,24 +58,18 @@ ALWAYS_INLINE OBJ_GETTER(mm::CompareAndSwapHeapRef, ObjHeader** location, ObjHea
OBJ_GETTER(mm::AllocateObject, ThreadData* threadData, const TypeInfo* typeInfo) noexcept { OBJ_GETTER(mm::AllocateObject, ThreadData* threadData, const TypeInfo* typeInfo) noexcept {
AssertThreadState(threadData, ThreadState::kRunnable); AssertThreadState(threadData, ThreadState::kRunnable);
// TODO: Make this work with GCs that can stop thread at any point. // TODO: Make this work with GCs that can stop thread at any point.
auto* object = threadData->objectFactoryThreadQueue().CreateObject(typeInfo); auto* object = threadData->gc().CreateObject(typeInfo);
RETURN_OBJ(object); RETURN_OBJ(object);
} }
OBJ_GETTER(mm::AllocateArray, ThreadData* threadData, const TypeInfo* typeInfo, uint32_t elements) noexcept { OBJ_GETTER(mm::AllocateArray, ThreadData* threadData, const TypeInfo* typeInfo, uint32_t elements) noexcept {
AssertThreadState(threadData, ThreadState::kRunnable); AssertThreadState(threadData, ThreadState::kRunnable);
// TODO: Make this work with GCs that can stop thread at any point. // TODO: Make this work with GCs that can stop thread at any point.
auto* array = threadData->objectFactoryThreadQueue().CreateArray(typeInfo, static_cast<uint32_t>(elements)); auto* array = threadData->gc().CreateArray(typeInfo, static_cast<uint32_t>(elements));
// `ArrayHeader` and `ObjHeader` are expected to be compatible. // `ArrayHeader` and `ObjHeader` are expected to be compatible.
RETURN_OBJ(reinterpret_cast<ObjHeader*>(array)); RETURN_OBJ(reinterpret_cast<ObjHeader*>(array));
} }
size_t mm::GetAllocatedHeapSize(ObjHeader* object) noexcept { size_t mm::GetAllocatedHeapSize(ObjHeader* object) noexcept {
RuntimeAssert(object->heap(), "Object must be a heap object"); return gc::GC::GetAllocatedHeapSize(object);
const auto* typeInfo = object->type_info();
if (typeInfo->IsArray()) {
return mm::ObjectFactory<gc::GC>::ThreadQueue::ArrayAllocatedSize(typeInfo, object->array()->count_);
} else {
return mm::ObjectFactory<gc::GC>::ThreadQueue::ObjectAllocatedSize(typeInfo);
}
} }
@@ -7,6 +7,7 @@
#include "gmock/gmock.h" #include "gmock/gmock.h"
#include "GC.hpp" #include "GC.hpp"
#include "GCTestSupport.hpp"
#include "GlobalData.hpp" #include "GlobalData.hpp"
#include "GlobalsRegistry.hpp" #include "GlobalsRegistry.hpp"
#include "TestSupport.hpp" #include "TestSupport.hpp"
@@ -45,15 +46,14 @@ extern "C" void Kotlin_TestSupport_AssertClearGlobalState() {
// Validate that global registries are empty. // Validate that global registries are empty.
auto globals = mm::GlobalsRegistry::Instance().LockForIter(); auto globals = mm::GlobalsRegistry::Instance().LockForIter();
auto extraObjects = mm::GlobalData::Instance().extraObjectDataFactory().LockForIter(); auto extraObjects = mm::GlobalData::Instance().extraObjectDataFactory().LockForIter();
auto objects = mm::GlobalData::Instance().objectFactory().LockForIter();
auto stableRefs = mm::StableRefRegistry::Instance().LockForIter(); auto stableRefs = mm::StableRefRegistry::Instance().LockForIter();
auto threads = mm::ThreadRegistry::Instance().LockForIter(); auto threads = mm::ThreadRegistry::Instance().LockForIter();
EXPECT_THAT(collectCopy(globals), testing::UnorderedElementsAre()); EXPECT_THAT(collectCopy(globals), testing::UnorderedElementsAre());
EXPECT_THAT(collectPointers(extraObjects), testing::UnorderedElementsAre()); EXPECT_THAT(collectPointers(extraObjects), testing::UnorderedElementsAre());
EXPECT_THAT(collectCopy(objects), testing::UnorderedElementsAre());
EXPECT_THAT(collectCopy(stableRefs), testing::UnorderedElementsAre()); EXPECT_THAT(collectCopy(stableRefs), testing::UnorderedElementsAre());
EXPECT_THAT(collectPointers(threads), testing::UnorderedElementsAre()); EXPECT_THAT(collectPointers(threads), testing::UnorderedElementsAre());
gc::AssertClear(mm::GlobalData::Instance().gc());
} }
void kotlin::DeinitMemoryForTests(MemoryState* memoryState) { void kotlin::DeinitMemoryForTests(MemoryState* memoryState) {
@@ -63,4 +63,4 @@ void kotlin::DeinitMemoryForTests(MemoryState* memoryState) {
std::ostream& kotlin::operator<<(std::ostream& stream, ThreadState state) { std::ostream& kotlin::operator<<(std::ostream& stream, ThreadState state) {
return stream << ThreadStateName(state); return stream << ThreadStateName(state);
} }
@@ -35,9 +35,7 @@ public:
globalsThreadQueue_(GlobalsRegistry::Instance()), globalsThreadQueue_(GlobalsRegistry::Instance()),
stableRefThreadQueue_(StableRefRegistry::Instance()), stableRefThreadQueue_(StableRefRegistry::Instance()),
extraObjectDataThreadQueue_(ExtraObjectDataFactory::Instance()), extraObjectDataThreadQueue_(ExtraObjectDataFactory::Instance()),
gcScheduler_(GlobalData::Instance().gcScheduler().NewThreadData()),
gc_(GlobalData::Instance().gc(), *this), gc_(GlobalData::Instance().gc(), *this),
objectFactoryThreadQueue_(GlobalData::Instance().objectFactory(), gc_),
suspensionData_(ThreadState::kNative) {} suspensionData_(ThreadState::kNative) {}
~ThreadData() = default; ~ThreadData() = default;
@@ -56,14 +54,10 @@ public:
ThreadState setState(ThreadState state) noexcept { return suspensionData_.setState(state); } ThreadState setState(ThreadState state) noexcept { return suspensionData_.setState(state); }
ObjectFactory<gc::GC>::ThreadQueue& objectFactoryThreadQueue() noexcept { return objectFactoryThreadQueue_; }
ShadowStack& shadowStack() noexcept { return shadowStack_; } ShadowStack& shadowStack() noexcept { return shadowStack_; }
KStdVector<std::pair<ObjHeader**, ObjHeader*>>& initializingSingletons() noexcept { return initializingSingletons_; } KStdVector<std::pair<ObjHeader**, ObjHeader*>>& initializingSingletons() noexcept { return initializingSingletons_; }
gc::GCSchedulerThreadData& gcScheduler() noexcept { return gcScheduler_; }
gc::GC::ThreadData& gc() noexcept { return gc_; } gc::GC::ThreadData& gc() noexcept { return gc_; }
ThreadSuspensionData& suspensionData() { return suspensionData_; } ThreadSuspensionData& suspensionData() { return suspensionData_; }
@@ -72,15 +66,15 @@ public:
// TODO: These use separate locks, which is inefficient. // TODO: These use separate locks, which is inefficient.
globalsThreadQueue_.Publish(); globalsThreadQueue_.Publish();
stableRefThreadQueue_.Publish(); stableRefThreadQueue_.Publish();
objectFactoryThreadQueue_.Publish();
extraObjectDataThreadQueue_.Publish(); extraObjectDataThreadQueue_.Publish();
gc_.Publish();
} }
void ClearForTests() noexcept { void ClearForTests() noexcept {
globalsThreadQueue_.ClearForTests(); globalsThreadQueue_.ClearForTests();
stableRefThreadQueue_.ClearForTests(); stableRefThreadQueue_.ClearForTests();
objectFactoryThreadQueue_.ClearForTests();
extraObjectDataThreadQueue_.ClearForTests(); extraObjectDataThreadQueue_.ClearForTests();
gc_.ClearForTests();
} }
private: private:
@@ -90,9 +84,7 @@ private:
StableRefRegistry::ThreadQueue stableRefThreadQueue_; StableRefRegistry::ThreadQueue stableRefThreadQueue_;
ExtraObjectDataFactory::ThreadQueue extraObjectDataThreadQueue_; ExtraObjectDataFactory::ThreadQueue extraObjectDataThreadQueue_;
ShadowStack shadowStack_; ShadowStack shadowStack_;
gc::GCSchedulerThreadData gcScheduler_;
gc::GC::ThreadData gc_; gc::GC::ThreadData gc_;
ObjectFactory<gc::GC>::ThreadQueue objectFactoryThreadQueue_;
KStdVector<std::pair<ObjHeader**, ObjHeader*>> initializingSingletons_; KStdVector<std::pair<ObjHeader**, ObjHeader*>> initializingSingletons_;
ThreadSuspensionData suspensionData_; ThreadSuspensionData suspensionData_;
}; };