Fix memory leak in runtime on macOS.

This commit is contained in:
Nikolay Igotti
2019-07-10 21:00:08 +03:00
committed by Nikolay Igotti
parent b19f5fabba
commit dcf35ffad2
6 changed files with 35 additions and 22 deletions
+1
View File
@@ -2453,6 +2453,7 @@ MemoryState* InitMemory() {
}
void DeinitMemory(MemoryState* memoryState) {
::memoryState = memoryState;
deinitMemory(memoryState);
}
+5 -3
View File
@@ -127,17 +127,18 @@ void consolePrintf(const char* format, ...) {
pthread_key_t terminationKey;
pthread_once_t terminationKeyOnceControl = PTHREAD_ONCE_INIT;
typedef void (*destructor_t)();
typedef void (*destructor_t)(void*);
struct DestructorRecord {
struct DestructorRecord* next;
destructor_t destructor;
void* destructorParameter;
};
static void onThreadExitCallback(void* value) {
DestructorRecord* record = reinterpret_cast<DestructorRecord*>(value);
while (record != nullptr) {
record->destructor();
record->destructor(record->destructorParameter);
auto next = record->next;
free(record);
record = next;
@@ -150,7 +151,7 @@ static void onThreadExitInit() {
#endif // !KONAN_NO_THREADS
void onThreadExit(void (*destructor)()) {
void onThreadExit(void (*destructor)(void*), void* destructorParameter) {
#if KONAN_NO_THREADS
#if KONAN_WASM || KONAN_ZEPHYR
// No way to do that.
@@ -162,6 +163,7 @@ void onThreadExit(void (*destructor)()) {
pthread_once(&terminationKeyOnceControl, onThreadExitInit);
DestructorRecord* destructorRecord = (DestructorRecord*)calloc(1, sizeof(DestructorRecord));
destructorRecord->destructor = destructor;
destructorRecord->destructorParameter = destructorParameter;
destructorRecord->next =
reinterpret_cast<DestructorRecord*>(pthread_getspecific(terminationKey));
pthread_setspecific(terminationKey, destructorRecord);
+1 -1
View File
@@ -37,7 +37,7 @@ RUNTIME_NORETURN void abort(void);
RUNTIME_NORETURN void exit(int32_t status);
// Thread control.
void onThreadExit(void (*destructor)());
void onThreadExit(void (*destructor)(void*), void* destructorParameter);
// String/byte operations.
// memcpy/memmove/memcmp are not here intentionally, as frequently implemented/optimized
+26 -16
View File
@@ -22,7 +22,6 @@
#include "Porting.h"
#include "Runtime.h"
struct RuntimeState {
MemoryState* memoryState;
volatile int executionStatus;
@@ -72,17 +71,23 @@ void InitOrDeinitGlobalVariables(int initialize) {
}
}
THREAD_LOCAL_VARIABLE RuntimeState* runtimeState = nullptr;
constexpr RuntimeState* kInvalidRuntime = nullptr;
THREAD_LOCAL_VARIABLE RuntimeState* runtimeState = kInvalidRuntime;
THREAD_LOCAL_VARIABLE int isMainThread = 0;
inline bool isValidRuntime() {
return ::runtimeState != kInvalidRuntime;
}
int aliveRuntimesCount = 0;
RuntimeState* initRuntime() {
SetKonanTerminateHandler();
RuntimeState* result = konanConstructInstance<RuntimeState>();
if (!result) return nullptr;
RuntimeCheck(runtimeState == nullptr, "No active runtimes allowed");
runtimeState = result;
if (!result) return kInvalidRuntime;
RuntimeCheck(!isValidRuntime(), "No active runtimes allowed");
::runtimeState = result;
result->memoryState = InitMemory();
bool firstRuntime = atomicAdd(&aliveRuntimesCount, 1) == 1;
// Keep global variables in state as well.
@@ -104,6 +109,12 @@ void deinitRuntime(RuntimeState* state) {
konanDestructInstance(state);
}
void Kotlin_deinitRuntimeCallback(void* argument) {
auto* state = reinterpret_cast<RuntimeState*>(argument);
RuntimeCheck(updateStatusIf(state, RUNNING, DESTROYING), "Cannot transition state to DESTROYING");
deinitRuntime(state);
}
} // namespace
extern "C" {
@@ -119,19 +130,18 @@ void AppendToInitializersTail(InitNode *next) {
}
void Kotlin_initRuntimeIfNeeded() {
if (runtimeState == nullptr) {
if (!isValidRuntime()) {
initRuntime();
RuntimeCheck(updateStatusIf(runtimeState, SUSPENDED, RUNNING), "Cannot transition state to RUNNING for init");
RuntimeCheck(updateStatusIf(::runtimeState, SUSPENDED, RUNNING), "Cannot transition state to RUNNING for init");
// Register runtime deinit function at thread cleanup.
konan::onThreadExit(Kotlin_deinitRuntimeIfNeeded);
konan::onThreadExit(Kotlin_deinitRuntimeCallback, runtimeState);
}
}
void Kotlin_deinitRuntimeIfNeeded() {
if (runtimeState != nullptr) {
RuntimeCheck(updateStatusIf(runtimeState, RUNNING, DESTROYING), "Cannot transition state to DESTROYING");
deinitRuntime(runtimeState);
runtimeState = nullptr;
if (isValidRuntime()) {
deinitRuntime(::runtimeState);
::runtimeState = kInvalidRuntime;
}
}
@@ -145,23 +155,23 @@ void Kotlin_destroyRuntime(RuntimeState* state) {
}
RuntimeState* Kotlin_suspendRuntime() {
RuntimeCheck(::runtimeState != nullptr, "Runtime must be active on the current thread");
RuntimeCheck(isValidRuntime(), "Runtime must be active on the current thread");
auto result = ::runtimeState;
RuntimeCheck(updateStatusIf(result, RUNNING, SUSPENDED), "Cannot transition state to SUSPENDED for suspend");
result->memoryState = SuspendMemory();
::runtimeState = nullptr;
::runtimeState = kInvalidRuntime;
return result;
}
void Kotlin_resumeRuntime(RuntimeState* state) {
RuntimeCheck(::runtimeState == nullptr, "Runtime must not be active on the current thread");
RuntimeCheck(!isValidRuntime(), "Runtime must not be active on the current thread");
RuntimeCheck(updateStatusIf(state, SUSPENDED, RUNNING), "Cannot transition state to RUNNING for resume");
::runtimeState = state;
ResumeMemory(state->memoryState);
}
RuntimeState* RUNTIME_USED Kotlin_getRuntime() {
RuntimeCheck(::runtimeState != nullptr, "Runtime must be active on the current thread");
RuntimeCheck(isValidRuntime(), "Runtime must be active on the current thread");
return ::runtimeState;
}
-2
View File
@@ -591,8 +591,6 @@ void* workerRoutine(void* argument) {
konanDestructInstance(worker);
Kotlin_deinitRuntimeIfNeeded();
return nullptr;
}
@@ -15,8 +15,10 @@ external public fun initRuntimeIfNeeded(): Unit
/**
* Deinitializes Kotlin runtime for the current thread, if was inited.
* Cannot be called from Kotlin frames holding references, thus deprecated.
*/
@SymbolName("Kotlin_deinitRuntimeIfNeeded")
@Deprecated("Deinit runtime can not be called from Kotlin", level = DeprecationLevel.ERROR)
external public fun deinitRuntimeIfNeeded(): Unit
/**