[K/N][New MM] Add runtime asserts to state switch functions
This commit is contained in:
@@ -29,12 +29,14 @@ std::string kotlin::internal::statesToString(std::initializer_list<ThreadState>
|
|||||||
}
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE ThreadState kotlin::SwitchThreadState(MemoryState* thread, ThreadState newState, bool reentrant) noexcept {
|
ALWAYS_INLINE ThreadState kotlin::SwitchThreadState(MemoryState* thread, ThreadState newState, bool reentrant) noexcept {
|
||||||
|
RuntimeAssert(thread != nullptr, "thread must not be nullptr");
|
||||||
return SwitchThreadState(thread->GetThreadData(), newState, reentrant);
|
return SwitchThreadState(thread->GetThreadData(), newState, reentrant);
|
||||||
}
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE void kotlin::AssertThreadState(MemoryState* thread, ThreadState expected) noexcept {
|
ALWAYS_INLINE void kotlin::AssertThreadState(MemoryState* thread, ThreadState expected) noexcept {
|
||||||
// Avoid redundant read in GetThreadData if runtime asserts are disabled.
|
// Avoid redundant read in GetThreadData if runtime asserts are disabled.
|
||||||
if (compiler::runtimeAssertsMode() != compiler::RuntimeAssertsMode::kIgnore) {
|
if (compiler::runtimeAssertsMode() != compiler::RuntimeAssertsMode::kIgnore) {
|
||||||
|
RuntimeAssert(thread != nullptr, "thread must not be nullptr");
|
||||||
AssertThreadState(thread->GetThreadData(), expected);
|
AssertThreadState(thread->GetThreadData(), expected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,6 +44,7 @@ ALWAYS_INLINE void kotlin::AssertThreadState(MemoryState* thread, ThreadState ex
|
|||||||
ALWAYS_INLINE void kotlin::AssertThreadState(MemoryState* thread, std::initializer_list<ThreadState> expected) noexcept {
|
ALWAYS_INLINE void kotlin::AssertThreadState(MemoryState* thread, std::initializer_list<ThreadState> expected) noexcept {
|
||||||
// Avoid redundant read in GetThreadData if runtime asserts are disabled.
|
// Avoid redundant read in GetThreadData if runtime asserts are disabled.
|
||||||
if (compiler::runtimeAssertsMode() != compiler::RuntimeAssertsMode::kIgnore) {
|
if (compiler::runtimeAssertsMode() != compiler::RuntimeAssertsMode::kIgnore) {
|
||||||
|
RuntimeAssert(thread != nullptr, "thread must not be nullptr");
|
||||||
AssertThreadState(thread->GetThreadData(), expected);
|
AssertThreadState(thread->GetThreadData(), expected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ const char* ThreadStateName(ThreadState state) noexcept;
|
|||||||
|
|
||||||
// Switches the state of the given thread to `newState` and returns the previous thread state.
|
// Switches the state of the given thread to `newState` and returns the previous thread state.
|
||||||
ALWAYS_INLINE inline ThreadState SwitchThreadState(mm::ThreadData* threadData, ThreadState newState, bool reentrant = false) noexcept {
|
ALWAYS_INLINE inline ThreadState SwitchThreadState(mm::ThreadData* threadData, ThreadState newState, bool reentrant = false) noexcept {
|
||||||
|
RuntimeAssert(threadData != nullptr, "threadData must not be nullptr");
|
||||||
auto oldState = threadData->setState(newState);
|
auto oldState = threadData->setState(newState);
|
||||||
// TODO(perf): Mesaure the impact of this assert in debug and opt modes.
|
// TODO(perf): Mesaure the impact of this assert in debug and opt modes.
|
||||||
RuntimeAssert(internal::isStateSwitchAllowed(oldState, newState, reentrant),
|
RuntimeAssert(internal::isStateSwitchAllowed(oldState, newState, reentrant),
|
||||||
@@ -42,6 +43,7 @@ ALWAYS_INLINE inline void AssertThreadState(mm::ThreadData* threadData, ThreadSt
|
|||||||
// even if its result is unused due to disabled runtime asserts.
|
// even if its result is unused due to disabled runtime asserts.
|
||||||
// So we explicitly avoid the read if asserts are disabled.
|
// So we explicitly avoid the read if asserts are disabled.
|
||||||
if (compiler::runtimeAssertsMode() != compiler::RuntimeAssertsMode::kIgnore) {
|
if (compiler::runtimeAssertsMode() != compiler::RuntimeAssertsMode::kIgnore) {
|
||||||
|
RuntimeAssert(threadData != nullptr, "threadData must not be nullptr");
|
||||||
auto actual = threadData->state();
|
auto actual = threadData->state();
|
||||||
RuntimeAssert(
|
RuntimeAssert(
|
||||||
actual == expected, "Unexpected thread state. Expected: %s. Actual: %s.", ThreadStateName(expected),
|
actual == expected, "Unexpected thread state. Expected: %s. Actual: %s.", ThreadStateName(expected),
|
||||||
@@ -54,6 +56,7 @@ ALWAYS_INLINE inline void AssertThreadState(mm::ThreadData* threadData, std::ini
|
|||||||
// even if its result is unused due to disabled runtime asserts.
|
// even if its result is unused due to disabled runtime asserts.
|
||||||
// So we explicitly avoid the read if asserts are disabled.
|
// So we explicitly avoid the read if asserts are disabled.
|
||||||
if (compiler::runtimeAssertsMode() != compiler::RuntimeAssertsMode::kIgnore) {
|
if (compiler::runtimeAssertsMode() != compiler::RuntimeAssertsMode::kIgnore) {
|
||||||
|
RuntimeAssert(threadData != nullptr, "threadData must not be nullptr");
|
||||||
auto actual = threadData->state();
|
auto actual = threadData->state();
|
||||||
RuntimeAssert(
|
RuntimeAssert(
|
||||||
std::any_of(expected.begin(), expected.end(), [actual](ThreadState expected) { return expected == actual; }),
|
std::any_of(expected.begin(), expected.end(), [actual](ThreadState expected) { return expected == actual; }),
|
||||||
|
|||||||
@@ -169,6 +169,23 @@ TEST(ThreadStateDeathTest, StateAsserts) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(ThreadStateDeathTest, StateAssertsForDetachedThread) {
|
||||||
|
EXPECT_DEATH(AssertThreadState(static_cast<MemoryState*>(nullptr), ThreadState::kNative),
|
||||||
|
"runtime assert: thread must not be nullptr");
|
||||||
|
EXPECT_DEATH(AssertThreadState(static_cast<mm::ThreadData*>(nullptr), ThreadState::kNative),
|
||||||
|
"runtime assert: threadData must not be nullptr");
|
||||||
|
EXPECT_DEATH(AssertThreadState(ThreadState::kNative),
|
||||||
|
"runtime assert: thread must not be nullptr");
|
||||||
|
|
||||||
|
EXPECT_DEATH(AssertThreadState(static_cast<MemoryState*>(nullptr), {ThreadState::kNative}),
|
||||||
|
"runtime assert: thread must not be nullptr");
|
||||||
|
EXPECT_DEATH(AssertThreadState(static_cast<mm::ThreadData*>(nullptr), {ThreadState::kNative}),
|
||||||
|
"runtime assert: threadData must not be nullptr");
|
||||||
|
EXPECT_DEATH(AssertThreadState({ThreadState::kNative}),
|
||||||
|
"runtime assert: thread must not be nullptr");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
TEST(ThreadStateDeathTest, IncorrectStateSwitchWithDifferentFunctions) {
|
TEST(ThreadStateDeathTest, IncorrectStateSwitchWithDifferentFunctions) {
|
||||||
RunInNewThread([](MemoryState* memoryState) {
|
RunInNewThread([](MemoryState* memoryState) {
|
||||||
auto* threadData = memoryState->GetThreadData();
|
auto* threadData = memoryState->GetThreadData();
|
||||||
@@ -202,6 +219,14 @@ TEST(ThreadStateDeathTest, StateSwitchCorrectness) {
|
|||||||
"runtime assert: Illegal thread state switch. Old state: NATIVE. New state: NATIVE");
|
"runtime assert: Illegal thread state switch. Old state: NATIVE. New state: NATIVE");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(ThreadStateDeathTest, StateSwitchForDetachedThread) {
|
||||||
|
EXPECT_DEATH(SwitchThreadState(static_cast<MemoryState*>(nullptr), ThreadState::kNative), "thread must not be nullptr");
|
||||||
|
EXPECT_DEATH(SwitchThreadState(static_cast<mm::ThreadData*>(nullptr), ThreadState::kNative), "threadData must not be nullptr");
|
||||||
|
|
||||||
|
EXPECT_DEATH(Kotlin_mm_switchThreadStateNative(), "threadData must not be nullptr");
|
||||||
|
EXPECT_DEATH(Kotlin_mm_switchThreadStateRunnable(), "threadData must not be nullptr" );
|
||||||
|
}
|
||||||
|
|
||||||
TEST(ThreadStateDeathTest, ReentrantStateSwitch) {
|
TEST(ThreadStateDeathTest, ReentrantStateSwitch) {
|
||||||
RunInNewThread([](MemoryState* memoryState) {
|
RunInNewThread([](MemoryState* memoryState) {
|
||||||
auto* threadData = memoryState->GetThreadData();
|
auto* threadData = memoryState->GetThreadData();
|
||||||
@@ -234,3 +259,16 @@ TEST(ThreadStateDeathTest, MovingReentrantGuard) {
|
|||||||
testing::Not(testing::ContainsRegex("runtime assert: Illegal thread state switch.")));
|
testing::Not(testing::ContainsRegex("runtime assert: Illegal thread state switch.")));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(ThreadStateDeathTest, GuardForDetachedThread) {
|
||||||
|
auto expectedError = "thread must not be nullptr";
|
||||||
|
EXPECT_DEATH({ ThreadStateGuard guard(nullptr, ThreadState::kRunnable, false); }, expectedError);
|
||||||
|
EXPECT_DEATH({ ThreadStateGuard guard(nullptr, ThreadState::kNative, false); }, expectedError);
|
||||||
|
EXPECT_DEATH({ ThreadStateGuard guard(nullptr, ThreadState::kRunnable, true); }, expectedError);
|
||||||
|
EXPECT_DEATH({ ThreadStateGuard guard(nullptr, ThreadState::kNative, true); }, expectedError);
|
||||||
|
|
||||||
|
EXPECT_DEATH({ ThreadStateGuard guard(ThreadState::kRunnable, false); }, expectedError);
|
||||||
|
EXPECT_DEATH({ ThreadStateGuard guard(ThreadState::kNative, false); }, expectedError);
|
||||||
|
EXPECT_DEATH({ ThreadStateGuard guard(ThreadState::kRunnable, true); }, expectedError);
|
||||||
|
EXPECT_DEATH({ ThreadStateGuard guard(ThreadState::kNative, true); }, expectedError);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user