diff --git a/runtime/src/main/cpp/Memory.cpp b/runtime/src/main/cpp/Memory.cpp index bcc70553a7c..1a3158526c9 100644 --- a/runtime/src/main/cpp/Memory.cpp +++ b/runtime/src/main/cpp/Memory.cpp @@ -2014,16 +2014,6 @@ void deinitMemory(MemoryState* memoryState) { ::memoryState = nullptr; } -MemoryState* suspendMemory() { - auto result = ::memoryState; - ::memoryState = nullptr; - return result; -} - -void resumeMemory(MemoryState* state) { - ::memoryState = state; -} - void makeShareable(ContainerHeader* container) { if (!container->frozen()) container->makeShared(); @@ -3189,12 +3179,9 @@ void DeinitMemory(MemoryState* memoryState) { deinitMemory(memoryState); } -MemoryState* SuspendMemory() { - return suspendMemory(); -} - -void ResumeMemory(MemoryState* state) { - resumeMemory(state); +void RestoreMemory(MemoryState* memoryState) { + RuntimeAssert((::memoryState == nullptr) || (::memoryState == memoryState), "Must not replace with unrelated memory state"); + ::memoryState = memoryState; } OBJ_GETTER(AllocInstanceStrict, const TypeInfo* type_info) { diff --git a/runtime/src/main/cpp/Memory.h b/runtime/src/main/cpp/Memory.h index 9ee0a251607..30a21a5b7cf 100644 --- a/runtime/src/main/cpp/Memory.h +++ b/runtime/src/main/cpp/Memory.h @@ -429,9 +429,7 @@ struct MemoryState; MemoryState* InitMemory(); void DeinitMemory(MemoryState*); - -MemoryState* SuspendMemory(); -void ResumeMemory(MemoryState* state); +void RestoreMemory(MemoryState*); // // Object allocation. diff --git a/runtime/src/main/cpp/MemorySharedRefs.cpp b/runtime/src/main/cpp/MemorySharedRefs.cpp index 71180cf38d8..5374f10039f 100644 --- a/runtime/src/main/cpp/MemorySharedRefs.cpp +++ b/runtime/src/main/cpp/MemorySharedRefs.cpp @@ -17,14 +17,12 @@ OBJ_GETTER(DescribeObjectForDebugging, KConstNativePtr typeInfo, KConstNativePtr namespace { inline bool isForeignRefAccessible(ObjHeader* object, ForeignRefContext context) { - if (!Kotlin_hasRuntime()) { - // So the object is either unowned or shared. + // If runtime has not been initialized on this thread, then the object is either unowned or shared. // In the former case initialized runtime is required to throw exceptions // in the latter case -- to provide proper execution context for caller. Kotlin_initRuntimeIfNeeded(); - } - return IsForeignRefAccessible(object, context); + return IsForeignRefAccessible(object, context); } RUNTIME_NORETURN inline void throwIllegalSharingException(ObjHeader* object) { diff --git a/runtime/src/main/cpp/Runtime.cpp b/runtime/src/main/cpp/Runtime.cpp index e4eb23ef4a9..1457e92e1c1 100644 --- a/runtime/src/main/cpp/Runtime.cpp +++ b/runtime/src/main/cpp/Runtime.cpp @@ -25,12 +25,6 @@ #include "Runtime.h" #include "Worker.h" -struct RuntimeState { - MemoryState* memoryState; - Worker* worker; - volatile int executionStatus; -}; - typedef void (*Initializer)(int initialize, MemoryState* memory); struct InitNode { Initializer init; @@ -42,6 +36,18 @@ namespace { InitNode* initHeadNode = nullptr; InitNode* initTailNode = nullptr; +enum class RuntimeStatus { + kUninitialized, + kRunning, + kDestroying, +}; + +struct RuntimeState { + MemoryState* memoryState; + Worker* worker; + RuntimeStatus status = RuntimeStatus::kUninitialized; +}; + enum { INIT_GLOBALS = 0, INIT_THREAD_LOCAL_GLOBALS = 1, @@ -49,24 +55,6 @@ enum { DEINIT_GLOBALS = 3 }; -enum { - SUSPENDED = 0, - RUNNING, - DESTROYING -}; - -bool updateStatusIf(RuntimeState* state, int oldStatus, int newStatus) { -#if KONAN_NO_THREADS - if (state->executionStatus == oldStatus) { - state->executionStatus = newStatus; - return true; - } - return false; -#else - return __sync_bool_compare_and_swap(&state->executionStatus, oldStatus, newStatus); -#endif -} - void InitOrDeinitGlobalVariables(int initialize, MemoryState* memory) { InitNode* currentNode = initHeadNode; while (currentNode != nullptr) { @@ -108,11 +96,16 @@ RuntimeState* initRuntime() { InitOrDeinitGlobalVariables(INIT_GLOBALS, result->memoryState); } InitOrDeinitGlobalVariables(INIT_THREAD_LOCAL_GLOBALS, result->memoryState); + RuntimeAssert(result->status == RuntimeStatus::kUninitialized, "Runtime must still be in the uninitialized state"); + result->status = RuntimeStatus::kRunning; return result; } void deinitRuntime(RuntimeState* state) { - ResumeMemory(state->memoryState); + RuntimeAssert(state->status == RuntimeStatus::kRunning, "Runtime must be in the running state"); + state->status = RuntimeStatus::kDestroying; + // This may be called after TLS is zeroed out, so ::memoryState in Memory cannot be trusted. + RestoreMemory(state->memoryState); bool lastRuntime = atomicAdd(&aliveRuntimesCount, -1) == 0; InitOrDeinitGlobalVariables(DEINIT_THREAD_LOCAL_GLOBALS, state->memoryState); if (lastRuntime) @@ -126,7 +119,6 @@ void deinitRuntime(RuntimeState* state) { void Kotlin_deinitRuntimeCallback(void* argument) { auto* state = reinterpret_cast(argument); - RuntimeCheck(updateStatusIf(state, RUNNING, DESTROYING), "Cannot transition state to DESTROYING"); deinitRuntime(state); } @@ -147,7 +139,6 @@ void AppendToInitializersTail(InitNode *next) { void Kotlin_initRuntimeIfNeeded() { if (!isValidRuntime()) { initRuntime(); - RuntimeCheck(updateStatusIf(::runtimeState, SUSPENDED, RUNNING), "Cannot transition state to RUNNING for init"); // Register runtime deinit function at thread cleanup. konan::onThreadExit(Kotlin_deinitRuntimeCallback, runtimeState); } @@ -160,42 +151,6 @@ void Kotlin_deinitRuntimeIfNeeded() { } } -RuntimeState* Kotlin_createRuntime() { - return initRuntime(); -} - -void Kotlin_destroyRuntime(RuntimeState* state) { - RuntimeCheck(updateStatusIf(state, SUSPENDED, DESTROYING), "Cannot transition state to DESTROYING"); - deinitRuntime(state); -} - -RuntimeState* Kotlin_suspendRuntime() { - 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(); - result->worker = WorkerSuspend(); - ::runtimeState = kInvalidRuntime; - return result; -} - -void Kotlin_resumeRuntime(RuntimeState* state) { - 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); - WorkerResume(state->worker); -} - -RuntimeState* Kotlin_getRuntime() { - RuntimeCheck(isValidRuntime(), "Runtime must be active on the current thread"); - return ::runtimeState; -} - -bool Kotlin_hasRuntime() { - return isValidRuntime(); -} - void CheckIsMainThread() { if (!isMainThread) ThrowIncorrectDereferenceException(); diff --git a/runtime/src/main/cpp/Runtime.h b/runtime/src/main/cpp/Runtime.h index ed564e7056f..1b296f7b7ea 100644 --- a/runtime/src/main/cpp/Runtime.h +++ b/runtime/src/main/cpp/Runtime.h @@ -19,7 +19,6 @@ #include "Porting.h" -struct RuntimeState; struct InitNode; #ifdef __cplusplus @@ -29,24 +28,6 @@ extern "C" { void Kotlin_initRuntimeIfNeeded(); void Kotlin_deinitRuntimeIfNeeded(); -// Operations below allow flexible runtime scheduling on different threads. -// Created runtime is in SUSPENDED state, and need to be resumed for actual execution. -RuntimeState* Kotlin_createRuntime(); -// Runtime must be in SUSPENDED state, before it could be destroyed. -void Kotlin_destroyRuntime(RuntimeState*); - -// Transition current runtime from RUNNING to SUSPENDED state, and clearing thread local variable caching -// the runtime. After suspension, runtime could be rescheduled to a different thread. -RuntimeState* Kotlin_suspendRuntime(); -// Transition runtime from SUSPENDED to RUNNING state, and sets thread local variable caching -// the runtime. After resume, current thread could be used for executing Kotlin code. -void Kotlin_resumeRuntime(RuntimeState*); - -// Gets currently active runtime, fails if no runtime is currently available. -RuntimeState* Kotlin_getRuntime(); - -bool Kotlin_hasRuntime(); - // Appends given node to an initializer list. void AppendToInitializersTail(struct InitNode*); diff --git a/runtime/src/main/cpp/Worker.cpp b/runtime/src/main/cpp/Worker.cpp index 8311f316e10..0dc6ada9f3f 100644 --- a/runtime/src/main/cpp/Worker.cpp +++ b/runtime/src/main/cpp/Worker.cpp @@ -752,22 +752,6 @@ void WaitNativeWorkerTermination(KInt id) { #endif } -Worker* WorkerSuspend() { -#if WITH_WORKERS - auto* result = ::g_worker; - ::g_worker = nullptr; - return result; -#else - return nullptr; -#endif // WITH_WORKERS -} - -void WorkerResume(Worker* worker) { -#if WITH_WORKERS - ::g_worker = worker; -#endif // WITH_WORKERS -} - bool WorkerSchedule(KInt id, KNativePtr jobStablePtr) { #if WITH_WORKERS return theState()->scheduleJobInWorkerUnlocked(id, jobStablePtr); @@ -819,7 +803,9 @@ namespace { void* workerRoutine(void* argument) { Worker* worker = reinterpret_cast(argument); - WorkerResume(worker); + // Kotlin_initRuntimeIfNeeded calls WorkerInit that needs + // to see there's already a worker created for this thread. + ::g_worker = worker; Kotlin_initRuntimeIfNeeded(); do { diff --git a/runtime/src/main/cpp/Worker.h b/runtime/src/main/cpp/Worker.h index 285d49ba092..bb99194b61a 100644 --- a/runtime/src/main/cpp/Worker.h +++ b/runtime/src/main/cpp/Worker.h @@ -19,7 +19,4 @@ void WaitNativeWorkerTermination(KInt id); // Schedule the job without the result. bool WorkerSchedule(KInt id, KNativePtr jobStablePtr); -Worker* WorkerSuspend(); -void WorkerResume(Worker* worker); - #endif // RUNTIME_WORKER_H