diff --git a/runtime/src/main/cpp/Memory.cpp b/runtime/src/main/cpp/Memory.cpp index 0fb39b167da..129a107efa6 100644 --- a/runtime/src/main/cpp/Memory.cpp +++ b/runtime/src/main/cpp/Memory.cpp @@ -102,6 +102,8 @@ FrameOverlay exportFrameOverlay; volatile int allocCount = 0; volatile int aliveMemoryStatesCount = 0; +KBoolean g_checkLeaks = KonanNeedDebugInfo; + // TODO: can we pass this variable as an explicit argument? THREAD_LOCAL_VARIABLE MemoryState* memoryState = nullptr; THREAD_LOCAL_VARIABLE FrameOverlay* currentFrame = nullptr; @@ -1724,10 +1726,16 @@ void deinitMemory(MemoryState* memoryState) { } #else #if USE_GC - if (IsStrictMemoryModel && lastMemoryState) - RuntimeAssert(allocCount == 0, "Memory leaks found"); -#endif -#endif + if (IsStrictMemoryModel && lastMemoryState && allocCount > 0 && g_checkLeaks) { + char buf[1024]; + konan::snprintf(buf, sizeof(buf), + "Memory leaks detected, %d objects leaked!\n" + "Use `Platform.isMemoryLeakCheckerActive = false` to avoid this check.\n", allocCount); + konan::consoleErrorUtf8(buf, konan::strnlen(buf, sizeof(buf))); + konan::abort(); + } +#endif // USE_GC +#endif // TRACE_MEMORY PRINT_EVENT(memoryState) DEINIT_EVENT(memoryState) @@ -2941,4 +2949,13 @@ void Kotlin_Any_share(ObjHeader* obj) { shareAny(obj); } +KBoolean Konan_Platform_getMemoryLeakChecker() { + return g_checkLeaks; +} + +void Konan_Platform_setMemoryLeakChecker(KBoolean value) { + g_checkLeaks = value; +} + + } // extern "C" diff --git a/runtime/src/main/cpp/Porting.cpp b/runtime/src/main/cpp/Porting.cpp index 6d5dbb3c9f6..6a2d5c4320f 100644 --- a/runtime/src/main/cpp/Porting.cpp +++ b/runtime/src/main/cpp/Porting.cpp @@ -143,6 +143,7 @@ static void onThreadExitCallback(void* value) { free(record); record = next; } + pthread_setspecific(terminationKey, nullptr); } static void onThreadExitInit() { diff --git a/runtime/src/main/kotlin/kotlin/native/Platform.kt b/runtime/src/main/kotlin/kotlin/native/Platform.kt index 9f972db8436..f031823e94d 100644 --- a/runtime/src/main/kotlin/kotlin/native/Platform.kt +++ b/runtime/src/main/kotlin/kotlin/native/Platform.kt @@ -81,6 +81,17 @@ public object Platform { public val isDebugBinary: Boolean get() = Platform_isDebugBinary() + /** + * If the memory leak checker is activated, by default `true` in debug mode, `false` in release. + * When memory leak checker is activated, and leak is detected during last Kotlin context + * deinitialization process - error message with leak information is printed and application + * execution is aborted. + * + * @see isDebugBinary + */ + public var isMemoryLeakCheckerActive: Boolean + get() = Platform_getMemoryLeakChecker() + set(value) = Platform_setMemoryLeakChecker(value) } @SymbolName("Konan_Platform_canAccessUnaligned") @@ -99,4 +110,10 @@ private external fun Platform_getCpuArchitecture(): Int private external fun Platform_getMemoryModel(): Int @SymbolName("Konan_Platform_isDebugBinary") -private external fun Platform_isDebugBinary(): Boolean \ No newline at end of file +private external fun Platform_isDebugBinary(): Boolean + +@SymbolName("Konan_Platform_getMemoryLeakChecker") +private external fun Platform_getMemoryLeakChecker(): Boolean + +@SymbolName("Konan_Platform_setMemoryLeakChecker") +private external fun Platform_setMemoryLeakChecker(value: Boolean): Unit