[K/N] Rework scheduler type choosing
This commit is contained in:
+2
@@ -25,6 +25,8 @@ object BinaryOptions : BinaryOptionRegistry() {
|
||||
val androidProgramType by option<AndroidProgramType>()
|
||||
|
||||
val unitSuspendFunctionObjCExport by option<UnitSuspendFunctionObjCExport>()
|
||||
|
||||
val gcSchedulerType by option<GCSchedulerType>()
|
||||
}
|
||||
|
||||
open class BinaryOption<T : Any>(
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.konan
|
||||
|
||||
enum class GCSchedulerType(val value: Int) {
|
||||
DISABLED(0),
|
||||
WITH_TIMER(1),
|
||||
ON_SAFE_POINTS(2)
|
||||
}
|
||||
+7
@@ -104,6 +104,13 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
|
||||
?: SourceInfoType.CORESYMBOLICATION.takeIf { debug && target.supportsCoreSymbolication() }
|
||||
?: SourceInfoType.NOOP
|
||||
|
||||
val gcSchedulerType: GCSchedulerType by lazy {
|
||||
configuration.get(BinaryOptions.gcSchedulerType) ?: when {
|
||||
!target.supportsThreads() -> GCSchedulerType.ON_SAFE_POINTS
|
||||
gcAggressive -> GCSchedulerType.ON_SAFE_POINTS
|
||||
else -> GCSchedulerType.WITH_TIMER
|
||||
}
|
||||
}
|
||||
|
||||
val needVerifyIr: Boolean
|
||||
get() = configuration.get(KonanConfigKeys.VERIFY_IR) == true
|
||||
|
||||
+1
@@ -2738,6 +2738,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
overrideRuntimeGlobal("Kotlin_gcAggressive", Int32(if (context.config.gcAggressive) 1 else 0))
|
||||
overrideRuntimeGlobal("Kotlin_workerExceptionHandling", Int32(context.config.workerExceptionHandling.value))
|
||||
overrideRuntimeGlobal("Kotlin_freezingEnabled", Int32(if (context.config.freezing.enableFreezeAtRuntime) 1 else 0))
|
||||
overrideRuntimeGlobal("Kotlin_gcSchedulerType", Int32(context.config.gcSchedulerType.value))
|
||||
val getSourceInfoFunctionName = when (context.config.sourceInfoType) {
|
||||
SourceInfoType.NOOP -> null
|
||||
SourceInfoType.LIBBACKTRACE -> "Kotlin_getSourceInfo_libbacktrace"
|
||||
|
||||
@@ -96,34 +96,39 @@ private:
|
||||
std::function<void()> scheduleGC_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
KStdUniquePtr<gc::GCSchedulerData> gc::internal::MakeEmptyGCSchedulerData() noexcept {
|
||||
KStdUniquePtr<gc::GCSchedulerData> MakeEmptyGCSchedulerData() noexcept {
|
||||
return ::make_unique<GCEmptySchedulerData>();
|
||||
}
|
||||
|
||||
KStdUniquePtr<gc::GCSchedulerData> gc::internal::MakeGCSchedulerDataWithTimer(
|
||||
GCSchedulerConfig& config, std::function<void()> scheduleGC) noexcept {
|
||||
KStdUniquePtr<gc::GCSchedulerData> MakeGCSchedulerDataWithTimer(
|
||||
gc::GCSchedulerConfig& config, std::function<void()> scheduleGC) noexcept {
|
||||
return ::make_unique<GCSchedulerDataWithTimer>(config, std::move(scheduleGC));
|
||||
}
|
||||
|
||||
KStdUniquePtr<gc::GCSchedulerData> gc::internal::MakeGCSchedulerDataWithoutTimer(
|
||||
GCSchedulerConfig& config, std::function<void()> scheduleGC, std::function<uint64_t()> currentTimeCallbackNs) noexcept {
|
||||
KStdUniquePtr<gc::GCSchedulerData> MakeGCSchedulerDataWithoutTimer(
|
||||
gc::GCSchedulerConfig& config, std::function<void()> scheduleGC, std::function<uint64_t()> currentTimeCallbackNs) noexcept {
|
||||
return ::make_unique<GCSchedulerDataWithoutTimer>(config, std::move(scheduleGC), std::move(currentTimeCallbackNs));
|
||||
}
|
||||
|
||||
KStdUniquePtr<gc::GCSchedulerData> gc::internal::MakeGCSchedulerData(GCSchedulerConfig& config, std::function<void()> scheduleGC) noexcept {
|
||||
if (internal::useGCTimer()) {
|
||||
return MakeGCSchedulerDataWithTimer(config, std::move(scheduleGC));
|
||||
} else {
|
||||
return MakeGCSchedulerDataWithoutTimer(config, std::move(scheduleGC), []() { return konan::getTimeNanos(); });
|
||||
} // namespace
|
||||
|
||||
KStdUniquePtr<gc::GCSchedulerData> kotlin::gc::MakeGCSchedulerData(SchedulerType type, gc::GCSchedulerConfig& config, std::function<void()> scheduleGC) noexcept {
|
||||
switch (type) {
|
||||
case SchedulerType::kDisabled:
|
||||
return MakeEmptyGCSchedulerData();
|
||||
case SchedulerType::kWithTimer:
|
||||
return MakeGCSchedulerDataWithTimer(config, std::move(scheduleGC));
|
||||
case SchedulerType::kOnSafepoints:
|
||||
return MakeGCSchedulerDataWithoutTimer(config, std::move(scheduleGC), []() { return konan::getTimeNanos(); });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void gc::GCScheduler::SetScheduleGC(std::function<void()> scheduleGC) noexcept {
|
||||
RuntimeAssert(static_cast<bool>(scheduleGC), "scheduleGC cannot be empty");
|
||||
RuntimeAssert(!static_cast<bool>(scheduleGC_), "scheduleGC must not have been set");
|
||||
scheduleGC_ = std::move(scheduleGC);
|
||||
RuntimeAssert(gcData_ == nullptr, "gcData_ must not be set prior to scheduleGC call");
|
||||
gcData_ = internal::MakeGCSchedulerData(config_, scheduleGC_);
|
||||
gcData_ = MakeGCSchedulerData(compiler::getGCSchedulerType(), config_, scheduleGC_);
|
||||
}
|
||||
|
||||
@@ -20,18 +20,8 @@
|
||||
namespace kotlin {
|
||||
namespace gc {
|
||||
|
||||
namespace internal {
|
||||
using SchedulerType = compiler::GCSchedulerType;
|
||||
|
||||
inline bool useGCTimer() noexcept {
|
||||
#if KONAN_NO_THREADS
|
||||
return false;
|
||||
#else
|
||||
// With aggressive mode we use safepoint counting to drive GC.
|
||||
return !compiler::gcAggressive();
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
struct GCSchedulerConfig {
|
||||
std::atomic<size_t> threshold = 100000; // Roughly 1 safepoint per 10ms (on a subset of examples on one particular machine).
|
||||
@@ -80,7 +70,7 @@ public:
|
||||
|
||||
// Should be called on encountering a safepoint.
|
||||
void OnSafePointRegular(size_t weight) noexcept {
|
||||
if (!internal::useGCTimer()) {
|
||||
if (compiler::getGCSchedulerType() == compiler::GCSchedulerType::kOnSafepoints) {
|
||||
safePointsCounter_ += weight;
|
||||
if (safePointsCounter_ < safePointsCounterThreshold_) {
|
||||
return;
|
||||
@@ -128,15 +118,6 @@ private:
|
||||
size_t safePointsCounterThreshold_ = 0;
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
|
||||
KStdUniquePtr<GCSchedulerData> MakeEmptyGCSchedulerData() noexcept;
|
||||
KStdUniquePtr<GCSchedulerData> MakeGCSchedulerDataWithTimer(GCSchedulerConfig& config, std::function<void()> scheduleGC) noexcept;
|
||||
KStdUniquePtr<GCSchedulerData> MakeGCSchedulerDataWithoutTimer(
|
||||
GCSchedulerConfig& config, std::function<void()> scheduleGC, std::function<uint64_t()> currentTimeCallbackNs) noexcept;
|
||||
KStdUniquePtr<GCSchedulerData> MakeGCSchedulerData(GCSchedulerConfig& config, std::function<void()> scheduleGC) noexcept;
|
||||
|
||||
} // namespace internal
|
||||
|
||||
class GCScheduler : private Pinned {
|
||||
public:
|
||||
@@ -156,23 +137,17 @@ public:
|
||||
return GCSchedulerThreadData(config_, [this](auto& threadData) { gcData_->OnSafePoint(threadData); });
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
KStdUniquePtr<GCSchedulerData> ReplaceGCSchedulerDataForTests(F&& factory) noexcept {
|
||||
RuntimeAssert(static_cast<bool>(scheduleGC_), "Can only be called after SetScheduleGC");
|
||||
|
||||
auto other = std::forward<F>(factory)(config_, scheduleGC_);
|
||||
RuntimeAssert(other != nullptr, "factory cannot return a null");
|
||||
using std::swap;
|
||||
swap(gcData_, other);
|
||||
return other;
|
||||
}
|
||||
|
||||
private:
|
||||
GCSchedulerConfig config_;
|
||||
KStdUniquePtr<GCSchedulerData> gcData_;
|
||||
std::function<void()> scheduleGC_;
|
||||
};
|
||||
|
||||
KStdUniquePtr<gc::GCSchedulerData> MakeGCSchedulerData(
|
||||
SchedulerType type,
|
||||
GCSchedulerConfig& config,
|
||||
std::function<void()> scheduleGC) noexcept;
|
||||
|
||||
} // namespace gc
|
||||
} // namespace kotlin
|
||||
|
||||
|
||||
@@ -216,17 +216,11 @@ WeakCounter& InstallWeakCounter(mm::ThreadData& threadData, ObjHeader* objHeader
|
||||
|
||||
class SameThreadMarkAndSweepTest : public testing::Test {
|
||||
public:
|
||||
SameThreadMarkAndSweepTest() {
|
||||
mm::GlobalData::Instance().gcScheduler().ReplaceGCSchedulerDataForTests(
|
||||
[](auto& config, auto scheduleGC) { return gc::internal::MakeEmptyGCSchedulerData(); });
|
||||
}
|
||||
|
||||
~SameThreadMarkAndSweepTest() {
|
||||
mm::GlobalsRegistry::Instance().ClearForTests();
|
||||
mm::GlobalData::Instance().extraObjectDataFactory().ClearForTests();
|
||||
mm::GlobalData::Instance().objectFactory().ClearForTests();
|
||||
mm::GlobalData::Instance().gcScheduler().ReplaceGCSchedulerDataForTests(
|
||||
[](auto& config, auto scheduleGC) { return gc::internal::MakeGCSchedulerData(config, std::move(scheduleGC)); });
|
||||
}
|
||||
|
||||
testing::MockFunction<void(ObjHeader*)>& finalizerHook() { return finalizerHooks_.finalizerHook(); }
|
||||
|
||||
@@ -13,6 +13,7 @@ using namespace kotlin;
|
||||
// These are defined by overrideRuntimeGlobals in IrToBitcode.kt
|
||||
RUNTIME_WEAK int32_t Kotlin_destroyRuntimeMode = 1;
|
||||
RUNTIME_WEAK int32_t Kotlin_gcAggressive = 0;
|
||||
RUNTIME_WEAK int32_t Kotlin_gcSchedulerType = 2;
|
||||
RUNTIME_WEAK int32_t Kotlin_workerExceptionHandling = 0;
|
||||
RUNTIME_WEAK int32_t Kotlin_freezingEnabled = 1;
|
||||
RUNTIME_WEAK const Kotlin_getSourceInfo_FunctionType Kotlin_getSourceInfo_Function = nullptr;
|
||||
@@ -36,6 +37,10 @@ ALWAYS_INLINE bool compiler::freezingEnabled() noexcept {
|
||||
return Kotlin_freezingEnabled != 0;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE compiler::GCSchedulerType compiler::getGCSchedulerType() noexcept {
|
||||
return static_cast<compiler::GCSchedulerType>(Kotlin_gcSchedulerType);
|
||||
}
|
||||
|
||||
#ifdef KONAN_ANDROID
|
||||
ALWAYS_INLINE bool compiler::printToAndroidLogcat() noexcept {
|
||||
return Kotlin_printToAndroidLogcat != 0;
|
||||
|
||||
@@ -54,6 +54,13 @@ enum class WorkerExceptionHandling : int32_t {
|
||||
kUseHook = 1,
|
||||
};
|
||||
|
||||
// Must match GCSchedulerType in GCSchedulerType.kt
|
||||
enum class GCSchedulerType {
|
||||
kDisabled = 0,
|
||||
kWithTimer = 1,
|
||||
kOnSafepoints = 2
|
||||
};
|
||||
|
||||
DestroyRuntimeMode destroyRuntimeMode() noexcept;
|
||||
|
||||
bool gcAggressive() noexcept;
|
||||
@@ -82,6 +89,8 @@ ALWAYS_INLINE inline int getSourceInfo(void* addr, SourceInfo *result, int resul
|
||||
}
|
||||
}
|
||||
|
||||
compiler::GCSchedulerType getGCSchedulerType() noexcept;
|
||||
|
||||
#ifdef KONAN_ANDROID
|
||||
bool printToAndroidLogcat() noexcept;
|
||||
#endif
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
|
||||
@@ -31,9 +33,11 @@ public:
|
||||
kotlin::SwitchThreadState(memoryState(), ThreadState::kRunnable);
|
||||
}
|
||||
~ScopedMemoryInit() {
|
||||
// ClearForTests must not be done concurrently with GC
|
||||
SwitchThreadState(memoryState(), ThreadState::kRunnable, /* reentrant = */ true);
|
||||
ClearMemoryForTests(memoryState());
|
||||
// Ensure that memory deinit is performed in the native state.
|
||||
SwitchThreadState(memoryState(), ThreadState::kNative, /* reentrant = */ true);
|
||||
SwitchThreadState(memoryState(), ThreadState::kNative);
|
||||
DeinitMemoryForTests(memoryState());
|
||||
}
|
||||
|
||||
|
||||
@@ -66,6 +66,7 @@ extern "C" {
|
||||
extern const int32_t KonanNeedDebugInfo = 1;
|
||||
extern const int32_t Kotlin_runtimeAssertsMode = static_cast<int32_t>(kotlin::compiler::RuntimeAssertsMode::kPanic);
|
||||
extern const char* const Kotlin_runtimeLogs = nullptr;
|
||||
int32_t Kotlin_gcSchedulerType = 0; // kDisabled
|
||||
|
||||
extern const TypeInfo* theAnyTypeInfo = theAnyTypeInfoHolder.typeInfo();
|
||||
extern const TypeInfo* theArrayTypeInfo = theArrayTypeInfoHolder.typeInfo();
|
||||
|
||||
Reference in New Issue
Block a user