[K/N][IR][runtime] Implemented lazy per-file initialization strategy
This commit is contained in:
@@ -58,6 +58,7 @@ void RUNTIME_NORETURN ThrowIllegalArgumentException();
|
||||
void RUNTIME_NORETURN ThrowIllegalStateException();
|
||||
void RUNTIME_NORETURN ThrowInvalidMutabilityException(KConstRef where);
|
||||
void RUNTIME_NORETURN ThrowIncorrectDereferenceException();
|
||||
void RUNTIME_NORETURN ThrowFileFailedToInitializeException();
|
||||
void RUNTIME_NORETURN ThrowIllegalObjectSharingException(KConstNativePtr typeInfo, KConstNativePtr address);
|
||||
void RUNTIME_NORETURN ThrowFreezingException(KRef toFreeze, KRef blocker);
|
||||
// Prints out message of Throwable.
|
||||
|
||||
@@ -243,6 +243,39 @@ void onThreadExit(void (*destructor)(void*), void* destructorParameter) {
|
||||
#endif // !KONAN_NO_THREADS
|
||||
}
|
||||
|
||||
#if KONAN_LINUX
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/syscall.h>
|
||||
#endif
|
||||
|
||||
NO_EXTERNAL_CALLS_CHECK int currentThreadId() {
|
||||
#if KONAN_NO_THREADS
|
||||
#if KONAN_WASM || KONAN_ZEPHYR
|
||||
// No way to do that.
|
||||
return 0;
|
||||
#else
|
||||
#error "How to find currentThreadId()?"
|
||||
#endif
|
||||
#else // !KONAN_NO_THREADS
|
||||
#if defined(KONAN_OSX) or defined(KONAN_IOS) or defined(KONAN_TVOS) or defined(KONAN_WATCHOS)
|
||||
uint64_t tid;
|
||||
pthread_t self = pthread_self();
|
||||
RuntimeCheck(!pthread_threadid_np(self, &tid), "Error getting thread id");
|
||||
RuntimeCheck((*(reinterpret_cast<int32_t*>(&tid) + 1)) == 0, "Thread id is not a uint32");
|
||||
return tid;
|
||||
#elif KONAN_ANDROID
|
||||
return gettid();
|
||||
#elif KONAN_LINUX
|
||||
return syscall(__NR_gettid);
|
||||
#elif KONAN_WINDOWS
|
||||
return GetCurrentThreadId();
|
||||
#else
|
||||
#error "How to find currentThreadId()?"
|
||||
#endif
|
||||
#endif // !KONAN_NO_THREADS
|
||||
}
|
||||
|
||||
// Process execution.
|
||||
void abort(void) {
|
||||
::abort();
|
||||
|
||||
@@ -42,6 +42,7 @@ RUNTIME_NORETURN void exit(int32_t status);
|
||||
// Thread control.
|
||||
void onThreadExit(void (*destructor)(void*), void* destructorParameter);
|
||||
bool isOnThreadExitNotSetOrAlreadyStarted();
|
||||
int currentThreadId();
|
||||
|
||||
// String/byte operations.
|
||||
// memcpy/memmove/memcmp are not here intentionally, as frequently implemented/optimized
|
||||
|
||||
@@ -73,6 +73,8 @@ constexpr RuntimeState* kInvalidRuntime = nullptr;
|
||||
|
||||
THREAD_LOCAL_VARIABLE RuntimeState* runtimeState = kInvalidRuntime;
|
||||
|
||||
volatile int mainThreadId = 0;
|
||||
|
||||
inline bool isValidRuntime() {
|
||||
return ::runtimeState != kInvalidRuntime;
|
||||
}
|
||||
@@ -135,6 +137,7 @@ RuntimeState* initRuntime() {
|
||||
CommitTLSStorage(result->memoryState);
|
||||
// Keep global variables in state as well.
|
||||
if (firstRuntime) {
|
||||
mainThreadId = konan::currentThreadId();
|
||||
konan::consoleInit();
|
||||
#if KONAN_OBJC_INTEROP
|
||||
Kotlin_ObjCExport_initialize();
|
||||
@@ -410,4 +413,55 @@ RUNTIME_NOTHROW void Kotlin_initRuntimeIfNeededFromKotlin() {
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr int FILE_NOT_INITIALIZED = 0;
|
||||
static constexpr int FILE_BEING_INITIALIZED = 1;
|
||||
static constexpr int FILE_INITIALIZED = 2;
|
||||
static constexpr int FILE_FAILED_TO_INITIALIZE = 3;
|
||||
|
||||
void CallInitGlobalPossiblyLock(int volatile* state, void (*init)(bool)) {
|
||||
int localState = *state;
|
||||
if (localState == FILE_INITIALIZED) return;
|
||||
if (localState == FILE_FAILED_TO_INITIALIZE)
|
||||
ThrowFileFailedToInitializeException();
|
||||
int threadId = konan::currentThreadId();
|
||||
if ((localState & 3) == FILE_BEING_INITIALIZED) {
|
||||
if ((localState & ~3) != (threadId << 2)) {
|
||||
do {
|
||||
localState = *state;
|
||||
if (localState == FILE_FAILED_TO_INITIALIZE)
|
||||
ThrowFileFailedToInitializeException();
|
||||
} while (localState != FILE_INITIALIZED);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (compareAndSwap(state, FILE_NOT_INITIALIZED, FILE_BEING_INITIALIZED | (threadId << 2)) == FILE_NOT_INITIALIZED) {
|
||||
// actual initialization
|
||||
try {
|
||||
init(threadId == mainThreadId);
|
||||
} catch (...) {
|
||||
*state = FILE_FAILED_TO_INITIALIZE;
|
||||
throw;
|
||||
}
|
||||
*state = FILE_INITIALIZED;
|
||||
} else {
|
||||
do {
|
||||
localState = *state;
|
||||
if (localState == FILE_FAILED_TO_INITIALIZE)
|
||||
ThrowFileFailedToInitializeException();
|
||||
} while (localState != FILE_INITIALIZED);
|
||||
}
|
||||
}
|
||||
|
||||
void CallInitThreadLocal(int volatile* globalState, int* localState, void (*init)(bool)) {
|
||||
if (*localState == FILE_FAILED_TO_INITIALIZE || (globalState != nullptr && *globalState == FILE_FAILED_TO_INITIALIZE))
|
||||
ThrowFileFailedToInitializeException();
|
||||
*localState = FILE_INITIALIZED;
|
||||
try {
|
||||
init(konan::currentThreadId() == mainThreadId);
|
||||
} catch(...) {
|
||||
*localState = FILE_FAILED_TO_INITIALIZE;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
@@ -38,6 +38,9 @@ void Kotlin_shutdownRuntime();
|
||||
// Appends given node to an initializer list.
|
||||
void AppendToInitializersTail(struct InitNode*);
|
||||
|
||||
void CallInitGlobalPossiblyLock(int volatile* state, void (*init)(bool));
|
||||
void CallInitThreadLocal(int volatile* globalState, int* localState, void (*init)(bool));
|
||||
|
||||
bool Kotlin_memoryLeakCheckerEnabled();
|
||||
|
||||
bool Kotlin_cleanersLeakCheckerEnabled();
|
||||
|
||||
@@ -31,6 +31,16 @@ public class IncorrectDereferenceException : RuntimeException {
|
||||
constructor(message: String) : super(message)
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception thrown when there was an error during file initalization.
|
||||
*/
|
||||
@ExperimentalStdlibApi
|
||||
public class FileFailedToInitializeException : RuntimeException {
|
||||
constructor() : super()
|
||||
|
||||
constructor(message: String) : super(message)
|
||||
}
|
||||
|
||||
/**
|
||||
* Typealias describing custom exception reporting hook.
|
||||
*/
|
||||
|
||||
@@ -106,6 +106,12 @@ internal fun ThrowIncorrectDereferenceException() {
|
||||
"Trying to access top level value not marked as @ThreadLocal or @SharedImmutable from non-main thread")
|
||||
}
|
||||
|
||||
@ExportForCppRuntime
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
internal fun ThrowFileFailedToInitializeException() {
|
||||
throw FileFailedToInitializeException("There was an error during file initialization")
|
||||
}
|
||||
|
||||
@ExportForCppRuntime
|
||||
internal fun PrintThrowable(throwable: Throwable) {
|
||||
println(throwable)
|
||||
|
||||
@@ -56,6 +56,7 @@ extern "C" const char* Kotlin_callsCheckerGoodFunctionNames[] = {
|
||||
"__cxa_begin_catch",
|
||||
"__cxa_end_catch",
|
||||
"__cxa_throw",
|
||||
"__cxa_rethrow",
|
||||
"__memset_chk",
|
||||
|
||||
"abort",
|
||||
|
||||
@@ -177,6 +177,10 @@ void RUNTIME_NORETURN ThrowIncorrectDereferenceException() {
|
||||
throw std::runtime_error("Not implemented for tests");
|
||||
}
|
||||
|
||||
void RUNTIME_NORETURN ThrowFileFailedToInitializeException() {
|
||||
throw std::runtime_error("Not implemented for tests");
|
||||
}
|
||||
|
||||
void RUNTIME_NORETURN ThrowIllegalObjectSharingException(KConstNativePtr typeInfo, KConstNativePtr address) {
|
||||
throw std::runtime_error("Not implemented for tests");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user