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