Fix unchecked runtime shutdown (#4575)
This commit is contained in:
committed by
Stanislav Erokhin
parent
b6497d07cf
commit
783517dbe8
@@ -3752,6 +3752,12 @@ createInterop("kt43265") {
|
|||||||
it.defFile 'interop/kt43265/kt43265.def'
|
it.defFile 'interop/kt43265/kt43265.def'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createInterop("leakMemoryWithRunningThread") {
|
||||||
|
it.defFile 'interop/leakMemoryWithRunningThread/leakMemory.def'
|
||||||
|
it.headers "$projectDir/interop/leakMemoryWithRunningThread/leakMemory.h"
|
||||||
|
it.extraOpts "-Xcompile-source", "$projectDir/interop/leakMemoryWithRunningThread/leakMemory.cpp"
|
||||||
|
}
|
||||||
|
|
||||||
if (PlatformInfo.isAppleTarget(project)) {
|
if (PlatformInfo.isAppleTarget(project)) {
|
||||||
createInterop("objcSmoke") {
|
createInterop("objcSmoke") {
|
||||||
it.defFile 'interop/objc/objcSmoke.def'
|
it.defFile 'interop/objc/objcSmoke.def'
|
||||||
@@ -4061,6 +4067,22 @@ interopTest("interop_kt43265") {
|
|||||||
source = "interop/kt43265/usage.kt"
|
source = "interop/kt43265/usage.kt"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interopTest("interop_leakMemoryWithRunningThreadUnchecked") {
|
||||||
|
disabled = project.globalTestArgs.contains('-opt') || (project.testTarget == 'wasm32') // Needs debug build.
|
||||||
|
interop = 'leakMemoryWithRunningThread'
|
||||||
|
source = "interop/leakMemoryWithRunningThread/unchecked.kt"
|
||||||
|
flags = ['-g']
|
||||||
|
}
|
||||||
|
|
||||||
|
interopTest("interop_leakMemoryWithRunningThreadChecked") {
|
||||||
|
disabled = project.globalTestArgs.contains('-opt') || (project.testTarget == 'wasm32') // Needs debug build.
|
||||||
|
interop = 'leakMemoryWithRunningThread'
|
||||||
|
source = "interop/leakMemoryWithRunningThread/checked.kt"
|
||||||
|
flags = ['-g']
|
||||||
|
expectedExitStatusChecker = { it != 0 }
|
||||||
|
outputChecker = { s -> s.contains("Cannot run checkers when there are 1 alive runtimes at the shutdown") }
|
||||||
|
}
|
||||||
|
|
||||||
standaloneTest("interop_pinning") {
|
standaloneTest("interop_pinning") {
|
||||||
disabled = (project.testTarget == 'wasm32') // Uses exceptions.
|
disabled = (project.testTarget == 'wasm32') // Uses exceptions.
|
||||||
source = "interop/basics/pinning.kt"
|
source = "interop/basics/pinning.kt"
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import leakMemory.*
|
||||||
|
import kotlin.native.concurrent.*
|
||||||
|
import kotlin.test.*
|
||||||
|
import kotlinx.cinterop.*
|
||||||
|
|
||||||
|
val global = AtomicInt(0)
|
||||||
|
|
||||||
|
fun ensureInititalized() {
|
||||||
|
kotlin.native.initRuntimeIfNeeded()
|
||||||
|
// Leak memory
|
||||||
|
StableRef.create(Any())
|
||||||
|
global.value = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
kotlin.native.internal.Debugging.forceCheckedShutdown = true
|
||||||
|
assertTrue(global.value == 0)
|
||||||
|
// Created a thread, made sure Kotlin is initialized there.
|
||||||
|
test_RunInNewThread(staticCFunction(::ensureInititalized))
|
||||||
|
assertTrue(global.value == 1)
|
||||||
|
// Now exiting. With checked shutdown we will fail, complaining there're
|
||||||
|
// unfinished threads with runtimes.
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#include "leakMemory.h"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
extern "C" void test_RunInNewThread(void (*f)()) {
|
||||||
|
std::atomic<bool> haveRun(false);
|
||||||
|
std::thread t([f, &haveRun]() {
|
||||||
|
f();
|
||||||
|
haveRun = true;
|
||||||
|
while (true) {}
|
||||||
|
});
|
||||||
|
t.detach();
|
||||||
|
while (!haveRun) {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package leakMemory
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
void test_RunInNewThread(void (*)());
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void test_RunInNewThread(void (*)());
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import leakMemory.*
|
||||||
|
import kotlin.native.concurrent.*
|
||||||
|
import kotlin.test.*
|
||||||
|
import kotlinx.cinterop.*
|
||||||
|
|
||||||
|
val global = AtomicInt(0)
|
||||||
|
|
||||||
|
fun ensureInititalized() {
|
||||||
|
kotlin.native.initRuntimeIfNeeded()
|
||||||
|
// Leak memory
|
||||||
|
StableRef.create(Any())
|
||||||
|
global.value = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
kotlin.native.internal.Debugging.forceCheckedShutdown = false
|
||||||
|
assertTrue(global.value == 0)
|
||||||
|
// Created a thread, made sure Kotlin is initialized there.
|
||||||
|
test_RunInNewThread(staticCFunction(::ensureInititalized))
|
||||||
|
assertTrue(global.value == 1)
|
||||||
|
// Now exiting. With unchecked shutdown, we exit quietly, even though there're
|
||||||
|
// unfinished threads with runtimes.
|
||||||
|
}
|
||||||
@@ -227,23 +227,28 @@ void Kotlin_shutdownRuntime() {
|
|||||||
auto lastStatus = compareAndSwap(&globalRuntimeStatus, kGlobalRuntimeRunning, kGlobalRuntimeShutdown);
|
auto lastStatus = compareAndSwap(&globalRuntimeStatus, kGlobalRuntimeRunning, kGlobalRuntimeShutdown);
|
||||||
RuntimeAssert(lastStatus == kGlobalRuntimeRunning, "Invalid runtime status for shutdown");
|
RuntimeAssert(lastStatus == kGlobalRuntimeRunning, "Invalid runtime status for shutdown");
|
||||||
|
|
||||||
|
bool canDestroyRuntime = true;
|
||||||
|
|
||||||
// TODO: When legacy mode is gone, this `if` will become unnecessary.
|
// TODO: When legacy mode is gone, this `if` will become unnecessary.
|
||||||
if (Kotlin_forceCheckedShutdown() || Kotlin_memoryLeakCheckerEnabled() || Kotlin_cleanersLeakCheckerEnabled()) {
|
if (Kotlin_forceCheckedShutdown() || Kotlin_memoryLeakCheckerEnabled() || Kotlin_cleanersLeakCheckerEnabled()) {
|
||||||
// First make sure workers are gone.
|
// First make sure workers are gone.
|
||||||
WaitNativeWorkersTermination();
|
WaitNativeWorkersTermination();
|
||||||
|
|
||||||
|
// Now check for existence of any other runtimes.
|
||||||
|
auto otherRuntimesCount = atomicGet(&aliveRuntimesCount) - 1;
|
||||||
|
RuntimeAssert(otherRuntimesCount >= 0, "Cannot be negative");
|
||||||
if (Kotlin_forceCheckedShutdown()) {
|
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) {
|
if (otherRuntimesCount > 0) {
|
||||||
konan::consoleErrorf("Cannot run checkers when there are %d alive runtimes at the shutdown", otherRuntimesCount);
|
konan::consoleErrorf("Cannot run checkers when there are %d alive runtimes at the shutdown", otherRuntimesCount);
|
||||||
konan::abort();
|
konan::abort();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Cannot destroy runtime globally if there're some other threads with Kotlin runtime on them.
|
||||||
|
canDestroyRuntime = otherRuntimesCount == 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deinitRuntime(runtime, true);
|
deinitRuntime(runtime, canDestroyRuntime);
|
||||||
::runtimeState = kInvalidRuntime;
|
::runtimeState = kInvalidRuntime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user