From 3a5c6480a30005d3259ac222a18169f6c1a0b06c Mon Sep 17 00:00:00 2001 From: Alexander Shabalin Date: Sun, 21 Feb 2021 11:35:03 +0300 Subject: [PATCH] Track current exception (#4701) --- .../kotlin/backend/konan/CAdapterGenerator.kt | 17 +-- .../backend/konan/llvm/CodeGenerator.kt | 8 +- .../kotlin/backend/konan/llvm/ContextUtils.kt | 1 + .../backend.native/tests/build.gradle | 10 ++ .../tests/runtime/exceptions/rethrow.kt | 19 +++ .../runtime/exceptions/throw_from_catch.kt | 19 +++ .../runtime/src/legacymm/cpp/Memory.cpp | 25 ++++ .../runtime/src/main/cpp/Exceptions.cpp | 12 +- .../runtime/src/main/cpp/Exceptions.h | 2 + kotlin-native/runtime/src/main/cpp/Memory.h | 22 ++-- .../runtime/src/main/cpp/MultiSourceQueue.hpp | 5 + kotlin-native/runtime/src/main/cpp/Worker.cpp | 4 +- .../runtime/src/mm/cpp/ExceptionObjHolder.cpp | 47 ++++++++ .../src/mm/cpp/ExceptionObjHolderTest.cpp | 109 ++++++++++++++++++ .../runtime/src/mm/cpp/StableRefRegistry.hpp | 2 + .../runtime/src/mm/cpp/TestSupport.hpp | 36 ++++++ .../runtime/src/mm/cpp/ThreadStateTest.cpp | 45 +++----- 17 files changed, 319 insertions(+), 64 deletions(-) create mode 100644 kotlin-native/backend.native/tests/runtime/exceptions/rethrow.kt create mode 100644 kotlin-native/backend.native/tests/runtime/exceptions/throw_from_catch.kt create mode 100644 kotlin-native/runtime/src/mm/cpp/ExceptionObjHolder.cpp create mode 100644 kotlin-native/runtime/src/mm/cpp/ExceptionObjHolderTest.cpp create mode 100644 kotlin-native/runtime/src/mm/cpp/TestSupport.hpp diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CAdapterGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CAdapterGenerator.kt index 5becbc2f988..e9c1941960d 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CAdapterGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CAdapterGenerator.kt @@ -461,7 +461,7 @@ private class ExportedElement(val kind: ElementKind, "result", cfunction[0], Direction.KOTLIN_TO_C, builder) builder.append(" return $result;\n") } - builder.append(" } catch (ExceptionObjHolder& e) { TerminateWithUnhandledException(e.obj()); } \n") + builder.append(" } catch (ExceptionObjHolder& e) { TerminateWithUnhandledException(e.GetExceptionObject()); } \n") builder.append("}\n") @@ -905,7 +905,6 @@ internal class CAdapterGenerator(val context: Context) : DeclarationDescriptorVi |#define RUNTIME_NORETURN __attribute__((noreturn)) | |extern "C" { - |void UpdateHeapRef(KObjHeader**, const KObjHeader*) RUNTIME_NOTHROW; |void UpdateStackRef(KObjHeader**, const KObjHeader*) RUNTIME_NOTHROW; |KObjHeader* AllocInstance(const KTypeInfo*, KObjHeader**) RUNTIME_NOTHROW; |KObjHeader* DerefStablePointer(void*, KObjHeader**) RUNTIME_NOTHROW; @@ -951,16 +950,10 @@ internal class CAdapterGenerator(val context: Context) : DeclarationDescriptorVi |}; | |class ExceptionObjHolder { - | public: - | explicit ExceptionObjHolder(const KObjHeader* obj): obj_(nullptr) { - | ::UpdateHeapRef(&obj_, obj); - | } - | ~ExceptionObjHolder() { - | UpdateHeapRef(&obj_, nullptr); - | } - | KObjHeader* obj() { return obj_; } - | private: - | KObjHeader* obj_; + |public: + | virtual ~ExceptionObjHolder() = default; + | + | KObjHeader* GetExceptionObject() noexcept; |}; | |static void DisposeStablePointerImpl(${prefix}_KNativePtr ptr) { diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt index 097d7731e33..8388d41b9d9 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt @@ -862,14 +862,10 @@ internal class FunctionGenerationContext(val function: LLVMValueRef, val beginCatch = context.llvm.cxaBeginCatchFunction val exceptionRawPtr = call(beginCatch, listOf(exceptionRecord)) - // Pointer to KotlinException instance: - val exceptionPtrPtr = bitcast(codegen.kObjHeaderPtrPtr, exceptionRawPtr, "") - // Pointer to Kotlin exception object: - // We do need a slot here, as otherwise exception instance could be freed by _cxa_end_catch. - val exceptionPtr = loadSlot(exceptionPtrPtr, true, "exception") + val exceptionPtr = call(context.llvm.Kotlin_getExceptionObject, listOf(exceptionRawPtr), Lifetime.GLOBAL) - // __cxa_end_catch performs some C++ cleanup, including calling `KotlinException` class destructor. + // __cxa_end_catch performs some C++ cleanup, including calling `ExceptionObjHolder` class destructor. val endCatch = context.llvm.cxaEndCatchFunction call(endCatch, listOf()) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt index b6c840a2133..150a1dfc98f 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt @@ -516,6 +516,7 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) { val checkLifetimesConstraint = importRtFunction("CheckLifetimesConstraint") val freezeSubgraph = importRtFunction("FreezeSubgraph") val checkGlobalsAccessible = importRtFunction("CheckGlobalsAccessible") + val Kotlin_getExceptionObject = importRtFunction("Kotlin_getExceptionObject") val kRefSharedHolderInitLocal = importRtFunction("KRefSharedHolder_initLocal") val kRefSharedHolderInit = importRtFunction("KRefSharedHolder_init") diff --git a/kotlin-native/backend.native/tests/build.gradle b/kotlin-native/backend.native/tests/build.gradle index 42d5c0c2ba2..2129fcbc625 100644 --- a/kotlin-native/backend.native/tests/build.gradle +++ b/kotlin-native/backend.native/tests/build.gradle @@ -2545,6 +2545,16 @@ standaloneTest("exception_in_global_init") { outputChecker = { s -> s.contains("Uncaught Kotlin exception: kotlin.IllegalStateException: FAIL") && !s.contains("in kotlin main") } } +task rethrow_exception(type: KonanLocalTest) { + enabled = (project.testTarget != 'wasm32') // Uses exceptions. + source = "runtime/exceptions/rethrow.kt" +} + +task throw_from_catch(type: KonanLocalTest) { + enabled = (project.testTarget != 'wasm32') // Uses exceptions. + source = "runtime/exceptions/throw_from_catch.kt" +} + standaloneTest("runtime_math_exceptions") { enabled = (project.testTarget != 'wasm32') source = "stdlib_external/numbers/MathExceptionTest.kt" diff --git a/kotlin-native/backend.native/tests/runtime/exceptions/rethrow.kt b/kotlin-native/backend.native/tests/runtime/exceptions/rethrow.kt new file mode 100644 index 00000000000..5de33887ae6 --- /dev/null +++ b/kotlin-native/backend.native/tests/runtime/exceptions/rethrow.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +package runtime.exceptions.rethtow + +import kotlin.test.* + +@Test +fun runTest() { + assertFailsWith("My error") { + try { + error("My error") + } catch (e: Throwable) { + throw e + } + } +} diff --git a/kotlin-native/backend.native/tests/runtime/exceptions/throw_from_catch.kt b/kotlin-native/backend.native/tests/runtime/exceptions/throw_from_catch.kt new file mode 100644 index 00000000000..1522b735d21 --- /dev/null +++ b/kotlin-native/backend.native/tests/runtime/exceptions/throw_from_catch.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +package runtime.exceptions.throw_from_catch + +import kotlin.test.* + +@Test +fun runTest() { + assertFailsWith("My another error") { + try { + error("My error") + } catch (e: Throwable) { + error("My another error") + } + } +} diff --git a/kotlin-native/runtime/src/legacymm/cpp/Memory.cpp b/kotlin-native/runtime/src/legacymm/cpp/Memory.cpp index 8d5b166f42f..bc5e6ceebd6 100644 --- a/kotlin-native/runtime/src/legacymm/cpp/Memory.cpp +++ b/kotlin-native/runtime/src/legacymm/cpp/Memory.cpp @@ -440,6 +440,20 @@ void setContainerFor(ObjHeader* obj, ContainerHeader* container) { obj->typeInfoOrMeta_ = setPointerBits(obj->typeInfoOrMeta_, OBJECT_TAG_NONTRIVIAL_CONTAINER); } +#if !KONAN_NO_EXCEPTIONS +class ExceptionObjHolderImpl : public ExceptionObjHolder { +public: + explicit ExceptionObjHolderImpl(ObjHeader* obj) noexcept { ::SetHeapRef(&obj_, obj); } + + ~ExceptionObjHolderImpl() override { ZeroHeapRef(&obj_); } + + ObjHeader* obj() noexcept { return obj_; } + +private: + ObjHeader* obj_; +}; +#endif + } // namespace ContainerHeader* containerFor(const ObjHeader* obj) { @@ -3691,3 +3705,14 @@ ALWAYS_INLINE RUNTIME_NOTHROW void Kotlin_mm_safePointExceptionUnwind() { } } // extern "C" + +#if !KONAN_NO_EXCEPTIONS +// static +ALWAYS_INLINE RUNTIME_NORETURN void ExceptionObjHolder::Throw(ObjHeader* exception) { + throw ExceptionObjHolderImpl(exception); +} + +ALWAYS_INLINE ObjHeader* ExceptionObjHolder::GetExceptionObject() noexcept { + return static_cast(this)->obj(); +} +#endif diff --git a/kotlin-native/runtime/src/main/cpp/Exceptions.cpp b/kotlin-native/runtime/src/main/cpp/Exceptions.cpp index 596018f1b3c..5c604905658 100644 --- a/kotlin-native/runtime/src/main/cpp/Exceptions.cpp +++ b/kotlin-native/runtime/src/main/cpp/Exceptions.cpp @@ -208,7 +208,7 @@ void ThrowException(KRef exception) { PrintThrowable(exception); RuntimeCheck(false, "Exceptions unsupported"); #else - throw ExceptionObjHolder(exception); + ExceptionObjHolder::Throw(exception); #endif } @@ -252,6 +252,14 @@ RUNTIME_NORETURN void TerminateWithUnhandledException(KRef throwable) { }); } +ALWAYS_INLINE RUNTIME_NOTHROW OBJ_GETTER(Kotlin_getExceptionObject, void* holder) { +#if !KONAN_NO_EXCEPTIONS + RETURN_OBJ(static_cast(holder)->GetExceptionObject()); +#else + RETURN_OBJ(nullptr); +#endif +} + #if !KONAN_NO_EXCEPTIONS namespace { @@ -266,7 +274,7 @@ class TerminateHandler : private kotlin::Pinned { try { std::rethrow_exception(currentException); } catch (ExceptionObjHolder& e) { - processUnhandledKotlinException(e.obj()); + processUnhandledKotlinException(e.GetExceptionObject()); konan::abort(); } catch (...) { // Not a Kotlin exception - call default handler diff --git a/kotlin-native/runtime/src/main/cpp/Exceptions.h b/kotlin-native/runtime/src/main/cpp/Exceptions.h index f115c5f9d42..c5cb866dc56 100644 --- a/kotlin-native/runtime/src/main/cpp/Exceptions.h +++ b/kotlin-native/runtime/src/main/cpp/Exceptions.h @@ -38,6 +38,8 @@ RUNTIME_NORETURN void TerminateWithUnhandledException(KRef exception); void SetKonanTerminateHandler(); +RUNTIME_NOTHROW OBJ_GETTER(Kotlin_getExceptionObject, void* holder); + // The functions below are implemented in Kotlin (at package kotlin.native.internal). // Throws null pointer exception. Context is evaluated from caller's address. diff --git a/kotlin-native/runtime/src/main/cpp/Memory.h b/kotlin-native/runtime/src/main/cpp/Memory.h index fe767630995..6bf213010ca 100644 --- a/kotlin-native/runtime/src/main/cpp/Memory.h +++ b/kotlin-native/runtime/src/main/cpp/Memory.h @@ -22,6 +22,7 @@ #include "TypeInfo.h" #include "Atomic.h" #include "PointerBits.h" +#include "Utils.hpp" typedef enum { // Must match to permTag() in Kotlin. @@ -356,23 +357,16 @@ class ObjHolder { ObjHeader* obj_; }; -//! TODO Follow the Rule of Zero to prevent dangling on unintented copy ctor class ExceptionObjHolder { - public: - explicit ExceptionObjHolder(const ObjHeader* obj) { - ::SetHeapRef(&obj_, obj); - } +public: +#if !KONAN_NO_EXCEPTIONS + static void Throw(ObjHeader* exception) RUNTIME_NORETURN; - ~ExceptionObjHolder() { - ZeroHeapRef(&obj_); - } + ObjHeader* GetExceptionObject() noexcept; +#endif - ObjHeader* obj() { return obj_; } - - const ObjHeader* obj() const { return obj_; } - - private: - ObjHeader* obj_; + // Exceptions are not on a hot path, so having virtual dispatch is fine. + virtual ~ExceptionObjHolder() = default; }; #endif // RUNTIME_MEMORY_H diff --git a/kotlin-native/runtime/src/main/cpp/MultiSourceQueue.hpp b/kotlin-native/runtime/src/main/cpp/MultiSourceQueue.hpp index 30166c31b0d..fae3649807b 100644 --- a/kotlin-native/runtime/src/main/cpp/MultiSourceQueue.hpp +++ b/kotlin-native/runtime/src/main/cpp/MultiSourceQueue.hpp @@ -144,6 +144,11 @@ public: deletionQueue_ = std::move(remainingDeletions); } + void ClearForTests() noexcept { + queue_.clear(); + deletionQueue_.clear(); + } + private: // Using `KStdList` as it allows to implement `Collect` without memory allocations, // which is important for GC mark phase. diff --git a/kotlin-native/runtime/src/main/cpp/Worker.cpp b/kotlin-native/runtime/src/main/cpp/Worker.cpp index 50888740b17..004a4b20d65 100644 --- a/kotlin-native/runtime/src/main/cpp/Worker.cpp +++ b/kotlin-native/runtime/src/main/cpp/Worker.cpp @@ -959,7 +959,7 @@ JobKind Worker::processQueueElement(bool blocking) { WorkerLaunchpad(obj, dummyHolder.slot()); } catch (ExceptionObjHolder& e) { if (errorReporting()) - ReportUnhandledException(e.obj()); + ReportUnhandledException(e.GetExceptionObject()); } DisposeStablePointer(job.executeAfter.operation); break; @@ -979,7 +979,7 @@ JobKind Worker::processQueueElement(bool blocking) { } catch (ExceptionObjHolder& e) { ok = false; if (errorReporting()) - ReportUnhandledException(e.obj()); + ReportUnhandledException(e.GetExceptionObject()); } // Notify the future. job.regularJob.future->storeResultUnlocked(result, ok); diff --git a/kotlin-native/runtime/src/mm/cpp/ExceptionObjHolder.cpp b/kotlin-native/runtime/src/mm/cpp/ExceptionObjHolder.cpp new file mode 100644 index 00000000000..a984bbd078a --- /dev/null +++ b/kotlin-native/runtime/src/mm/cpp/ExceptionObjHolder.cpp @@ -0,0 +1,47 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +#include "Memory.h" + +#include "StableRefRegistry.hpp" +#include "ThreadData.hpp" +#include "ThreadRegistry.hpp" + +using namespace kotlin; + +namespace { + +#if !KONAN_NO_EXCEPTIONS +class ExceptionObjHolderImpl : public ExceptionObjHolder { +public: + explicit ExceptionObjHolderImpl(ObjHeader* obj) noexcept { + auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData(); + stableRef_ = threadData->stableRefThreadQueue().Insert(obj); + } + + ~ExceptionObjHolderImpl() override { + auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData(); + threadData->stableRefThreadQueue().Erase(stableRef_); + } + + ObjHeader* obj() noexcept { return **stableRef_; } + +private: + mm::StableRefRegistry::Node* stableRef_; +}; +#endif + +} // namespace + +#if !KONAN_NO_EXCEPTIONS +// static +RUNTIME_NORETURN void ExceptionObjHolder::Throw(ObjHeader* exception) { + throw ExceptionObjHolderImpl(exception); +} + +ObjHeader* ExceptionObjHolder::GetExceptionObject() noexcept { + return static_cast(this)->obj(); +} +#endif diff --git a/kotlin-native/runtime/src/mm/cpp/ExceptionObjHolderTest.cpp b/kotlin-native/runtime/src/mm/cpp/ExceptionObjHolderTest.cpp new file mode 100644 index 00000000000..30e5a919c23 --- /dev/null +++ b/kotlin-native/runtime/src/mm/cpp/ExceptionObjHolderTest.cpp @@ -0,0 +1,109 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "Memory.h" +#include "TestSupport.hpp" +#include "ThreadData.hpp" +#include "Types.h" + +using namespace kotlin; + +namespace { + +class ExceptionObjHolderTest : public ::testing::Test { +public: + ~ExceptionObjHolderTest() { + auto& stableRefs = mm::StableRefRegistry::Instance(); + stableRefs.ClearForTests(); + } + + static KStdVector Collect(mm::ThreadData& threadData) { + auto& stableRefs = mm::StableRefRegistry::Instance(); + stableRefs.ProcessThread(&threadData); + stableRefs.ProcessDeletions(); + KStdVector result; + for (const auto& obj : stableRefs.Iter()) { + result.push_back(obj); + } + return result; + } + +private: +}; + +} // namespace + +TEST_F(ExceptionObjHolderTest, NothingByDefault) { + mm::RunInNewThread([](mm::ThreadData& threadData) { EXPECT_THAT(Collect(threadData), testing::IsEmpty()); }); +} + +TEST_F(ExceptionObjHolderTest, Throw) { + mm::RunInNewThread([](mm::ThreadData& threadData) { + ASSERT_THAT(Collect(threadData), testing::IsEmpty()); + + ObjHeader exception; + try { + ExceptionObjHolder::Throw(&exception); + } catch (...) { + EXPECT_THAT(Collect(threadData), testing::ElementsAre(&exception)); + } + EXPECT_THAT(Collect(threadData), testing::IsEmpty()); + }); +} + +TEST_F(ExceptionObjHolderTest, ThrowInsideCatch) { + mm::RunInNewThread([](mm::ThreadData& threadData) { + ASSERT_THAT(Collect(threadData), testing::IsEmpty()); + + ObjHeader exception1; + try { + ExceptionObjHolder::Throw(&exception1); + } catch (...) { + ObjHeader exception2; + try { + ExceptionObjHolder::Throw(&exception2); + } catch (...) { + EXPECT_THAT(Collect(threadData), testing::ElementsAre(&exception1, &exception2)); + } + EXPECT_THAT(Collect(threadData), testing::ElementsAre(&exception1)); + } + EXPECT_THAT(Collect(threadData), testing::IsEmpty()); + }); +} + +TEST_F(ExceptionObjHolderTest, StoreException) { + mm::RunInNewThread([](mm::ThreadData& threadData) { + ASSERT_THAT(Collect(threadData), testing::IsEmpty()); + + ObjHeader exception1; + std::exception_ptr storedException1; + try { + ExceptionObjHolder::Throw(&exception1); + } catch (...) { + storedException1 = std::current_exception(); + } + EXPECT_THAT(Collect(threadData), testing::ElementsAre(&exception1)); + + ObjHeader exception2; + std::exception_ptr storedException2; + try { + ExceptionObjHolder::Throw(&exception2); + } catch (...) { + storedException2 = std::current_exception(); + } + EXPECT_THAT(Collect(threadData), testing::ElementsAre(&exception1, &exception2)); + + storedException1 = std::exception_ptr(); + EXPECT_THAT(Collect(threadData), testing::ElementsAre(&exception2)); + + storedException2 = std::exception_ptr(); + EXPECT_THAT(Collect(threadData), testing::IsEmpty()); + }); +} diff --git a/kotlin-native/runtime/src/mm/cpp/StableRefRegistry.hpp b/kotlin-native/runtime/src/mm/cpp/StableRefRegistry.hpp index 6ed972c15e6..ec272554d4e 100644 --- a/kotlin-native/runtime/src/mm/cpp/StableRefRegistry.hpp +++ b/kotlin-native/runtime/src/mm/cpp/StableRefRegistry.hpp @@ -48,6 +48,8 @@ public: // much of a problem is it. Iterable Iter() noexcept { return stableRefs_.Iter(); } + void ClearForTests() noexcept { stableRefs_.ClearForTests(); } + private: // Current approach optimizes for creating and disposing of stable refs: // * creation just enqueues ref, disposing either queues or deletes the ref immediately (if it still resides in the current queue). diff --git a/kotlin-native/runtime/src/mm/cpp/TestSupport.hpp b/kotlin-native/runtime/src/mm/cpp/TestSupport.hpp new file mode 100644 index 00000000000..62800541de8 --- /dev/null +++ b/kotlin-native/runtime/src/mm/cpp/TestSupport.hpp @@ -0,0 +1,36 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +#include "../../main/cpp/TestSupport.hpp" + +#include + +#include "ThreadData.hpp" +#include "ThreadRegistry.hpp" + +namespace kotlin { +namespace mm { + +template +void RunInNewThread(F f) { + std::thread([&f]() { + class ScopedRegistration : private kotlin::Pinned { + public: + ScopedRegistration() : node_(mm::ThreadRegistry::Instance().RegisterCurrentThread()) {} + + ~ScopedRegistration() { mm::ThreadRegistry::Instance().Unregister(node_); } + + mm::ThreadData& threadData() { return *node_->Get(); } + + private: + mm::ThreadRegistry::Node* node_; + } registration; + + f(registration.threadData()); + }).join(); +} + +} // namespace mm +} // namespace kotlin diff --git a/kotlin-native/runtime/src/mm/cpp/ThreadStateTest.cpp b/kotlin-native/runtime/src/mm/cpp/ThreadStateTest.cpp index 75f0ef5ed69..92218c1cafb 100644 --- a/kotlin-native/runtime/src/mm/cpp/ThreadStateTest.cpp +++ b/kotlin-native/runtime/src/mm/cpp/ThreadStateTest.cpp @@ -7,69 +7,58 @@ #include "gtest/gtest.h" +#include "TestSupport.hpp" #include "ThreadData.hpp" -#include "ThreadRegistry.hpp" #include "ThreadState.hpp" using namespace kotlin; TEST(ThreadStateTest, StateSwitch) { - std::thread t([]() { - mm::ThreadRegistry::Instance().RegisterCurrentThread(); - - auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData(); - auto initialState = threadData->state(); + mm::RunInNewThread([](mm::ThreadData& threadData) { + auto initialState = threadData.state(); EXPECT_EQ(mm::ThreadState::kRunnable, initialState); - mm::ThreadState oldState = mm::SwitchThreadState(threadData, mm::ThreadState::kNative); + mm::ThreadState oldState = mm::SwitchThreadState(&threadData, mm::ThreadState::kNative); EXPECT_EQ(initialState, oldState); - EXPECT_EQ(mm::ThreadState::kNative, threadData->state()); + EXPECT_EQ(mm::ThreadState::kNative, threadData.state()); // Check functions exported for the compiler too. Kotlin_mm_switchThreadStateRunnable(); - EXPECT_EQ(mm::ThreadState::kRunnable, threadData->state()); + EXPECT_EQ(mm::ThreadState::kRunnable, threadData.state()); Kotlin_mm_switchThreadStateNative(); - EXPECT_EQ(mm::ThreadState::kNative, threadData->state()); + EXPECT_EQ(mm::ThreadState::kNative, threadData.state()); }); - t.join(); } TEST(ThreadStateTest, StateGuard) { - std::thread t([]() { - mm::ThreadRegistry::Instance().RegisterCurrentThread(); - auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData(); - auto initialState = threadData->state(); + mm::RunInNewThread([](mm::ThreadData& threadData) { + auto initialState = threadData.state(); EXPECT_EQ(mm::ThreadState::kRunnable, initialState); { - mm::ThreadStateGuard guard(threadData, mm::ThreadState::kNative); - EXPECT_EQ(mm::ThreadState::kNative, threadData->state()); + mm::ThreadStateGuard guard(&threadData, mm::ThreadState::kNative); + EXPECT_EQ(mm::ThreadState::kNative, threadData.state()); } - EXPECT_EQ(initialState, threadData->state()); + EXPECT_EQ(initialState, threadData.state()); }); - t.join(); } TEST(ThreadStateDeathTest, StateAsserts) { - std::thread t([]() { - auto* threadData = mm::ThreadRegistry::Instance().RegisterCurrentThread()->Get(); - EXPECT_DEATH(mm::AssertThreadState(threadData, mm::ThreadState::kNative), + mm::RunInNewThread([](mm::ThreadData& threadData) { + EXPECT_DEATH(mm::AssertThreadState(&threadData, mm::ThreadState::kNative), "runtime assert: Unexpected thread state. Expected: NATIVE. Actual: RUNNABLE"); }); - t.join(); } TEST(ThreadStateDeathTest, IncorrectStateSwitch) { - std::thread t([]() { - auto* threadData = mm::ThreadRegistry::Instance().RegisterCurrentThread()->Get(); - EXPECT_DEATH(mm::SwitchThreadState(threadData, kotlin::mm::ThreadState::kRunnable), + mm::RunInNewThread([](mm::ThreadData& threadData) { + EXPECT_DEATH(mm::SwitchThreadState(&threadData, kotlin::mm::ThreadState::kRunnable), "runtime assert: Illegal thread state switch. Old state: RUNNABLE. New state: RUNNABLE"); EXPECT_DEATH(Kotlin_mm_switchThreadStateRunnable(), "runtime assert: Illegal thread state switch. Old state: RUNNABLE. New state: RUNNABLE"); - mm::SwitchThreadState(threadData, kotlin::mm::ThreadState::kNative); + mm::SwitchThreadState(&threadData, kotlin::mm::ThreadState::kNative); EXPECT_DEATH(Kotlin_mm_switchThreadStateNative(), "runtime assert: Illegal thread state switch. Old state: NATIVE. New state: NATIVE"); }); - t.join(); }