From 7373183e6f8469f046b262f025ca1dcfb8b671a3 Mon Sep 17 00:00:00 2001 From: Pavel Kunyavskiy Date: Wed, 10 Nov 2021 12:00:25 +0300 Subject: [PATCH] [K/N] Rework scheduler type choosing --- .../kotlin/backend/konan/BinaryOptions.kt | 2 + .../kotlin/backend/konan/GCSchedulerType.kt | 12 ++++++ .../kotlin/backend/konan/KonanConfig.kt | 7 ++++ .../kotlin/backend/konan/llvm/IrToBitcode.kt | 1 + .../runtime/src/gc/common/cpp/GCScheduler.cpp | 29 ++++++++------ .../runtime/src/gc/common/cpp/GCScheduler.hpp | 39 ++++--------------- .../stms/cpp/SameThreadMarkAndSweepTest.cpp | 6 --- .../src/main/cpp/CompilerConstants.cpp | 5 +++ .../src/main/cpp/CompilerConstants.hpp | 9 +++++ .../runtime/src/main/cpp/TestSupport.hpp | 6 ++- .../test_support/cpp/CompilerGenerated.cpp | 1 + 11 files changed, 66 insertions(+), 51 deletions(-) create mode 100644 kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/GCSchedulerType.kt 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 d05190e21c3..f829fce9985 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 @@ -25,6 +25,8 @@ object BinaryOptions : BinaryOptionRegistry() { val androidProgramType by option() val unitSuspendFunctionObjCExport by option() + + val gcSchedulerType by option() } open class BinaryOption( diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/GCSchedulerType.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/GCSchedulerType.kt new file mode 100644 index 00000000000..182d5d66a5c --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/GCSchedulerType.kt @@ -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) +} \ No newline at end of file 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 b1f76b1d65d..4666fc75e5f 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 @@ -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 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 6d2ea643560..9a427b97fcd 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 @@ -2738,6 +2738,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map null SourceInfoType.LIBBACKTRACE -> "Kotlin_getSourceInfo_libbacktrace" diff --git a/kotlin-native/runtime/src/gc/common/cpp/GCScheduler.cpp b/kotlin-native/runtime/src/gc/common/cpp/GCScheduler.cpp index 93700a13f46..ae4b5f3aaf0 100644 --- a/kotlin-native/runtime/src/gc/common/cpp/GCScheduler.cpp +++ b/kotlin-native/runtime/src/gc/common/cpp/GCScheduler.cpp @@ -96,34 +96,39 @@ private: std::function scheduleGC_; }; -} // namespace -KStdUniquePtr gc::internal::MakeEmptyGCSchedulerData() noexcept { +KStdUniquePtr MakeEmptyGCSchedulerData() noexcept { return ::make_unique(); } -KStdUniquePtr gc::internal::MakeGCSchedulerDataWithTimer( - GCSchedulerConfig& config, std::function scheduleGC) noexcept { +KStdUniquePtr MakeGCSchedulerDataWithTimer( + gc::GCSchedulerConfig& config, std::function scheduleGC) noexcept { return ::make_unique(config, std::move(scheduleGC)); } -KStdUniquePtr gc::internal::MakeGCSchedulerDataWithoutTimer( - GCSchedulerConfig& config, std::function scheduleGC, std::function currentTimeCallbackNs) noexcept { +KStdUniquePtr MakeGCSchedulerDataWithoutTimer( + gc::GCSchedulerConfig& config, std::function scheduleGC, std::function currentTimeCallbackNs) noexcept { return ::make_unique(config, std::move(scheduleGC), std::move(currentTimeCallbackNs)); } -KStdUniquePtr gc::internal::MakeGCSchedulerData(GCSchedulerConfig& config, std::function scheduleGC) noexcept { - if (internal::useGCTimer()) { - return MakeGCSchedulerDataWithTimer(config, std::move(scheduleGC)); - } else { - return MakeGCSchedulerDataWithoutTimer(config, std::move(scheduleGC), []() { return konan::getTimeNanos(); }); +} // namespace + +KStdUniquePtr kotlin::gc::MakeGCSchedulerData(SchedulerType type, gc::GCSchedulerConfig& config, std::function 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 scheduleGC) noexcept { RuntimeAssert(static_cast(scheduleGC), "scheduleGC cannot be empty"); RuntimeAssert(!static_cast(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_); } diff --git a/kotlin-native/runtime/src/gc/common/cpp/GCScheduler.hpp b/kotlin-native/runtime/src/gc/common/cpp/GCScheduler.hpp index 28632611a06..971d5d21e53 100644 --- a/kotlin-native/runtime/src/gc/common/cpp/GCScheduler.hpp +++ b/kotlin-native/runtime/src/gc/common/cpp/GCScheduler.hpp @@ -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 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 MakeEmptyGCSchedulerData() noexcept; -KStdUniquePtr MakeGCSchedulerDataWithTimer(GCSchedulerConfig& config, std::function scheduleGC) noexcept; -KStdUniquePtr MakeGCSchedulerDataWithoutTimer( - GCSchedulerConfig& config, std::function scheduleGC, std::function currentTimeCallbackNs) noexcept; -KStdUniquePtr MakeGCSchedulerData(GCSchedulerConfig& config, std::function scheduleGC) noexcept; - -} // namespace internal class GCScheduler : private Pinned { public: @@ -156,23 +137,17 @@ public: return GCSchedulerThreadData(config_, [this](auto& threadData) { gcData_->OnSafePoint(threadData); }); } - template - KStdUniquePtr ReplaceGCSchedulerDataForTests(F&& factory) noexcept { - RuntimeAssert(static_cast(scheduleGC_), "Can only be called after SetScheduleGC"); - - auto other = std::forward(factory)(config_, scheduleGC_); - RuntimeAssert(other != nullptr, "factory cannot return a null"); - using std::swap; - swap(gcData_, other); - return other; - } - private: GCSchedulerConfig config_; KStdUniquePtr gcData_; std::function scheduleGC_; }; +KStdUniquePtr MakeGCSchedulerData( + SchedulerType type, + GCSchedulerConfig& config, + std::function scheduleGC) noexcept; + } // namespace gc } // namespace kotlin diff --git a/kotlin-native/runtime/src/gc/stms/cpp/SameThreadMarkAndSweepTest.cpp b/kotlin-native/runtime/src/gc/stms/cpp/SameThreadMarkAndSweepTest.cpp index 3004cb6ea3b..43a63b3dbb8 100644 --- a/kotlin-native/runtime/src/gc/stms/cpp/SameThreadMarkAndSweepTest.cpp +++ b/kotlin-native/runtime/src/gc/stms/cpp/SameThreadMarkAndSweepTest.cpp @@ -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& finalizerHook() { return finalizerHooks_.finalizerHook(); } diff --git a/kotlin-native/runtime/src/main/cpp/CompilerConstants.cpp b/kotlin-native/runtime/src/main/cpp/CompilerConstants.cpp index 8fb1c3ad1a9..a5892df2444 100644 --- a/kotlin-native/runtime/src/main/cpp/CompilerConstants.cpp +++ b/kotlin-native/runtime/src/main/cpp/CompilerConstants.cpp @@ -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(Kotlin_gcSchedulerType); +} + #ifdef KONAN_ANDROID ALWAYS_INLINE bool compiler::printToAndroidLogcat() noexcept { return Kotlin_printToAndroidLogcat != 0; diff --git a/kotlin-native/runtime/src/main/cpp/CompilerConstants.hpp b/kotlin-native/runtime/src/main/cpp/CompilerConstants.hpp index 02868cf777a..71c485e9bf3 100644 --- a/kotlin-native/runtime/src/main/cpp/CompilerConstants.hpp +++ b/kotlin-native/runtime/src/main/cpp/CompilerConstants.hpp @@ -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 diff --git a/kotlin-native/runtime/src/main/cpp/TestSupport.hpp b/kotlin-native/runtime/src/main/cpp/TestSupport.hpp index 6871c5ff4c2..56509392f6e 100644 --- a/kotlin-native/runtime/src/main/cpp/TestSupport.hpp +++ b/kotlin-native/runtime/src/main/cpp/TestSupport.hpp @@ -3,6 +3,8 @@ * that can be found in the LICENSE file. */ +#pragma once + #include #include @@ -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()); } diff --git a/kotlin-native/runtime/src/test_support/cpp/CompilerGenerated.cpp b/kotlin-native/runtime/src/test_support/cpp/CompilerGenerated.cpp index a4164f906d3..81188ed8543 100644 --- a/kotlin-native/runtime/src/test_support/cpp/CompilerGenerated.cpp +++ b/kotlin-native/runtime/src/test_support/cpp/CompilerGenerated.cpp @@ -66,6 +66,7 @@ extern "C" { extern const int32_t KonanNeedDebugInfo = 1; extern const int32_t Kotlin_runtimeAssertsMode = static_cast(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();