diff --git a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt index 61cfb7e85da..267c4777719 100644 --- a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt +++ b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt @@ -250,6 +250,14 @@ class K2Native : CLICompiler() { put(DISABLE_FAKE_OVERRIDE_VALIDATOR, arguments.disableFakeOverrideValidator) putIfNotNull(PRE_LINK_CACHES, parsePreLinkCachesValue(configuration, arguments.preLinkCaches)) putIfNotNull(OVERRIDE_KONAN_PROPERTIES, parseOverrideKonanProperties(arguments, configuration)) + put(DESTROY_RUNTIME_MODE, when (arguments.destroyRuntimeMode) { + "legacy" -> DestroyRuntimeMode.LEGACY + "on-shutdown" -> DestroyRuntimeMode.ON_SHUTDOWN + else -> { + configuration.report(ERROR, "Unsupported destroy runtime mode ${arguments.destroyRuntimeMode}") + DestroyRuntimeMode.ON_SHUTDOWN + } + }) } } } diff --git a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt index e1626f30e83..09dd3304088 100644 --- a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt +++ b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt @@ -285,6 +285,9 @@ class K2NativeCompilerArguments : CommonCompilerArguments() { ) var overrideKonanProperties: Array? = null + @Argument(value="-Xdestroy-runtime-mode", valueDescription = "", description = "When to destroy runtime. 'legacy' and 'on-shutdown' are currently supported. NOTE: 'legacy' mode is deprecated and will be removed.") + var destroyRuntimeMode: String? = "on-shutdown" + override fun configureAnalysisFlags(collector: MessageCollector): MutableMap, Any> = super.configureAnalysisFlags(collector).also { val useExperimental = it[AnalysisFlags.useExperimental] as List<*> diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/DestroyRuntimeMode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/DestroyRuntimeMode.kt new file mode 100644 index 00000000000..e55b9955df1 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/DestroyRuntimeMode.kt @@ -0,0 +1,11 @@ +/* + * Copyright 2010-2020 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. + */ +package org.jetbrains.kotlin.backend.konan + +// Must match `DestroyRuntimeMode` in Runtime.h +enum class DestroyRuntimeMode(val value: Int) { + LEGACY(0), + ON_SHUTDOWN(1), +} diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt index ccfc2ccc813..d1a4b25f86f 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt @@ -45,6 +45,7 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration ?: target.family.isAppleFamily // Default is true for Apple targets. val memoryModel: MemoryModel get() = configuration.get(KonanConfigKeys.MEMORY_MODEL)!! + val destroyRuntimeMode: DestroyRuntimeMode get() = configuration.get(KonanConfigKeys.DESTROY_RUNTIME_MODE)!! val needCompilerVerification: Boolean get() = configuration.get(KonanConfigKeys.VERIFY_COMPILER) ?: @@ -128,6 +129,10 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration configuration.report(CompilerMessageSeverity.STRONG_WARNING, "Experimental memory model requires threads, which are not supported on target ${target.name}. Used strict memory model.") MemoryModel.STRICT + } else if (destroyRuntimeMode == DestroyRuntimeMode.LEGACY) { + configuration.report(CompilerMessageSeverity.STRONG_WARNING, + "Experimental memory model is incompatible with 'legacy' destroy runtime mode. Used strict memory model.") + MemoryModel.STRICT } else { MemoryModel.EXPERIMENTAL } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt index 140a23265be..16b84887046 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt @@ -144,6 +144,8 @@ class KonanConfigKeys { = CompilerConfigurationKey.create("perform compiler caches pre-link") val OVERRIDE_KONAN_PROPERTIES: CompilerConfigurationKey> = CompilerConfigurationKey.create("override konan.properties values") + val DESTROY_RUNTIME_MODE: CompilerConfigurationKey + = CompilerConfigurationKey.create("when to destroy runtime") } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index b6eeb3a3cc5..f2044d3c6fc 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -365,6 +365,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map s.contains("Memory leaks detected, 1 objects leaked!") } } diff --git a/backend.native/tests/interop/migrating_main_thread/lib.kt b/backend.native/tests/interop/migrating_main_thread/lib.kt index afbe1a9f296..f2bc4c99127 100644 --- a/backend.native/tests/interop/migrating_main_thread/lib.kt +++ b/backend.native/tests/interop/migrating_main_thread/lib.kt @@ -13,4 +13,10 @@ fun writeToA(i: Int) { globalA.i = i } -fun readFromA() = globalA.i +fun tryReadFromA(default: Int): Int { + return try { + globalA.i + } catch (e: IncorrectDereferenceException) { + default + } +} diff --git a/backend.native/tests/interop/migrating_main_thread/main.cpp b/backend.native/tests/interop/migrating_main_thread/main.cpp index 40cb53a1a98..35664ea9580 100644 --- a/backend.native/tests/interop/migrating_main_thread/main.cpp +++ b/backend.native/tests/interop/migrating_main_thread/main.cpp @@ -8,17 +8,26 @@ #include #include +constexpr int kInitialValue = 0; +constexpr int kNewValue = 1; +constexpr int kErrorValue = 2; + int main() { std::thread main1([]() { - assert(testlib_symbols()->kotlin.root.readFromA() == 0); - testlib_symbols()->kotlin.root.writeToA(1); - assert(testlib_symbols()->kotlin.root.readFromA() == 1); + assert(testlib_symbols()->kotlin.root.tryReadFromA(kErrorValue) == kInitialValue); + testlib_symbols()->kotlin.root.writeToA(kNewValue); + assert(testlib_symbols()->kotlin.root.tryReadFromA(kErrorValue) == kNewValue); }); main1.join(); std::thread main2([]() { +#if defined(IS_LEGACY) // Globals were reinitialized. - assert(testlib_symbols()->kotlin.root.readFromA() == 0); + assert(testlib_symbols()->kotlin.root.tryReadFromA(kErrorValue) == kInitialValue); +#else + // Globals are not accessible. + assert(testlib_symbols()->kotlin.root.tryReadFromA(kErrorValue) == kErrorValue); +#endif }); main2.join(); diff --git a/backend.native/tests/interop/objc/smoke.kt b/backend.native/tests/interop/objc/smoke.kt index c65be68f0bd..b01f0680854 100644 --- a/backend.native/tests/interop/objc/smoke.kt +++ b/backend.native/tests/interop/objc/smoke.kt @@ -10,6 +10,8 @@ import kotlin.native.ref.* import kotlin.test.* fun main(args: Array) { + // Test relies on full deinitialization at shutdown. + kotlin.native.internal.Debugging.forceCheckedShutdown = true autoreleasepool { run() } diff --git a/build-tools/src/main/kotlin/org/jetbrains/kotlin/KotlinNativeTest.kt b/build-tools/src/main/kotlin/org/jetbrains/kotlin/KotlinNativeTest.kt index 3e06ca43962..cd1420545bf 100644 --- a/build-tools/src/main/kotlin/org/jetbrains/kotlin/KotlinNativeTest.kt +++ b/build-tools/src/main/kotlin/org/jetbrains/kotlin/KotlinNativeTest.kt @@ -419,8 +419,12 @@ open class KonanDynamicTest : KonanStandaloneTest() { @Input lateinit var cSource: String + @Input var clangTool = "clang" + @Input + var clangFlags: List = listOf() + // Replace testlib_api.h and all occurrences of the testlib with the actual name of the test private fun processCSource(): String { val sourceFile = File(cSource) @@ -453,7 +457,7 @@ open class KonanDynamicTest : KonanStandaloneTest() { "-c", "-o", "$executable.o", "-I", artifactsDir - ) + ) + clangFlags it.standardOutput = log it.errorOutput = log it.isIgnoreExitValue = true @@ -485,4 +489,4 @@ open class KonanDynamicTest : KonanStandaloneTest() { it.execute() } } -} \ No newline at end of file +} diff --git a/runtime/src/legacymm/cpp/Memory.cpp b/runtime/src/legacymm/cpp/Memory.cpp index b91b2997eaa..01e3b2dbade 100644 --- a/runtime/src/legacymm/cpp/Memory.cpp +++ b/runtime/src/legacymm/cpp/Memory.cpp @@ -576,7 +576,7 @@ class ForeignRefManager { if (atomicGet(&aliveMemoryStatesCount) == 0) return; - memoryState = InitMemory(); // Required by ReleaseHeapRef. + memoryState = InitMemory(false); // Required by ReleaseHeapRef. } processEnqueuedReleaseRefsWith([](ObjHeader* obj) { @@ -585,7 +585,7 @@ class ForeignRefManager { if (hadNoStateInitialized) { // Discard the memory state. - DeinitMemory(memoryState); + DeinitMemory(memoryState, false); } } } @@ -1978,7 +1978,7 @@ void deinitForeignRef(ObjHeader* object, ForeignRefManager* manager) { } } -MemoryState* initMemory() { +MemoryState* initMemory(bool firstRuntime) { RuntimeAssert(offsetof(ArrayHeader, typeInfoOrMeta_) == offsetof(ObjHeader, typeInfoOrMeta_), @@ -2005,7 +2005,15 @@ MemoryState* initMemory() { memoryState->tlsMap = konanConstructInstance(); memoryState->foreignRefManager = ForeignRefManager::create(); bool firstMemoryState = atomicAdd(&aliveMemoryStatesCount, 1) == 1; - if (firstMemoryState) { + switch (Kotlin_getDestroyRuntimeMode()) { + case DESTROY_RUNTIME_LEGACY: + firstRuntime = firstMemoryState; + break; + case DESTROY_RUNTIME_ON_SHUTDOWN: + // Nothing to do. + break; + } + if (firstRuntime) { #if USE_CYCLIC_GC cyclicInit(); #endif // USE_CYCLIC_GC @@ -2014,13 +2022,21 @@ MemoryState* initMemory() { return memoryState; } -void deinitMemory(MemoryState* memoryState) { +void deinitMemory(MemoryState* memoryState, bool destroyRuntime) { static int pendingDeinit = 0; atomicAdd(&pendingDeinit, 1); #if USE_GC bool lastMemoryState = atomicAdd(&aliveMemoryStatesCount, -1) == 0; - bool checkLeaks = Kotlin_memoryLeakCheckerEnabled() && lastMemoryState; - if (lastMemoryState) { + switch (Kotlin_getDestroyRuntimeMode()) { + case DESTROY_RUNTIME_LEGACY: + destroyRuntime = lastMemoryState; + break; + case DESTROY_RUNTIME_ON_SHUTDOWN: + // Nothing to do + break; + } + bool checkLeaks = Kotlin_memoryLeakCheckerEnabled() && destroyRuntime; + if (destroyRuntime) { garbageCollect(memoryState, true); #if USE_CYCLIC_GC // If there are other pending deinits (rare situation) - just skip the leak checker. @@ -2051,7 +2067,7 @@ void deinitMemory(MemoryState* memoryState) { atomicAdd(&pendingDeinit, -1); #if TRACE_MEMORY - if (IsStrictMemoryModel && lastMemoryState && allocCount > 0) { + if (IsStrictMemoryModel && destroyRuntime && allocCount > 0) { MEMORY_LOG("*** Memory leaks, leaked %d containers ***\n", allocCount); dumpReachable("", memoryState->containers); } @@ -3231,12 +3247,12 @@ void AdoptReferenceFromSharedVariable(ObjHeader* object) { } // Public memory interface. -MemoryState* InitMemory() { - return initMemory(); +MemoryState* InitMemory(bool firstRuntime) { + return initMemory(firstRuntime); } -void DeinitMemory(MemoryState* memoryState) { - deinitMemory(memoryState); +void DeinitMemory(MemoryState* memoryState, bool destroyRuntime) { + deinitMemory(memoryState, destroyRuntime); } void RestoreMemory(MemoryState* memoryState) { diff --git a/runtime/src/main/cpp/Memory.h b/runtime/src/main/cpp/Memory.h index 46b45c36b05..be5ab005796 100644 --- a/runtime/src/main/cpp/Memory.h +++ b/runtime/src/main/cpp/Memory.h @@ -122,8 +122,8 @@ extern "C" { struct MemoryState; -MemoryState* InitMemory(); -void DeinitMemory(MemoryState*); +MemoryState* InitMemory(bool firstRuntime); +void DeinitMemory(MemoryState*, bool destroyRuntime); void RestoreMemory(MemoryState*); // diff --git a/runtime/src/main/cpp/Runtime.cpp b/runtime/src/main/cpp/Runtime.cpp index 40d45e493c0..9c81ded50df 100644 --- a/runtime/src/main/cpp/Runtime.cpp +++ b/runtime/src/main/cpp/Runtime.cpp @@ -31,6 +31,13 @@ struct InitNode { InitNode* next; }; +// This global is overriden by the compiler. +RUNTIME_WEAK DestroyRuntimeMode Kotlin_destroyRuntimeMode = DESTROY_RUNTIME_ON_SHUTDOWN; + +DestroyRuntimeMode Kotlin_getDestroyRuntimeMode() { + return Kotlin_destroyRuntimeMode; +} + namespace { InitNode* initHeadNode = nullptr; @@ -65,6 +72,7 @@ void InitOrDeinitGlobalVariables(int initialize, MemoryState* memory) { KBoolean g_checkLeaks = KonanNeedDebugInfo; KBoolean g_checkLeakedCleaners = KonanNeedDebugInfo; +KBoolean g_forceCheckedShutdown = false; constexpr RuntimeState* kInvalidRuntime = nullptr; @@ -91,11 +99,27 @@ RuntimeState* initRuntime() { RuntimeCheck(!isValidRuntime(), "No active runtimes allowed"); ::runtimeState = result; - compareAndSwap(&globalRuntimeStatus, kGlobalRuntimeUninitialized, kGlobalRuntimeRunning); + bool firstRuntime = false; + switch (Kotlin_getDestroyRuntimeMode()) { + case DESTROY_RUNTIME_LEGACY: + compareAndSwap(&globalRuntimeStatus, kGlobalRuntimeUninitialized, kGlobalRuntimeRunning); + result->memoryState = InitMemory(false); // The argument will be ignored for legacy DestroyRuntimeMode + result->worker = WorkerInit(true); + firstRuntime = atomicAdd(&aliveRuntimesCount, 1) == 1; + break; + case DESTROY_RUNTIME_ON_SHUTDOWN: + // First update `aliveRuntimesCount` and then update `globalRuntimeStatus`, for synchronization with + // runtime shutdown, which does it the other way around. + atomicAdd(&aliveRuntimesCount, 1); + auto lastStatus = compareAndSwap(&globalRuntimeStatus, kGlobalRuntimeUninitialized, kGlobalRuntimeRunning); + if (Kotlin_forceCheckedShutdown()) { + RuntimeAssert(lastStatus != kGlobalRuntimeShutdown, "Kotlin runtime was shut down. Cannot create new runtimes."); + } + firstRuntime = lastStatus == kGlobalRuntimeUninitialized; + result->memoryState = InitMemory(firstRuntime); + result->worker = WorkerInit(true); + } - result->memoryState = InitMemory(); - result->worker = WorkerInit(true); - bool firstRuntime = atomicAdd(&aliveRuntimesCount, 1) == 1; // Keep global variables in state as well. if (firstRuntime) { konan::consoleInit(); @@ -110,25 +134,33 @@ RuntimeState* initRuntime() { return result; } -void deinitRuntime(RuntimeState* state) { +void deinitRuntime(RuntimeState* state, bool destroyRuntime) { RuntimeAssert(state->status == RuntimeStatus::kRunning, "Runtime must be in the running state"); state->status = RuntimeStatus::kDestroying; // This may be called after TLS is zeroed out, so ::memoryState in Memory cannot be trusted. RestoreMemory(state->memoryState); bool lastRuntime = atomicAdd(&aliveRuntimesCount, -1) == 0; + switch (Kotlin_getDestroyRuntimeMode()) { + case DESTROY_RUNTIME_LEGACY: + destroyRuntime = lastRuntime; + break; + case DESTROY_RUNTIME_ON_SHUTDOWN: + // Nothing to do. + break; + } InitOrDeinitGlobalVariables(DEINIT_THREAD_LOCAL_GLOBALS, state->memoryState); - if (lastRuntime) + if (destroyRuntime) InitOrDeinitGlobalVariables(DEINIT_GLOBALS, state->memoryState); auto workerId = GetWorkerId(state->worker); WorkerDeinit(state->worker); - DeinitMemory(state->memoryState); + DeinitMemory(state->memoryState, destroyRuntime); konanDestructInstance(state); WorkerDestroyThreadDataIfNeeded(workerId); } void Kotlin_deinitRuntimeCallback(void* argument) { auto* state = reinterpret_cast(argument); - deinitRuntime(state); + deinitRuntime(state, false); } } // namespace @@ -155,17 +187,31 @@ void Kotlin_initRuntimeIfNeeded() { void Kotlin_deinitRuntimeIfNeeded() { if (isValidRuntime()) { - deinitRuntime(::runtimeState); + deinitRuntime(::runtimeState, false); ::runtimeState = kInvalidRuntime; } } // TODO: Consider exporting it to interop API. void Kotlin_shutdownRuntime() { - // TODO: If checkers are disabled, we can set status to "shutdown" here, and return. auto* runtime = ::runtimeState; RuntimeAssert(runtime != kInvalidRuntime, "Current thread must have Kotlin runtime initialized on it"); + bool needsFullShutdown = false; + switch (Kotlin_getDestroyRuntimeMode()) { + case DESTROY_RUNTIME_LEGACY: + needsFullShutdown = true; + break; + case DESTROY_RUNTIME_ON_SHUTDOWN: + needsFullShutdown = Kotlin_forceCheckedShutdown() || Kotlin_memoryLeakCheckerEnabled() || Kotlin_cleanersLeakCheckerEnabled(); + break; + } + if (!needsFullShutdown) { + auto lastStatus = compareAndSwap(&globalRuntimeStatus, kGlobalRuntimeRunning, kGlobalRuntimeShutdown); + RuntimeAssert(lastStatus == kGlobalRuntimeRunning, "Invalid runtime status for shutdown"); + return; + } + if (Kotlin_cleanersLeakCheckerEnabled()) { // Make sure to collect any lingering cleaners. PerformFullGC(runtime->memoryState); @@ -178,13 +224,23 @@ void Kotlin_shutdownRuntime() { auto lastStatus = compareAndSwap(&globalRuntimeStatus, kGlobalRuntimeRunning, kGlobalRuntimeShutdown); RuntimeAssert(lastStatus == kGlobalRuntimeRunning, "Invalid runtime status for shutdown"); - // TODO: If we add early return at the top, this if would be unneeded. - if (Kotlin_memoryLeakCheckerEnabled() || Kotlin_cleanersLeakCheckerEnabled()) { + // TODO: When legacy mode is gone, this `if` will become unnecessary. + if (Kotlin_forceCheckedShutdown() || Kotlin_memoryLeakCheckerEnabled() || Kotlin_cleanersLeakCheckerEnabled()) { // First make sure workers are gone. WaitNativeWorkersTermination(); + + if (Kotlin_forceCheckedShutdown()) { + // Now check for existence of any other runtimes. + auto otherRuntimesCount = atomicGet(&aliveRuntimesCount) - 1; + RuntimeAssert(otherRuntimesCount >= 0, "Cannot be negative"); + if (otherRuntimesCount > 0) { + konan::consoleErrorf("Cannot run checkers when there are %d alive runtimes at the shutdown", otherRuntimesCount); + konan::abort(); + } + } } - deinitRuntime(runtime); + deinitRuntime(runtime, true); ::runtimeState = kInvalidRuntime; } @@ -280,4 +336,23 @@ void Konan_Platform_setCleanersLeakChecker(KBoolean value) { g_checkLeakedCleaners = value; } +bool Kotlin_forceCheckedShutdown() { + return g_forceCheckedShutdown; +} + +KBoolean Kotlin_Debugging_getForceCheckedShutdown() { + return g_forceCheckedShutdown; +} + +void Kotlin_Debugging_setForceCheckedShutdown(KBoolean value) { + switch (Kotlin_getDestroyRuntimeMode()) { + case DESTROY_RUNTIME_LEGACY: + // Only applicable to ON_SHUTDOWN modes. + return; + case DESTROY_RUNTIME_ON_SHUTDOWN: + break; + } + g_forceCheckedShutdown = value; +} + } // extern "C" diff --git a/runtime/src/main/cpp/Runtime.h b/runtime/src/main/cpp/Runtime.h index 2b4e6d152ed..d5779db9b10 100644 --- a/runtime/src/main/cpp/Runtime.h +++ b/runtime/src/main/cpp/Runtime.h @@ -25,6 +25,14 @@ struct InitNode; extern "C" { #endif +// Must match DestroyRuntimeMode in DestroyRuntimeMode.kt +enum DestroyRuntimeMode { + DESTROY_RUNTIME_LEGACY = 0, + DESTROY_RUNTIME_ON_SHUTDOWN = 1, +}; + +DestroyRuntimeMode Kotlin_getDestroyRuntimeMode(); + void Kotlin_initRuntimeIfNeeded(); void Kotlin_deinitRuntimeIfNeeded(); @@ -41,6 +49,8 @@ bool Kotlin_memoryLeakCheckerEnabled(); bool Kotlin_cleanersLeakCheckerEnabled(); +bool Kotlin_forceCheckedShutdown(); + #ifdef __cplusplus } #endif diff --git a/runtime/src/main/kotlin/kotlin/native/internal/Debugging.kt b/runtime/src/main/kotlin/kotlin/native/internal/Debugging.kt new file mode 100644 index 00000000000..638e0363df9 --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/native/internal/Debugging.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2010-2020 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. + */ +package kotlin.native.internal + +/* + * Internal utilities for debugging K/N compiler and runtime. + */ +public object Debugging { + public var forceCheckedShutdown: Boolean + get() = Debugging_getForceCheckedShutdown() + set(value) = Debugging_setForceCheckedShutdown(value) +} + +@SymbolName("Kotlin_Debugging_getForceCheckedShutdown") +private external fun Debugging_getForceCheckedShutdown(): Boolean + +@SymbolName("Kotlin_Debugging_setForceCheckedShutdown") +private external fun Debugging_setForceCheckedShutdown(value: Boolean): Unit diff --git a/runtime/src/mm/cpp/Stubs.cpp b/runtime/src/mm/cpp/Stubs.cpp index cfc6582cd1d..c0b6f7b2019 100644 --- a/runtime/src/mm/cpp/Stubs.cpp +++ b/runtime/src/mm/cpp/Stubs.cpp @@ -47,11 +47,11 @@ static void destroyMetaObject(TypeInfo** location) { extern "C" { -MemoryState* InitMemory() { +MemoryState* InitMemory(bool firstRuntime) { RuntimeCheck(false, "Unimplemented"); } -void DeinitMemory(MemoryState*) { +void DeinitMemory(MemoryState*, bool destroyRuntime) { RuntimeCheck(false, "Unimplemented"); }