[K/N][New MM] Move RunInNewThread to the main ThreadSupport.hpp

Some of unit-tests for the C++ part of the stdlib will require
thread state switching. In the new MM, we already have a helper
function that allows us to scopely initialize the memory subsystem
for a separate thread. This patch makes this helper available
for tests in the main runtime module by moving it to the main
ThreadSupport.hpp.
This commit is contained in:
Ilya Matveev
2021-02-28 21:11:29 +07:00
committed by TeamCityServer
parent fc43fbf578
commit 482305cfb2
8 changed files with 101 additions and 34 deletions
@@ -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) {}
@@ -3,6 +3,11 @@
* that can be found in the LICENSE file.
*/
#include <functional>
#include <thread>
#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<void(MemoryState*)> 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<void()> f) {
RunInNewThread([&f](MemoryState* unused) {
f();
});
}
} // namespace kotlin
@@ -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;
+1 -2
View File
@@ -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) {
@@ -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);
}
@@ -5,34 +5,15 @@
#include "../../main/cpp/TestSupport.hpp"
#include <thread>
#include "MemoryPrivate.hpp"
#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());
registration.threadData().ClearForTests();
}).join();
inline void RunInNewThread(std::function<void(mm::ThreadData&)> f) {
kotlin::RunInNewThread([&f](MemoryState* state) {
f(*state->GetThreadData());
});
}
} // namespace mm
} // namespace kotlin
@@ -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();
@@ -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(),