diff --git a/kotlin-native/runtime/src/legacymm/cpp/TestSupport.cpp b/kotlin-native/runtime/src/legacymm/cpp/TestSupport.cpp new file mode 100644 index 00000000000..5164460fb2c --- /dev/null +++ b/kotlin-native/runtime/src/legacymm/cpp/TestSupport.cpp @@ -0,0 +1,14 @@ +/* + * 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 "MemoryPrivate.hpp" +#include "TestSupport.hpp" + + +// No-op, required for the new MM only. +MemoryState* kotlin::InitMemoryForTests() { return nullptr; } + +// No-op, required for the new MM only. +void kotlin::DeinitMemoryForTests(MemoryState* state) {} \ No newline at end of file diff --git a/kotlin-native/runtime/src/main/cpp/TestSupport.hpp b/kotlin-native/runtime/src/main/cpp/TestSupport.hpp index f2e7b2ade92..b01fc45effb 100644 --- a/kotlin-native/runtime/src/main/cpp/TestSupport.hpp +++ b/kotlin-native/runtime/src/main/cpp/TestSupport.hpp @@ -3,6 +3,11 @@ * that can be found in the LICENSE file. */ +#include +#include + +#include "Memory.h" + namespace kotlin { #if KONAN_WINDOWS @@ -15,4 +20,44 @@ constexpr int kDefaultThreadCount = 10; constexpr int kDefaultThreadCount = 100; #endif +// Performs minimal initialization of the current thread memory subsystem. +// This initilization is enough for running tests for the C++ part for the stdlib. +// - For the new MM: registeres the current thread in the thread registry. +// - For the legacy MM: does nothing, returns nullptr. +MemoryState* InitMemoryForTests(); + +// Deinitiliazes memory subsystem used for C++ stdlib tests. +// - For the new MM: unregisteres the current thread, nullify current thread data. +// - For the legacy MM: does nothing. +void DeinitMemoryForTests(MemoryState* state); + +// Scopely initializes the memory subsystem using the functions above. +class ScopedMemoryInit : private kotlin::Pinned { +public: + ScopedMemoryInit() : memoryState_(InitMemoryForTests()) {} + ~ScopedMemoryInit() { + ClearMemoryForTests(memoryState()); + DeinitMemoryForTests(memoryState_); + } + + MemoryState* memoryState() { return memoryState_; } +private: + MemoryState* memoryState_; +}; + +// Runs the given function in a separate thread with minimally initialized memory subsystem. +inline void RunInNewThread(std::function f) { + std::thread([&f]() { + ScopedMemoryInit init; + f(init.memoryState()); + }).join(); +} + +// Runs the given function in a separate thread with minimally initialized memory subsystem. +inline void RunInNewThread(std::function f) { + RunInNewThread([&f](MemoryState* unused) { + f(); + }); +} + } // namespace kotlin diff --git a/kotlin-native/runtime/src/mm/cpp/ExceptionObjHolderTest.cpp b/kotlin-native/runtime/src/mm/cpp/ExceptionObjHolderTest.cpp index 6f7d7d62f02..7fd91b07634 100644 --- a/kotlin-native/runtime/src/mm/cpp/ExceptionObjHolderTest.cpp +++ b/kotlin-native/runtime/src/mm/cpp/ExceptionObjHolderTest.cpp @@ -36,11 +36,11 @@ private: } // namespace TEST_F(ExceptionObjHolderTest, NothingByDefault) { - mm::RunInNewThread([](mm::ThreadData& threadData) { EXPECT_THAT(Collect(threadData), testing::IsEmpty()); }); + RunInNewThread([](mm::ThreadData& threadData) { EXPECT_THAT(Collect(threadData), testing::IsEmpty()); }); } TEST_F(ExceptionObjHolderTest, Throw) { - mm::RunInNewThread([](mm::ThreadData& threadData) { + RunInNewThread([](mm::ThreadData& threadData) { ASSERT_THAT(Collect(threadData), testing::IsEmpty()); ObjHeader exception; @@ -54,7 +54,7 @@ TEST_F(ExceptionObjHolderTest, Throw) { } TEST_F(ExceptionObjHolderTest, ThrowInsideCatch) { - mm::RunInNewThread([](mm::ThreadData& threadData) { + RunInNewThread([](mm::ThreadData& threadData) { ASSERT_THAT(Collect(threadData), testing::IsEmpty()); ObjHeader exception1; @@ -74,7 +74,7 @@ TEST_F(ExceptionObjHolderTest, ThrowInsideCatch) { } TEST_F(ExceptionObjHolderTest, StoreException) { - mm::RunInNewThread([](mm::ThreadData& threadData) { + RunInNewThread([](mm::ThreadData& threadData) { ASSERT_THAT(Collect(threadData), testing::IsEmpty()); ObjHeader exception1; diff --git a/kotlin-native/runtime/src/mm/cpp/Memory.cpp b/kotlin-native/runtime/src/mm/cpp/Memory.cpp index b321b8d907c..307fb1e1458 100644 --- a/kotlin-native/runtime/src/mm/cpp/Memory.cpp +++ b/kotlin-native/runtime/src/mm/cpp/Memory.cpp @@ -99,8 +99,7 @@ extern "C" void RestoreMemory(MemoryState*) { } extern "C" void ClearMemoryForTests(MemoryState* state) { - auto* threadData = FromMemoryState(state)->Get(); - threadData->ClearForTests(); + state->GetThreadData()->ClearForTests(); } extern "C" RUNTIME_NOTHROW OBJ_GETTER(AllocInstance, const TypeInfo* typeInfo) { diff --git a/kotlin-native/runtime/src/mm/cpp/TestSupport.cpp b/kotlin-native/runtime/src/mm/cpp/TestSupport.cpp new file mode 100644 index 00000000000..0f8fd2efe72 --- /dev/null +++ b/kotlin-native/runtime/src/mm/cpp/TestSupport.cpp @@ -0,0 +1,20 @@ +/* + * 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 "MemoryPrivate.hpp" +#include "TestSupport.hpp" +#include "ThreadRegistry.hpp" + +MemoryState* kotlin::InitMemoryForTests() { + auto threadDataNode = mm::ThreadRegistry::Instance().RegisterCurrentThread(); + return mm::ToMemoryState(threadDataNode); +} + +void kotlin::DeinitMemoryForTests(MemoryState* state) { + auto threadDataNode = mm::FromMemoryState(state); + mm::ThreadRegistry::Instance().Unregister(threadDataNode); + // Nullify current thread data. The thread is still alive, so this is safe. + mm::ThreadRegistry::TestSupport::SetCurrentThreadData(nullptr); +} \ No newline at end of file diff --git a/kotlin-native/runtime/src/mm/cpp/TestSupport.hpp b/kotlin-native/runtime/src/mm/cpp/TestSupport.hpp index 9937a45bad9..ec8c6bc65bf 100644 --- a/kotlin-native/runtime/src/mm/cpp/TestSupport.hpp +++ b/kotlin-native/runtime/src/mm/cpp/TestSupport.hpp @@ -5,34 +5,15 @@ #include "../../main/cpp/TestSupport.hpp" -#include - +#include "MemoryPrivate.hpp" #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()); - - registration.threadData().ClearForTests(); - }).join(); +inline void RunInNewThread(std::function f) { + kotlin::RunInNewThread([&f](MemoryState* state) { + f(*state->GetThreadData()); + }); } -} // namespace mm } // namespace kotlin diff --git a/kotlin-native/runtime/src/mm/cpp/ThreadRegistry.hpp b/kotlin-native/runtime/src/mm/cpp/ThreadRegistry.hpp index 58b494b9ced..d470863f407 100644 --- a/kotlin-native/runtime/src/mm/cpp/ThreadRegistry.hpp +++ b/kotlin-native/runtime/src/mm/cpp/ThreadRegistry.hpp @@ -36,8 +36,16 @@ public: // Using this after `Unregister` for the thread has been called is undefined behaviour. ThreadData* CurrentThreadData() const noexcept { return currentThreadData_; } + class TestSupport { + public: + static void SetCurrentThreadData(ThreadData* newThreadData) { + ThreadRegistry::currentThreadData_ = newThreadData; + } + }; + private: friend class GlobalData; + friend class ThreadRegistry::TestSupport; ThreadRegistry(); ~ThreadRegistry(); diff --git a/kotlin-native/runtime/src/mm/cpp/ThreadStateTest.cpp b/kotlin-native/runtime/src/mm/cpp/ThreadStateTest.cpp index 92218c1cafb..3e6b54d90b6 100644 --- a/kotlin-native/runtime/src/mm/cpp/ThreadStateTest.cpp +++ b/kotlin-native/runtime/src/mm/cpp/ThreadStateTest.cpp @@ -14,7 +14,7 @@ using namespace kotlin; TEST(ThreadStateTest, StateSwitch) { - mm::RunInNewThread([](mm::ThreadData& threadData) { + RunInNewThread([](mm::ThreadData& threadData) { auto initialState = threadData.state(); EXPECT_EQ(mm::ThreadState::kRunnable, initialState); @@ -32,7 +32,7 @@ TEST(ThreadStateTest, StateSwitch) { } TEST(ThreadStateTest, StateGuard) { - mm::RunInNewThread([](mm::ThreadData& threadData) { + RunInNewThread([](mm::ThreadData& threadData) { auto initialState = threadData.state(); EXPECT_EQ(mm::ThreadState::kRunnable, initialState); { @@ -44,14 +44,14 @@ TEST(ThreadStateTest, StateGuard) { } TEST(ThreadStateDeathTest, StateAsserts) { - mm::RunInNewThread([](mm::ThreadData& threadData) { + RunInNewThread([](mm::ThreadData& threadData) { EXPECT_DEATH(mm::AssertThreadState(&threadData, mm::ThreadState::kNative), "runtime assert: Unexpected thread state. Expected: NATIVE. Actual: RUNNABLE"); }); } TEST(ThreadStateDeathTest, IncorrectStateSwitch) { - mm::RunInNewThread([](mm::ThreadData& threadData) { + 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(),