[K/N][IR][runtime] Implemented lazy per-file initialization strategy

This commit is contained in:
Igor Chevdar
2021-03-12 11:18:55 +05:00
parent 504449f03e
commit b11201be81
37 changed files with 976 additions and 123 deletions
@@ -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");
}