diff --git a/runtime/src/main/cpp/Porting.cpp b/runtime/src/main/cpp/Porting.cpp index 4158125e6aa..797764e6df7 100644 --- a/runtime/src/main/cpp/Porting.cpp +++ b/runtime/src/main/cpp/Porting.cpp @@ -22,6 +22,9 @@ #include #include #include +#if !KONAN_NO_THREADS +#include +#endif #include #if KONAN_WINDOWS #include @@ -90,6 +93,53 @@ void consolePrintf(const char* format, ...) { } +// Thread execution. +#if !KONAN_NO_THREADS + +pthread_key_t terminationKey; +pthread_once_t terminationKeyOnceControl = PTHREAD_ONCE_INIT; + +typedef void (*destructor_t)(); + +struct DestructorRecord { + struct DestructorRecord* next; + destructor_t destructor; +}; + +static void onThreadExitCallback(void* value) { + DestructorRecord* record = reinterpret_cast(value); + while (record != nullptr) { + record->destructor(); + auto next = record->next; + free(record); + record = next; + } +} + +static void onThreadExitInit() { + pthread_key_create(&terminationKey, onThreadExitCallback); +} + +#endif // !KONAN_NO_THREADS + +void onThreadExit(void (*destructor)()) { +#if KONAN_NO_THREADS +#ifdef KONAN_WASM + // No way to do that. +#else + ::atexit(destructor); +#endif +#else // !KONAN_NO_THREADS + // We cannot use pthread_cleanup_push() as it is lexical scope bound. + pthread_once(&terminationKeyOnceControl, onThreadExitInit); + DestructorRecord* destructorRecord = (DestructorRecord*)calloc(1, sizeof(DestructorRecord)); + destructorRecord->destructor = destructor; + destructorRecord->next = + reinterpret_cast(pthread_getspecific(terminationKey)); + pthread_setspecific(terminationKey, destructorRecord); +#endif // !KONAN_NO_THREADS +} + // Process execution. void abort() { ::abort(); diff --git a/runtime/src/main/cpp/Porting.h b/runtime/src/main/cpp/Porting.h index b683bcab6d4..9c6664409f3 100644 --- a/runtime/src/main/cpp/Porting.h +++ b/runtime/src/main/cpp/Porting.h @@ -33,6 +33,9 @@ uint32_t consoleReadUtf8(void* utf8, uint32_t maxSizeBytes); void abort(); void exit(int32_t status); +// Thread control. +void onThreadExit(void (*destructor)()); + // String/byte operations. // memcpy/memmove/memcmp are not here intentionally, as frequently implemented/optimized // by C compiler. diff --git a/runtime/src/main/cpp/Runtime.cpp b/runtime/src/main/cpp/Runtime.cpp index cea7cbc2f12..351a918f4ed 100644 --- a/runtime/src/main/cpp/Runtime.cpp +++ b/runtime/src/main/cpp/Runtime.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#include - #include "Alloc.h" #include "Exceptions.h" #include "Memory.h" @@ -82,8 +80,11 @@ void DeinitRuntime(RuntimeState* state) { } void Kotlin_initRuntimeIfNeeded() { - if (runtimeState == nullptr) + if (runtimeState == nullptr) { runtimeState = InitRuntime(); + // Register runtime deinit function at thread cleanup. + konan::onThreadExit(Kotlin_deinitRuntimeIfNeeded); + } } void Kotlin_deinitRuntimeIfNeeded() { diff --git a/runtime/src/main/cpp/Runtime.h b/runtime/src/main/cpp/Runtime.h index 1d7605fc636..59e237c8134 100644 --- a/runtime/src/main/cpp/Runtime.h +++ b/runtime/src/main/cpp/Runtime.h @@ -26,6 +26,8 @@ extern "C" { RuntimeState* InitRuntime(); void DeinitRuntime(RuntimeState* state); +void Kotlin_initRuntimeIfNeeded(); +void Kotlin_deinitRuntimeIfNeeded(); // Appends given node to an initializer list. void AppendToInitializersTail(struct InitNode*);