[K/N] Warn gc scheduler before alloc
diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BinaryOptions.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BinaryOptions.kt
index c797fdf..ac63b04 100644
--- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BinaryOptions.kt
+++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BinaryOptions.kt
@@ -66,6 +66,8 @@
val disableMmap by booleanOption()
val disableAllocatorOverheadEstimate by booleanOption()
+
+ val gcBeforeAlloc by booleanOption()
}
open class BinaryOption<T : Any>(
diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt
index 9fd3db3..a0c59dc 100644
--- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt
+++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt
@@ -209,6 +209,10 @@
configuration.get(BinaryOptions.objcDisposeOnMain) ?: true
}
+ val gcBeforeAlloc: Boolean by lazy {
+ configuration.get(BinaryOptions.gcBeforeAlloc) ?: false
+ }
+
init {
if (!platformManager.isEnabled(target)) {
error("Target ${target.visibleName} is not available on the ${HostManager.hostName} host")
diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt
index 54d7a5a..54cc3c8 100644
--- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt
+++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt
@@ -3046,6 +3046,7 @@
setRuntimeConstGlobal("Kotlin_freezingChecksEnabled", llvm.constInt32(if (config.freezing.enableFreezeChecks) 1 else 0))
setRuntimeConstGlobal("Kotlin_concurrentWeakSweep", llvm.constInt32(if (context.config.concurrentWeakSweep) 1 else 0))
setRuntimeConstGlobal("Kotlin_gcMarkSingleThreaded", llvm.constInt32(if (config.gcMarkSingleThreaded) 1 else 0))
+ setRuntimeConstGlobal("Kotlin_gcBeforeAlloc", llvm.constInt32(if (config.gcBeforeAlloc) 1 else 0))
return llvmModule
}
diff --git a/kotlin-native/runtime/src/alloc/custom/cpp/CustomAllocConstants.hpp b/kotlin-native/runtime/src/alloc/custom/cpp/CustomAllocConstants.hpp
index 6fc90e7..bda3f01 100644
--- a/kotlin-native/runtime/src/alloc/custom/cpp/CustomAllocConstants.hpp
+++ b/kotlin-native/runtime/src/alloc/custom/cpp/CustomAllocConstants.hpp
@@ -13,21 +13,19 @@
#include "NextFitPage.hpp"
#include "ExtraObjectPage.hpp"
-inline constexpr const size_t KiB = 1024;
-
-inline constexpr const size_t FIXED_BLOCK_PAGE_SIZE = (256 * KiB);
+inline constexpr const size_t FIXED_BLOCK_PAGE_SIZE = kotlin::alloc::FixedBlockPage::PageSize;
inline constexpr const int FIXED_BLOCK_PAGE_MAX_BLOCK_SIZE = 128;
inline constexpr const size_t FIXED_BLOCK_PAGE_CELL_COUNT =
((FIXED_BLOCK_PAGE_SIZE - sizeof(kotlin::alloc::FixedBlockPage)) / sizeof(kotlin::alloc::FixedBlockCell));
-inline constexpr const size_t NEXT_FIT_PAGE_SIZE = (256 * KiB);
+inline constexpr const size_t NEXT_FIT_PAGE_SIZE = kotlin::alloc::NextFitPage::PageSize;
inline constexpr const size_t NEXT_FIT_PAGE_CELL_COUNT =
((NEXT_FIT_PAGE_SIZE - sizeof(kotlin::alloc::NextFitPage)) / sizeof(kotlin::alloc::Cell));
// NEXT_FIT_PAGE_CELL_COUNT minus one cell for header minus another for the 0-sized dummy block at cells_[0]
inline constexpr const size_t NEXT_FIT_PAGE_MAX_BLOCK_SIZE = (NEXT_FIT_PAGE_CELL_COUNT - 2);
-inline constexpr const size_t EXTRA_OBJECT_PAGE_SIZE = 64 * KiB;
+inline constexpr const size_t EXTRA_OBJECT_PAGE_SIZE = kotlin::alloc::ExtraObjectPage::PageSize;
inline constexpr const int EXTRA_OBJECT_COUNT =
(EXTRA_OBJECT_PAGE_SIZE - sizeof(kotlin::alloc::ExtraObjectPage)) / sizeof(kotlin::alloc::ExtraObjectCell);
diff --git a/kotlin-native/runtime/src/alloc/custom/cpp/ExtraObjectPage.hpp b/kotlin-native/runtime/src/alloc/custom/cpp/ExtraObjectPage.hpp
index 1a2cde1..8f41170 100644
--- a/kotlin-native/runtime/src/alloc/custom/cpp/ExtraObjectPage.hpp
+++ b/kotlin-native/runtime/src/alloc/custom/cpp/ExtraObjectPage.hpp
@@ -36,6 +36,7 @@
class alignas(8) ExtraObjectPage {
public:
using GCSweepScope = gc::GCHandle::GCSweepExtraObjectsScope;
+ static inline constexpr const size_t PageSize = 64 * 1024;
static GCSweepScope currentGCSweepScope(gc::GCHandle& handle) noexcept { return handle.sweepExtraObjects(); }
diff --git a/kotlin-native/runtime/src/alloc/custom/cpp/FixedBlockPage.hpp b/kotlin-native/runtime/src/alloc/custom/cpp/FixedBlockPage.hpp
index 173c7a0..eb23be5 100644
--- a/kotlin-native/runtime/src/alloc/custom/cpp/FixedBlockPage.hpp
+++ b/kotlin-native/runtime/src/alloc/custom/cpp/FixedBlockPage.hpp
@@ -32,6 +32,7 @@
class alignas(8) FixedBlockPage {
public:
using GCSweepScope = gc::GCHandle::GCSweepScope;
+ static inline constexpr const size_t PageSize = 256 * 1024;
static GCSweepScope currentGCSweepScope(gc::GCHandle& handle) noexcept { return handle.sweep(); }
diff --git a/kotlin-native/runtime/src/alloc/custom/cpp/GCApi.cpp b/kotlin-native/runtime/src/alloc/custom/cpp/GCApi.cpp
index 07e992c..097682b 100644
--- a/kotlin-native/runtime/src/alloc/custom/cpp/GCApi.cpp
+++ b/kotlin-native/runtime/src/alloc/custom/cpp/GCApi.cpp
@@ -10,6 +10,9 @@
#include <cstdlib>
#include <cstring>
#include <limits>
+#include "GlobalData.hpp"
+#include "SafePoint.hpp"
+#include "ThreadRegistry.hpp"
#ifndef KONAN_WINDOWS
#include <sys/mman.h>
@@ -24,6 +27,7 @@
#include "GCStatistics.hpp"
#include "KAssert.h"
#include "Memory.h"
+#include "ThreadData.hpp"
namespace {
diff --git a/kotlin-native/runtime/src/alloc/custom/cpp/NextFitPage.hpp b/kotlin-native/runtime/src/alloc/custom/cpp/NextFitPage.hpp
index 3fb13cf..949563e 100644
--- a/kotlin-native/runtime/src/alloc/custom/cpp/NextFitPage.hpp
+++ b/kotlin-native/runtime/src/alloc/custom/cpp/NextFitPage.hpp
@@ -20,6 +20,7 @@
class alignas(8) NextFitPage {
public:
using GCSweepScope = gc::GCHandle::GCSweepScope;
+ static inline constexpr const size_t PageSize = 256 * 1024;
static GCSweepScope currentGCSweepScope(gc::GCHandle& handle) noexcept { return handle.sweep(); }
diff --git a/kotlin-native/runtime/src/alloc/custom/cpp/PageStore.hpp b/kotlin-native/runtime/src/alloc/custom/cpp/PageStore.hpp
index 87050d1..77eec5d 100644
--- a/kotlin-native/runtime/src/alloc/custom/cpp/PageStore.hpp
+++ b/kotlin-native/runtime/src/alloc/custom/cpp/PageStore.hpp
@@ -12,7 +12,9 @@
#include "AtomicStack.hpp"
#include "ExtraObjectPage.hpp"
+#include "GCApi.hpp"
#include "GCStatistics.hpp"
+#include "Memory.h"
namespace kotlin::alloc {
@@ -46,28 +48,34 @@
T* GetPage(uint32_t cellCount, FinalizerQueue& finalizerQueue, std::atomic<std::size_t>& concurrentSweepersCount_) noexcept {
T* page;
- if ((page = ready_.Pop())) {
- used_.Push(page);
- return page;
- }
- {
- auto handle = gc::GCHandle::currentEpoch();
- ScopeGuard counterGuard(
- [&]() { ++concurrentSweepersCount_; },
- [&]() { --concurrentSweepersCount_; }
- );
+ int limit = 1;
+ for (int iter = 0 ; iter < limit ; iter++) {
+ if ((page = ready_.Pop())) {
+ used_.Push(page);
+ return page;
+ }
+ {
+ auto handle = gc::GCHandle::currentEpoch();
+ ScopeGuard counterGuard(
+ [&]() { ++concurrentSweepersCount_; },
+ [&]() { --concurrentSweepersCount_; }
+ );
- if ((page = unswept_.Pop())) {
- // If there're unswept_ pages, the GC is in progress.
- GCSweepScope sweepHandle = T::currentGCSweepScope(*handle);
- if ((page = SweepSingle(sweepHandle, page, unswept_, used_, finalizerQueue))) {
- return page;
+ if ((page = unswept_.Pop())) {
+ // If there're unswept_ pages, the GC is in progress.
+ GCSweepScope sweepHandle = T::currentGCSweepScope(*handle);
+ if ((page = SweepSingle(sweepHandle, page, unswept_, used_, finalizerQueue))) {
+ return page;
+ }
}
}
- }
- if ((page = empty_.Pop())) {
- used_.Push(page);
- return page;
+ if ((page = empty_.Pop())) {
+ used_.Push(page);
+ return page;
+ }
+ if (!used_.isEmpty() && TryGCBeforeMemoryAllocation(T::PageSize)) {
+ limit = 2;
+ }
}
return NewPage(cellCount);
}
diff --git a/kotlin-native/runtime/src/alloc/custom/cpp/SingleObjectPage.hpp b/kotlin-native/runtime/src/alloc/custom/cpp/SingleObjectPage.hpp
index d1d4765..f0cbbad 100644
--- a/kotlin-native/runtime/src/alloc/custom/cpp/SingleObjectPage.hpp
+++ b/kotlin-native/runtime/src/alloc/custom/cpp/SingleObjectPage.hpp
@@ -19,6 +19,7 @@
class alignas(8) SingleObjectPage {
public:
using GCSweepScope = gc::GCHandle::GCSweepScope;
+ static inline constexpr const size_t PageSize = 0; // Not used
static GCSweepScope currentGCSweepScope(gc::GCHandle& handle) noexcept { return handle.sweep(); }
diff --git a/kotlin-native/runtime/src/gc/cms/cpp/ConcurrentMarkAndSweep.cpp b/kotlin-native/runtime/src/gc/cms/cpp/ConcurrentMarkAndSweep.cpp
index eb03de0..f398dcd 100644
--- a/kotlin-native/runtime/src/gc/cms/cpp/ConcurrentMarkAndSweep.cpp
+++ b/kotlin-native/runtime/src/gc/cms/cpp/ConcurrentMarkAndSweep.cpp
@@ -193,6 +193,7 @@
#endif
resumeTheWorld(gcHandle);
+ state_.resumed(epoch);
#ifndef CUSTOM_ALLOCATOR
alloc::SweepExtraObjects<alloc::DefaultSweepTraits<alloc::ObjectFactoryImpl>>(gcHandle, *extraObjectFactoryIterable);
diff --git a/kotlin-native/runtime/src/gc/cms/cpp/GCImpl.cpp b/kotlin-native/runtime/src/gc/cms/cpp/GCImpl.cpp
index e5eb0f5..b51f273 100644
--- a/kotlin-native/runtime/src/gc/cms/cpp/GCImpl.cpp
+++ b/kotlin-native/runtime/src/gc/cms/cpp/GCImpl.cpp
@@ -76,6 +76,10 @@
return impl_->gc().state().schedule();
}
+void gc::GC::WaitResumed(int64_t epoch) noexcept {
+ impl_->gc().state().waitEpochResumed(epoch);
+}
+
void gc::GC::WaitFinished(int64_t epoch) noexcept {
impl_->gc().state().waitEpochFinished(epoch);
}
diff --git a/kotlin-native/runtime/src/gc/common/cpp/GC.hpp b/kotlin-native/runtime/src/gc/common/cpp/GC.hpp
index 1451238..087b0bc 100644
--- a/kotlin-native/runtime/src/gc/common/cpp/GC.hpp
+++ b/kotlin-native/runtime/src/gc/common/cpp/GC.hpp
@@ -76,6 +76,7 @@
// TODO: These should exist only in the scheduler.
int64_t Schedule() noexcept;
+ void WaitResumed(int64_t epoch) noexcept;
void WaitFinished(int64_t epoch) noexcept;
void WaitFinalizers(int64_t epoch) noexcept;
diff --git a/kotlin-native/runtime/src/gc/common/cpp/GCState.hpp b/kotlin-native/runtime/src/gc/common/cpp/GCState.hpp
index db2648d..6ebdedb 100644
--- a/kotlin-native/runtime/src/gc/common/cpp/GCState.hpp
+++ b/kotlin-native/runtime/src/gc/common/cpp/GCState.hpp
@@ -27,6 +27,7 @@
std::unique_lock lock(mutex_);
shutdownFlag_ = true;
startedEpoch.notify();
+ resumedEpoch.notify();
finishedEpoch.notify();
scheduledEpoch.notify();
finalizedEpoch.notify();
@@ -34,10 +35,16 @@
void start(int64_t epoch) { startedEpoch.set(epoch); }
+ void resumed(int64_t epoch) { resumedEpoch.set(epoch); }
+
void finish(int64_t epoch) { finishedEpoch.set(epoch); }
void finalized(int64_t epoch) { finalizedEpoch.set(epoch); }
+ void waitEpochResumed(int64_t epoch) {
+ resumedEpoch.wait([this, epoch] { return *resumedEpoch >= epoch || shutdownFlag_; });
+ }
+
void waitEpochFinished(int64_t epoch) {
finishedEpoch.wait([this, epoch] { return *finishedEpoch >= epoch || shutdownFlag_; });
}
@@ -88,6 +95,7 @@
std::mutex mutex_;
// Use a separate conditional variable for each counter to mitigate a winpthreads bug (see KT-50948 for details).
ValueWithCondVar<int64_t> startedEpoch{0, mutex_};
+ ValueWithCondVar<int64_t> resumedEpoch{0, mutex_};
ValueWithCondVar<int64_t> finishedEpoch{0, mutex_};
ValueWithCondVar<int64_t> scheduledEpoch{0, mutex_};
ValueWithCondVar<int64_t> finalizedEpoch{0, mutex_};
diff --git a/kotlin-native/runtime/src/gc/noop/cpp/GCImpl.cpp b/kotlin-native/runtime/src/gc/noop/cpp/GCImpl.cpp
index 3c62fff..5eff141 100644
--- a/kotlin-native/runtime/src/gc/noop/cpp/GCImpl.cpp
+++ b/kotlin-native/runtime/src/gc/noop/cpp/GCImpl.cpp
@@ -55,6 +55,8 @@
return 0;
}
+void gc::GC::WaitResumed(int64_t epoch) noexcept {}
+
void gc::GC::WaitFinished(int64_t epoch) noexcept {}
void gc::GC::WaitFinalizers(int64_t epoch) noexcept {}
diff --git a/kotlin-native/runtime/src/gc/stms/cpp/GCImpl.cpp b/kotlin-native/runtime/src/gc/stms/cpp/GCImpl.cpp
index 0778689..8210bc9 100644
--- a/kotlin-native/runtime/src/gc/stms/cpp/GCImpl.cpp
+++ b/kotlin-native/runtime/src/gc/stms/cpp/GCImpl.cpp
@@ -69,6 +69,10 @@
return impl_->gc().state().schedule();
}
+void gc::GC::WaitResumed(int64_t epoch) noexcept {
+ impl_->gc().state().waitEpochResumed(epoch);
+}
+
void gc::GC::WaitFinished(int64_t epoch) noexcept {
impl_->gc().state().waitEpochFinished(epoch);
}
diff --git a/kotlin-native/runtime/src/gcScheduler/adaptive/cpp/GCSchedulerImpl.cpp b/kotlin-native/runtime/src/gcScheduler/adaptive/cpp/GCSchedulerImpl.cpp
index 0604154..d845d5f 100644
--- a/kotlin-native/runtime/src/gcScheduler/adaptive/cpp/GCSchedulerImpl.cpp
+++ b/kotlin-native/runtime/src/gcScheduler/adaptive/cpp/GCSchedulerImpl.cpp
@@ -4,6 +4,7 @@
*/
#include "GCSchedulerImpl.hpp"
+#include <cstdint>
#include "GlobalData.hpp"
#include "Memory.h"
@@ -38,6 +39,13 @@
impl().impl().schedule();
}
+void gcScheduler::GCScheduler::scheduleAndWaitResumed() noexcept {
+ RuntimeLogInfo({kTagGC}, "Scheduling GC manually");
+ auto epoch = impl().impl().schedule();
+ NativeOrUnregisteredThreadGuard guard(/* reentrant = */ true);
+ mm::GlobalData::Instance().gc().WaitResumed(epoch);
+}
+
void gcScheduler::GCScheduler::scheduleAndWaitFinished() noexcept {
RuntimeLogInfo({kTagGC}, "Scheduling GC manually");
auto epoch = impl().impl().schedule();
@@ -52,8 +60,8 @@
mm::GlobalData::Instance().gc().WaitFinalizers(epoch);
}
-ALWAYS_INLINE void gcScheduler::GCScheduler::setAllocatedBytes(size_t bytes) noexcept {
- impl().impl().setAllocatedBytes(bytes);
+ALWAYS_INLINE int64_t gcScheduler::GCScheduler::setAllocatedBytes(size_t bytes) noexcept {
+ return impl().impl().setAllocatedBytes(bytes);
}
ALWAYS_INLINE void gcScheduler::GCScheduler::onGCStart() noexcept {
diff --git a/kotlin-native/runtime/src/gcScheduler/adaptive/cpp/GCSchedulerImpl.hpp b/kotlin-native/runtime/src/gcScheduler/adaptive/cpp/GCSchedulerImpl.hpp
index 0d8940e..6a9a9e2 100644
--- a/kotlin-native/runtime/src/gcScheduler/adaptive/cpp/GCSchedulerImpl.hpp
+++ b/kotlin-native/runtime/src/gcScheduler/adaptive/cpp/GCSchedulerImpl.hpp
@@ -5,6 +5,7 @@
#pragma once
+#include <cstdint>
#include "GCScheduler.hpp"
#include "AppStateTracking.hpp"
@@ -67,21 +68,20 @@
timer_.restart(config_.regularGcInterval());
}
- void setAllocatedBytes(size_t bytes) noexcept {
+ int64_t setAllocatedBytes(size_t bytes) noexcept {
auto boundary = heapGrowthController_.boundaryForHeapSize(bytes);
switch (boundary) {
case HeapGrowthController::MemoryBoundary::kNone:
- return;
+ return 0;
case HeapGrowthController::MemoryBoundary::kTrigger:
RuntimeLogDebug({kTagGC}, "Scheduling GC by allocation");
- scheduleGC_.scheduleNextEpochIfNotInProgress();
- return;
+ return scheduleGC_.scheduleNextEpochIfNotInProgress();
case HeapGrowthController::MemoryBoundary::kTarget:
RuntimeLogDebug({kTagGC}, "Scheduling GC by allocation");
auto epoch = scheduleGC_.scheduleNextEpochIfNotInProgress();
RuntimeLogWarning({kTagGC}, "Pausing the mutators until epoch %" PRId64 " is done", epoch);
mutatorAssists_.requestAssists(epoch);
- return;
+ return epoch;
}
}
diff --git a/kotlin-native/runtime/src/gcScheduler/aggressive/cpp/GCSchedulerImpl.cpp b/kotlin-native/runtime/src/gcScheduler/aggressive/cpp/GCSchedulerImpl.cpp
index cb01f97..df97e35 100644
--- a/kotlin-native/runtime/src/gcScheduler/aggressive/cpp/GCSchedulerImpl.cpp
+++ b/kotlin-native/runtime/src/gcScheduler/aggressive/cpp/GCSchedulerImpl.cpp
@@ -4,6 +4,7 @@
*/
#include "GCSchedulerImpl.hpp"
+#include <cstdint>
#include "GlobalData.hpp"
#include "Memory.h"
@@ -39,6 +40,13 @@
impl().impl().schedule();
}
+void gcScheduler::GCScheduler::scheduleAndWaitResumed() noexcept {
+ RuntimeLogInfo({kTagGC}, "Scheduling GC manually");
+ auto epoch = impl().impl().schedule();
+ NativeOrUnregisteredThreadGuard guard(/* reentrant = */ true);
+ mm::GlobalData::Instance().gc().WaitResumed(epoch);
+}
+
void gcScheduler::GCScheduler::scheduleAndWaitFinished() noexcept {
RuntimeLogInfo({kTagGC}, "Scheduling GC manually");
auto epoch = impl().impl().schedule();
@@ -53,8 +61,8 @@
mm::GlobalData::Instance().gc().WaitFinalizers(epoch);
}
-ALWAYS_INLINE void gcScheduler::GCScheduler::setAllocatedBytes(size_t bytes) noexcept {
- impl().impl().setAllocatedBytes(bytes);
+ALWAYS_INLINE int64_t gcScheduler::GCScheduler::setAllocatedBytes(size_t bytes) noexcept {
+ return impl().impl().setAllocatedBytes(bytes);
}
ALWAYS_INLINE void gcScheduler::GCScheduler::onGCStart() noexcept {}
diff --git a/kotlin-native/runtime/src/gcScheduler/aggressive/cpp/GCSchedulerImpl.hpp b/kotlin-native/runtime/src/gcScheduler/aggressive/cpp/GCSchedulerImpl.hpp
index 6ef5cd5..8e4e4cc 100644
--- a/kotlin-native/runtime/src/gcScheduler/aggressive/cpp/GCSchedulerImpl.hpp
+++ b/kotlin-native/runtime/src/gcScheduler/aggressive/cpp/GCSchedulerImpl.hpp
@@ -7,6 +7,7 @@
#include "GCScheduler.hpp"
+#include <cstdint>
#include <functional>
#include "GCSchedulerConfig.hpp"
@@ -47,32 +48,31 @@
RuntimeLogInfo({kTagGC}, "Aggressive GC scheduler initialized");
}
- void setAllocatedBytes(size_t bytes) noexcept {
+ int64_t setAllocatedBytes(size_t bytes) noexcept {
// Still checking allocations: with a long running loop all safepoints
// might be "met", so that's the only trigger to not run out of memory.
auto boundary = heapGrowthController_.boundaryForHeapSize(bytes);
switch (boundary) {
case HeapGrowthController::MemoryBoundary::kNone:
- safePoint();
- return;
+ return safePoint();
case HeapGrowthController::MemoryBoundary::kTrigger:
RuntimeLogDebug({kTagGC}, "Scheduling GC by allocation");
- scheduleGC_.scheduleNextEpochIfNotInProgress();
- return;
+ return scheduleGC_.scheduleNextEpochIfNotInProgress();
case HeapGrowthController::MemoryBoundary::kTarget:
RuntimeLogDebug({kTagGC}, "Scheduling GC by allocation");
auto epoch = scheduleGC_.scheduleNextEpochIfNotInProgress();
RuntimeLogWarning({kTagGC}, "Pausing the mutators");
mutatorAssists_.requestAssists(epoch);
- return;
+ return epoch;
}
}
- void safePoint() noexcept {
+ int64_t safePoint() noexcept {
if (safePointTracker_.registerCurrentSafePoint(1)) {
RuntimeLogDebug({kTagGC}, "Scheduling GC by safepoint");
- schedule();
+ return schedule();
}
+ return 0;
}
void onGCFinish(int64_t epoch, size_t aliveBytes) noexcept {
diff --git a/kotlin-native/runtime/src/gcScheduler/common/cpp/GCScheduler.hpp b/kotlin-native/runtime/src/gcScheduler/common/cpp/GCScheduler.hpp
index 4ea9af5..c449ee0 100644
--- a/kotlin-native/runtime/src/gcScheduler/common/cpp/GCScheduler.hpp
+++ b/kotlin-native/runtime/src/gcScheduler/common/cpp/GCScheduler.hpp
@@ -6,6 +6,7 @@
#pragma once
#include <cstddef>
+#include <cstdint>
#include <memory>
#include <functional>
#include <utility>
@@ -47,11 +48,14 @@
GCSchedulerConfig& config() noexcept { return config_; }
// Called by different mutator threads.
- void setAllocatedBytes(size_t bytes) noexcept;
+ int64_t setAllocatedBytes(size_t bytes) noexcept;
// Can be called by any thread.
void schedule() noexcept;
+ // Can be called by any thread
+ void scheduleAndWaitResumed() noexcept;
+
// Can be called by any thread.
void scheduleAndWaitFinished() noexcept;
diff --git a/kotlin-native/runtime/src/gcScheduler/manual/cpp/GCSchedulerImpl.cpp b/kotlin-native/runtime/src/gcScheduler/manual/cpp/GCSchedulerImpl.cpp
index 110077d..88a7365 100644
--- a/kotlin-native/runtime/src/gcScheduler/manual/cpp/GCSchedulerImpl.cpp
+++ b/kotlin-native/runtime/src/gcScheduler/manual/cpp/GCSchedulerImpl.cpp
@@ -4,6 +4,7 @@
*/
#include "GCSchedulerImpl.hpp"
+#include <cstdint>
#include "CallsChecker.hpp"
#include "GC.hpp"
@@ -27,6 +28,15 @@
mm::GlobalData::Instance().gc().Schedule();
}
+void gcScheduler::GCScheduler::scheduleAndWaitResumed() noexcept {
+ RuntimeLogInfo({kTagGC}, "Scheduling GC manually");
+ CallsCheckerIgnoreGuard guard;
+ auto& gc = mm::GlobalData::Instance().gc();
+ auto epoch = gc.Schedule();
+ NativeOrUnregisteredThreadGuard stateGuard(/* reentrant = */ true);
+ gc.WaitResumed(epoch);
+}
+
void gcScheduler::GCScheduler::scheduleAndWaitFinished() noexcept {
RuntimeLogInfo({kTagGC}, "Scheduling GC manually");
CallsCheckerIgnoreGuard guard;
@@ -45,6 +55,6 @@
gc.WaitFinalizers(epoch);
}
-ALWAYS_INLINE void gcScheduler::GCScheduler::setAllocatedBytes(size_t bytes) noexcept {}
+ALWAYS_INLINE int64_t gcScheduler::GCScheduler::setAllocatedBytes(size_t bytes) noexcept { return 0; }
ALWAYS_INLINE void gcScheduler::GCScheduler::onGCStart() noexcept {}
ALWAYS_INLINE void gcScheduler::GCScheduler::onGCFinish(int64_t epoch, size_t aliveBytes) noexcept {}
diff --git a/kotlin-native/runtime/src/main/cpp/CompilerConstants.hpp b/kotlin-native/runtime/src/main/cpp/CompilerConstants.hpp
index 421045001..adf3e4c 100644
--- a/kotlin-native/runtime/src/main/cpp/CompilerConstants.hpp
+++ b/kotlin-native/runtime/src/main/cpp/CompilerConstants.hpp
@@ -31,6 +31,7 @@
extern "C" const int32_t Kotlin_gcMarkSingleThreaded;
extern "C" const int32_t Kotlin_freezingEnabled;
extern "C" const int32_t Kotlin_freezingChecksEnabled;
+extern "C" const int32_t Kotlin_gcBeforeAlloc;
class SourceInfo;
@@ -103,6 +104,10 @@
}
+ALWAYS_INLINE inline bool gcBeforeAlloc() noexcept {
+ return Kotlin_gcBeforeAlloc != 0;
+}
+
WorkerExceptionHandling workerExceptionHandling() noexcept;
DestroyRuntimeMode destroyRuntimeMode() noexcept;
bool gcMutatorsCooperate() noexcept;
diff --git a/kotlin-native/runtime/src/main/cpp/Memory.h b/kotlin-native/runtime/src/main/cpp/Memory.h
index ee2c113..59699ad 100644
--- a/kotlin-native/runtime/src/main/cpp/Memory.h
+++ b/kotlin-native/runtime/src/main/cpp/Memory.h
@@ -602,6 +602,9 @@
void StartFinalizerThreadIfNeeded() noexcept;
bool FinalizersThreadIsRunning() noexcept;
+// May trigger GC and wait during STW
+bool TryGCBeforeMemoryAllocation(size_t extraBytes) noexcept;
+// May trigger GC, but does not wait
void OnMemoryAllocation(size_t totalAllocatedBytes) noexcept;
void initObjectPool() noexcept;
diff --git a/kotlin-native/runtime/src/mm/cpp/Memory.cpp b/kotlin-native/runtime/src/mm/cpp/Memory.cpp
index cc5be1f..5aea102 100644
--- a/kotlin-native/runtime/src/mm/cpp/Memory.cpp
+++ b/kotlin-native/runtime/src/mm/cpp/Memory.cpp
@@ -4,6 +4,7 @@
*/
#include "Memory.h"
+#include "GlobalData.hpp"
#include "MemoryPrivate.hpp"
#include "Allocator.hpp"
@@ -634,6 +635,19 @@
mm::disposeRegularWeakReferenceImpl(weakRef);
}
+bool kotlin::TryGCBeforeMemoryAllocation(size_t extraBytes) noexcept {
+ if (!compiler::gcBeforeAlloc()) {
+ return false;
+ }
+ size_t totalAllocatedBytes = alloc::allocatedBytes() + extraBytes;
+ auto epoch = mm::GlobalData::Instance().gcScheduler().setAllocatedBytes(totalAllocatedBytes);
+ if (epoch == 0) {
+ return false;
+ }
+ mm::safePoint();
+ return true;
+}
+
void kotlin::OnMemoryAllocation(size_t totalAllocatedBytes) noexcept {
mm::GlobalData::Instance().gcScheduler().setAllocatedBytes(totalAllocatedBytes);
}
diff --git a/kotlin-native/runtime/src/test_support/cpp/CompilerGenerated.cpp b/kotlin-native/runtime/src/test_support/cpp/CompilerGenerated.cpp
index b3a5ab8..813009f 100644
--- a/kotlin-native/runtime/src/test_support/cpp/CompilerGenerated.cpp
+++ b/kotlin-native/runtime/src/test_support/cpp/CompilerGenerated.cpp
@@ -84,6 +84,7 @@
#endif
extern const int32_t Kotlin_freezingChecksEnabled = 1;
extern const int32_t Kotlin_freezingEnabled = 1;
+extern const int32_t Kotlin_gcBeforeAlloc = 0;
extern const TypeInfo* theAnyTypeInfo = theAnyTypeInfoHolder.typeInfo();
extern const TypeInfo* theArrayTypeInfo = theArrayTypeInfoHolder.typeInfo();