From f4ac18b780f3d8f351dc9aaea3fec1260e9bb5c8 Mon Sep 17 00:00:00 2001 From: Nikolay Igotti Date: Sat, 9 Jun 2018 16:08:42 +0300 Subject: [PATCH] Kotlin machine scheduling primitives. (#1677) --- runtime/src/main/cpp/Memory.cpp | 11 +++++++ runtime/src/main/cpp/Memory.h | 3 ++ runtime/src/main/cpp/Runtime.cpp | 52 ++++++++++++++++++++++++++++++++ runtime/src/main/cpp/Runtime.h | 16 ++++++++++ 4 files changed, 82 insertions(+) diff --git a/runtime/src/main/cpp/Memory.cpp b/runtime/src/main/cpp/Memory.cpp index 1f69345d592..71362f8d100 100644 --- a/runtime/src/main/cpp/Memory.cpp +++ b/runtime/src/main/cpp/Memory.cpp @@ -1103,6 +1103,17 @@ void DeinitMemory(MemoryState* memoryState) { ::memoryState = nullptr; } +MemoryState* SuspendMemory() { + auto result = ::memoryState; + ::memoryState = nullptr; + return result; +} + +void ResumeMemory(MemoryState* state) { + RuntimeAssert(::memoryState == nullptr, "Cannot schedule on existing state"); + ::memoryState = state; +} + OBJ_GETTER(AllocInstance, const TypeInfo* type_info) { RuntimeAssert(type_info->instanceSize_ >= 0, "must be an object"); if (isArenaSlot(OBJ_RESULT)) { diff --git a/runtime/src/main/cpp/Memory.h b/runtime/src/main/cpp/Memory.h index 7e6a3fa52bf..e0c6d25f203 100644 --- a/runtime/src/main/cpp/Memory.h +++ b/runtime/src/main/cpp/Memory.h @@ -379,6 +379,9 @@ struct MemoryState; MemoryState* InitMemory(); void DeinitMemory(MemoryState*); +MemoryState* SuspendMemory(); +void ResumeMemory(MemoryState* state); + // // Object allocation. // diff --git a/runtime/src/main/cpp/Runtime.cpp b/runtime/src/main/cpp/Runtime.cpp index ecadf7e2d02..ac843bd242f 100644 --- a/runtime/src/main/cpp/Runtime.cpp +++ b/runtime/src/main/cpp/Runtime.cpp @@ -23,6 +23,7 @@ struct RuntimeState { MemoryState* memoryState; + volatile int executionStatus; }; typedef void (*Initializer)(int initialize); @@ -42,6 +43,24 @@ enum { DEINIT_GLOBALS = 2 }; +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) { InitNode *currNode = initHeadNode; while (currNode != nullptr) { @@ -74,6 +93,7 @@ void deinitRuntime(RuntimeState* state) { DeinitMemory(state->memoryState); konanDestructInstance(state); } + } // namespace extern "C" { @@ -91,6 +111,7 @@ void AppendToInitializersTail(InitNode *next) { void Kotlin_initRuntimeIfNeeded() { if (runtimeState == nullptr) { runtimeState = initRuntime(); + RuntimeCheck(updateStatusIf(runtimeState, SUSPENDED, RUNNING), "Cannot transition state to RUNNING for init"); // Register runtime deinit function at thread cleanup. konan::onThreadExit(Kotlin_deinitRuntimeIfNeeded); } @@ -98,9 +119,40 @@ void Kotlin_initRuntimeIfNeeded() { void Kotlin_deinitRuntimeIfNeeded() { if (runtimeState != nullptr) { + RuntimeCheck(updateStatusIf(runtimeState, RUNNING, DESTROYING), "Cannot transition state to DESTROYING"); deinitRuntime(runtimeState); runtimeState = nullptr; } } +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(::runtimeState != nullptr, "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(); + ::runtimeState = nullptr; + return result; +} + +void Kotlin_resumeRuntime(RuntimeState* state) { + RuntimeCheck(::runtimeState == nullptr, "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); +} + +RuntimeState* RUNTIME_USED Kotlin_getRuntime() { + RuntimeCheck(::runtimeState != nullptr, "Runtime must be active on the current thread"); + return ::runtimeState; +} + } // extern "C" diff --git a/runtime/src/main/cpp/Runtime.h b/runtime/src/main/cpp/Runtime.h index e9bf48aeae4..c9a2f9a4416 100644 --- a/runtime/src/main/cpp/Runtime.h +++ b/runtime/src/main/cpp/Runtime.h @@ -29,6 +29,22 @@ extern "C" { void RUNTIME_USED Kotlin_initRuntimeIfNeeded(); void RUNTIME_USED 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* RUNTIME_USED Kotlin_createRuntime(); +// Runtime must be in SUSPENDED state, before it could be destroyed. +void RUNTIME_USED 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* RUNTIME_USED 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 RUNTIME_USED Kotlin_resumeRuntime(RuntimeState*); + +// Gets currently active runtime, nullptr if no runtime is currently available. +RuntimeState* RUNTIME_USED Kotlin_getRuntime(); + // Appends given node to an initializer list. void AppendToInitializersTail(struct InitNode*);