Track current exception (#4701)
This commit is contained in:
committed by
Vasily Levchenko
parent
a38e64a7c8
commit
3a5c6480a3
+5
-12
@@ -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) {
|
||||
|
||||
+2
-6
@@ -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())
|
||||
|
||||
|
||||
+1
@@ -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")
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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<IllegalStateException>("My error") {
|
||||
try {
|
||||
error("My error")
|
||||
} catch (e: Throwable) {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<IllegalStateException>("My another error") {
|
||||
try {
|
||||
error("My error")
|
||||
} catch (e: Throwable) {
|
||||
error("My another error")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<ExceptionObjHolderImpl*>(this)->obj();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -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<ExceptionObjHolder*>(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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<ExceptionObjHolderImpl*>(this)->obj();
|
||||
}
|
||||
#endif
|
||||
@@ -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 <thread>
|
||||
|
||||
#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<ObjHeader*> Collect(mm::ThreadData& threadData) {
|
||||
auto& stableRefs = mm::StableRefRegistry::Instance();
|
||||
stableRefs.ProcessThread(&threadData);
|
||||
stableRefs.ProcessDeletions();
|
||||
KStdVector<ObjHeader*> 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());
|
||||
});
|
||||
}
|
||||
@@ -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).
|
||||
|
||||
@@ -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 <thread>
|
||||
|
||||
#include "ThreadData.hpp"
|
||||
#include "ThreadRegistry.hpp"
|
||||
|
||||
namespace kotlin {
|
||||
namespace mm {
|
||||
|
||||
template <typename F>
|
||||
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
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user