Skip to content

Commit db2475f

Browse files
projediSpace Team
authored andcommitted
[K/N] Make STMS use separate threads for GC and finalizers.
Moves GC into a separate thread, and finalizers processing into yet another thread. This makes STMS more similar to CMS.
1 parent 6996e03 commit db2475f

17 files changed

+441
-594
lines changed

kotlin-native/runtime/src/custom_alloc/cpp/AtomicStack.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ class AtomicStack: private kotlin::MoveOnly {
6767
} while (!stack_.compare_exchange_weak(thisHead, otherHead, std::memory_order_acq_rel));
6868
}
6969

70-
bool isEmpty() noexcept { return stack_.load(std::memory_order_relaxed) == nullptr; }
70+
bool isEmpty() const noexcept { return stack_.load(std::memory_order_relaxed) == nullptr; }
7171

7272
// Not thread-safe. Named like this to make AtomicStack compatible with FinalizerQueue
7373
size_t size() {

kotlin-native/runtime/src/custom_alloc/cpp/CustomFinalizerProcessor.cpp

Lines changed: 0 additions & 108 deletions
This file was deleted.

kotlin-native/runtime/src/custom_alloc/cpp/CustomFinalizerProcessor.hpp

Lines changed: 16 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,41 +6,27 @@
66
#ifndef CUSTOM_ALLOC_CPP_CUSTOMFINALIZERPROCESSOR_HPP_
77
#define CUSTOM_ALLOC_CPP_CUSTOMFINALIZERPROCESSOR_HPP_
88

9-
#include <cstdint>
10-
#include <condition_variable>
11-
129
#include "AtomicStack.hpp"
10+
#include "ExtraObjectData.hpp"
1311
#include "ExtraObjectPage.hpp"
14-
#include "ScopedThread.hpp"
12+
#include "FinalizerHooks.hpp"
1513

1614
namespace kotlin::alloc {
1715

18-
class CustomFinalizerProcessor : Pinned {
19-
public:
20-
using Queue = typename kotlin::alloc::AtomicStack<kotlin::alloc::ExtraObjectCell>;
21-
explicit CustomFinalizerProcessor(std::function<void(int64_t)> epochDoneCallback) : epochDoneCallback_(std::move(epochDoneCallback)) {}
22-
void ScheduleTasks(Queue&& tasks, int64_t epoch) noexcept;
23-
void StopFinalizerThread() noexcept;
24-
bool IsRunning() noexcept;
25-
void StartFinalizerThreadIfNone() noexcept;
26-
void WaitFinalizerThreadInitialized() noexcept;
27-
~CustomFinalizerProcessor();
28-
29-
private:
30-
ScopedThread finalizerThread_;
31-
Queue finalizerQueue_;
32-
std::condition_variable finalizerQueueCondVar_;
33-
std::mutex finalizerQueueMutex_;
34-
std::function<void(int64_t)> epochDoneCallback_;
35-
int64_t finalizerQueueEpoch_ = 0;
36-
bool shutdownFlag_ = false;
37-
bool newTasksAllowed_ = true;
38-
39-
std::mutex initializedMutex_;
40-
std::condition_variable initializedCondVar_;
41-
bool initialized_ = false;
42-
43-
std::mutex threadCreatingMutex_;
16+
using FinalizerQueue = kotlin::alloc::AtomicStack<kotlin::alloc::ExtraObjectCell>;
17+
18+
struct FinalizerQueueTraits {
19+
static bool isEmpty(const FinalizerQueue& queue) noexcept { return queue.isEmpty(); }
20+
21+
static void add(FinalizerQueue& into, FinalizerQueue from) noexcept { into.TransferAllFrom(std::move(from)); }
22+
23+
static void process(FinalizerQueue queue) noexcept {
24+
while (auto* cell = queue.Pop()) {
25+
auto* extraObject = cell->Data();
26+
auto* baseObject = extraObject->GetBaseObject();
27+
RunFinalizers(baseObject);
28+
}
29+
}
4430
};
4531

4632
} // namespace kotlin::alloc

kotlin-native/runtime/src/gc/cms/cpp/ConcurrentMarkAndSweep.cpp

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,9 @@
1919
#include "ThreadRegistry.hpp"
2020
#include "ThreadSuspension.hpp"
2121
#include "GCState.hpp"
22-
#include "FinalizerProcessor.hpp"
2322
#include "GCStatistics.hpp"
2423

2524
#ifdef CUSTOM_ALLOCATOR
26-
#include "CustomFinalizerProcessor.hpp"
2725
#include "Heap.hpp"
2826
#endif
2927

@@ -113,15 +111,14 @@ void gc::ConcurrentMarkAndSweep::ThreadData::OnSuspendForGC() noexcept {
113111
gc::ConcurrentMarkAndSweep::ConcurrentMarkAndSweep(
114112
mm::ObjectFactory<ConcurrentMarkAndSweep>& objectFactory, GCScheduler& gcScheduler) noexcept :
115113
objectFactory_(objectFactory),
116-
gcScheduler_(gcScheduler),
117-
finalizerProcessor_(std_support::make_unique<FinalizerProcessor>([this](int64_t epoch) {
118114
#else
119115
gc::ConcurrentMarkAndSweep::ConcurrentMarkAndSweep(GCScheduler& gcScheduler) noexcept :
120-
gcScheduler_(gcScheduler), finalizerProcessor_(std_support::make_unique<alloc::CustomFinalizerProcessor>([this](int64_t epoch) {
121116
#endif
117+
gcScheduler_(gcScheduler),
118+
finalizerProcessor_([this](int64_t epoch) {
122119
GCHandle::getByEpoch(epoch).finalizersDone();
123120
state_.finalized(epoch);
124-
})) {
121+
}) {
125122
gcScheduler_.SetScheduleGC([this]() NO_INLINE {
126123
// This call acquires a lock, so we need to ensure that we're in the safe state.
127124
NativeOrUnregisteredThreadGuard guard(/* reentrant = */ true);
@@ -147,31 +144,31 @@ gc::ConcurrentMarkAndSweep::~ConcurrentMarkAndSweep() {
147144

148145
void gc::ConcurrentMarkAndSweep::StartFinalizerThreadIfNeeded() noexcept {
149146
NativeOrUnregisteredThreadGuard guard(true);
150-
finalizerProcessor_->StartFinalizerThreadIfNone();
151-
finalizerProcessor_->WaitFinalizerThreadInitialized();
147+
finalizerProcessor_.StartFinalizerThreadIfNone();
148+
finalizerProcessor_.WaitFinalizerThreadInitialized();
152149
}
153150

154151
void gc::ConcurrentMarkAndSweep::StopFinalizerThreadIfRunning() noexcept {
155152
NativeOrUnregisteredThreadGuard guard(true);
156-
finalizerProcessor_->StopFinalizerThread();
153+
finalizerProcessor_.StopFinalizerThread();
157154
}
158155

159156
bool gc::ConcurrentMarkAndSweep::FinalizersThreadIsRunning() noexcept {
160-
return finalizerProcessor_->IsRunning();
157+
return finalizerProcessor_.IsRunning();
161158
}
162159

163160
void gc::ConcurrentMarkAndSweep::SetMarkingBehaviorForTests(MarkingBehavior markingBehavior) noexcept {
164161
markingBehavior_ = markingBehavior;
165162
}
166163

167-
bool gc::ConcurrentMarkAndSweep::PerformFullGC(int64_t epoch) noexcept {
164+
void gc::ConcurrentMarkAndSweep::PerformFullGC(int64_t epoch) noexcept {
168165
auto gcHandle = GCHandle::create(epoch);
169166
SetMarkingRequested(epoch);
170167
bool didSuspend = mm::RequestThreadsSuspension();
171168
RuntimeAssert(didSuspend, "Only GC thread can request suspension");
172169
gcHandle.suspensionRequested();
173170

174-
RuntimeAssert(!kotlin::mm::IsCurrentThreadRegistered(), "Concurrent GC must run on unregistered thread");
171+
RuntimeAssert(!kotlin::mm::IsCurrentThreadRegistered(), "GC must run on unregistered thread");
175172
WaitForThreadsReadyToMark();
176173
gcHandle.threadsAreSuspended();
177174

@@ -218,11 +215,11 @@ bool gc::ConcurrentMarkAndSweep::PerformFullGC(int64_t epoch) noexcept {
218215
state_.finish(epoch);
219216
gcHandle.finalizersScheduled(finalizerQueue.size());
220217
gcHandle.finished();
218+
221219
// This may start a new thread. On some pthreads implementations, this may block waiting for concurrent thread
222220
// destructors running. So, it must ensured that no locks are held by this point.
223221
// TODO: Consider having an always on sleeping finalizer thread.
224-
finalizerProcessor_->ScheduleTasks(std::move(finalizerQueue), epoch);
225-
return true;
222+
finalizerProcessor_.ScheduleTasks(std::move(finalizerQueue), epoch);
226223
}
227224

228225
namespace {

kotlin-native/runtime/src/gc/cms/cpp/ConcurrentMarkAndSweep.hpp

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,34 +9,28 @@
99
#include <cstddef>
1010

1111
#include "Allocator.hpp"
12+
#include "FinalizerProcessor.hpp"
1213
#include "GCScheduler.hpp"
14+
#include "GCState.hpp"
15+
#include "GCStatistics.hpp"
1316
#include "IntrusiveList.hpp"
1417
#include "MarkAndSweepUtils.hpp"
1518
#include "ObjectFactory.hpp"
1619
#include "ScopedThread.hpp"
1720
#include "ThreadData.hpp"
1821
#include "Types.h"
1922
#include "Utils.hpp"
20-
#include "GCState.hpp"
2123
#include "std_support/Memory.hpp"
22-
#include "GCStatistics.hpp"
2324

2425
#ifdef CUSTOM_ALLOCATOR
2526
#include "CustomAllocator.hpp"
27+
#include "CustomFinalizerProcessor.hpp"
2628
#include "Heap.hpp"
27-
28-
namespace kotlin::alloc {
29-
class CustomFinalizerProcessor;
30-
}
3129
#endif
3230

3331
namespace kotlin {
3432
namespace gc {
3533

36-
#ifndef CUSTOM_ALLOCATOR
37-
class FinalizerProcessor;
38-
#endif
39-
4034
// Stop-the-world parallel mark + concurrent sweep. The GC runs in a separate thread, finalizers run in another thread of their own.
4135
// TODO: Also make marking run concurrently with Kotlin threads.
4236
class ConcurrentMarkAndSweep : private Pinned {
@@ -108,6 +102,14 @@ class ConcurrentMarkAndSweep : private Pinned {
108102

109103
using Allocator = ThreadData::Allocator;
110104

105+
#ifndef CUSTOM_ALLOCATOR
106+
using FinalizerQueue = mm::ObjectFactory<ConcurrentMarkAndSweep>::FinalizerQueue;
107+
using FinalizerQueueTraits = mm::ObjectFactory<ConcurrentMarkAndSweep>::FinalizerQueueTraits;
108+
#else
109+
using FinalizerQueue = alloc::FinalizerQueue;
110+
using FinalizerQueueTraits = alloc::FinalizerQueueTraits;
111+
#endif
112+
111113
#ifdef CUSTOM_ALLOCATOR
112114
explicit ConcurrentMarkAndSweep(GCScheduler& scheduler) noexcept;
113115
#else
@@ -128,8 +130,7 @@ class ConcurrentMarkAndSweep : private Pinned {
128130
#endif
129131

130132
private:
131-
// Returns `true` if GC has happened, and `false` if not (because someone else has suspended the threads).
132-
bool PerformFullGC(int64_t epoch) noexcept;
133+
void PerformFullGC(int64_t epoch) noexcept;
133134

134135
#ifndef CUSTOM_ALLOCATOR
135136
mm::ObjectFactory<ConcurrentMarkAndSweep>& objectFactory_;
@@ -140,11 +141,7 @@ class ConcurrentMarkAndSweep : private Pinned {
140141

141142
GCStateHolder state_;
142143
ScopedThread gcThread_;
143-
#ifndef CUSTOM_ALLOCATOR
144-
std_support::unique_ptr<FinalizerProcessor> finalizerProcessor_;
145-
#else
146-
std_support::unique_ptr<alloc::CustomFinalizerProcessor> finalizerProcessor_;
147-
#endif
144+
FinalizerProcessor<FinalizerQueue, FinalizerQueueTraits> finalizerProcessor_;
148145

149146
MarkQueue markQueue_;
150147
MarkingBehavior markingBehavior_;

0 commit comments

Comments
 (0)