[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:
committed by
TeamCityServer
parent
fc43fbf578
commit
482305cfb2
@@ -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.
|
* that can be found in the LICENSE file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "Memory.h"
|
||||||
|
|
||||||
namespace kotlin {
|
namespace kotlin {
|
||||||
|
|
||||||
#if KONAN_WINDOWS
|
#if KONAN_WINDOWS
|
||||||
@@ -15,4 +20,44 @@ constexpr int kDefaultThreadCount = 10;
|
|||||||
constexpr int kDefaultThreadCount = 100;
|
constexpr int kDefaultThreadCount = 100;
|
||||||
#endif
|
#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
|
} // namespace kotlin
|
||||||
|
|||||||
@@ -36,11 +36,11 @@ private:
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TEST_F(ExceptionObjHolderTest, NothingByDefault) {
|
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) {
|
TEST_F(ExceptionObjHolderTest, Throw) {
|
||||||
mm::RunInNewThread([](mm::ThreadData& threadData) {
|
RunInNewThread([](mm::ThreadData& threadData) {
|
||||||
ASSERT_THAT(Collect(threadData), testing::IsEmpty());
|
ASSERT_THAT(Collect(threadData), testing::IsEmpty());
|
||||||
|
|
||||||
ObjHeader exception;
|
ObjHeader exception;
|
||||||
@@ -54,7 +54,7 @@ TEST_F(ExceptionObjHolderTest, Throw) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ExceptionObjHolderTest, ThrowInsideCatch) {
|
TEST_F(ExceptionObjHolderTest, ThrowInsideCatch) {
|
||||||
mm::RunInNewThread([](mm::ThreadData& threadData) {
|
RunInNewThread([](mm::ThreadData& threadData) {
|
||||||
ASSERT_THAT(Collect(threadData), testing::IsEmpty());
|
ASSERT_THAT(Collect(threadData), testing::IsEmpty());
|
||||||
|
|
||||||
ObjHeader exception1;
|
ObjHeader exception1;
|
||||||
@@ -74,7 +74,7 @@ TEST_F(ExceptionObjHolderTest, ThrowInsideCatch) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ExceptionObjHolderTest, StoreException) {
|
TEST_F(ExceptionObjHolderTest, StoreException) {
|
||||||
mm::RunInNewThread([](mm::ThreadData& threadData) {
|
RunInNewThread([](mm::ThreadData& threadData) {
|
||||||
ASSERT_THAT(Collect(threadData), testing::IsEmpty());
|
ASSERT_THAT(Collect(threadData), testing::IsEmpty());
|
||||||
|
|
||||||
ObjHeader exception1;
|
ObjHeader exception1;
|
||||||
|
|||||||
@@ -99,8 +99,7 @@ extern "C" void RestoreMemory(MemoryState*) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void ClearMemoryForTests(MemoryState* state) {
|
extern "C" void ClearMemoryForTests(MemoryState* state) {
|
||||||
auto* threadData = FromMemoryState(state)->Get();
|
state->GetThreadData()->ClearForTests();
|
||||||
threadData->ClearForTests();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" RUNTIME_NOTHROW OBJ_GETTER(AllocInstance, const TypeInfo* typeInfo) {
|
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 "../../main/cpp/TestSupport.hpp"
|
||||||
|
|
||||||
#include <thread>
|
#include "MemoryPrivate.hpp"
|
||||||
|
|
||||||
#include "ThreadData.hpp"
|
#include "ThreadData.hpp"
|
||||||
#include "ThreadRegistry.hpp"
|
|
||||||
|
|
||||||
namespace kotlin {
|
namespace kotlin {
|
||||||
namespace mm {
|
|
||||||
|
|
||||||
template <typename F>
|
inline void RunInNewThread(std::function<void(mm::ThreadData&)> f) {
|
||||||
void RunInNewThread(F f) {
|
kotlin::RunInNewThread([&f](MemoryState* state) {
|
||||||
std::thread([&f]() {
|
f(*state->GetThreadData());
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mm
|
|
||||||
} // namespace kotlin
|
} // namespace kotlin
|
||||||
|
|||||||
@@ -36,8 +36,16 @@ public:
|
|||||||
// Using this after `Unregister` for the thread has been called is undefined behaviour.
|
// Using this after `Unregister` for the thread has been called is undefined behaviour.
|
||||||
ThreadData* CurrentThreadData() const noexcept { return currentThreadData_; }
|
ThreadData* CurrentThreadData() const noexcept { return currentThreadData_; }
|
||||||
|
|
||||||
|
class TestSupport {
|
||||||
|
public:
|
||||||
|
static void SetCurrentThreadData(ThreadData* newThreadData) {
|
||||||
|
ThreadRegistry::currentThreadData_ = newThreadData;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class GlobalData;
|
friend class GlobalData;
|
||||||
|
friend class ThreadRegistry::TestSupport;
|
||||||
|
|
||||||
ThreadRegistry();
|
ThreadRegistry();
|
||||||
~ThreadRegistry();
|
~ThreadRegistry();
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
using namespace kotlin;
|
using namespace kotlin;
|
||||||
|
|
||||||
TEST(ThreadStateTest, StateSwitch) {
|
TEST(ThreadStateTest, StateSwitch) {
|
||||||
mm::RunInNewThread([](mm::ThreadData& threadData) {
|
RunInNewThread([](mm::ThreadData& threadData) {
|
||||||
auto initialState = threadData.state();
|
auto initialState = threadData.state();
|
||||||
EXPECT_EQ(mm::ThreadState::kRunnable, initialState);
|
EXPECT_EQ(mm::ThreadState::kRunnable, initialState);
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ TEST(ThreadStateTest, StateSwitch) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(ThreadStateTest, StateGuard) {
|
TEST(ThreadStateTest, StateGuard) {
|
||||||
mm::RunInNewThread([](mm::ThreadData& threadData) {
|
RunInNewThread([](mm::ThreadData& threadData) {
|
||||||
auto initialState = threadData.state();
|
auto initialState = threadData.state();
|
||||||
EXPECT_EQ(mm::ThreadState::kRunnable, initialState);
|
EXPECT_EQ(mm::ThreadState::kRunnable, initialState);
|
||||||
{
|
{
|
||||||
@@ -44,14 +44,14 @@ TEST(ThreadStateTest, StateGuard) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(ThreadStateDeathTest, StateAsserts) {
|
TEST(ThreadStateDeathTest, StateAsserts) {
|
||||||
mm::RunInNewThread([](mm::ThreadData& threadData) {
|
RunInNewThread([](mm::ThreadData& threadData) {
|
||||||
EXPECT_DEATH(mm::AssertThreadState(&threadData, mm::ThreadState::kNative),
|
EXPECT_DEATH(mm::AssertThreadState(&threadData, mm::ThreadState::kNative),
|
||||||
"runtime assert: Unexpected thread state. Expected: NATIVE. Actual: RUNNABLE");
|
"runtime assert: Unexpected thread state. Expected: NATIVE. Actual: RUNNABLE");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ThreadStateDeathTest, IncorrectStateSwitch) {
|
TEST(ThreadStateDeathTest, IncorrectStateSwitch) {
|
||||||
mm::RunInNewThread([](mm::ThreadData& threadData) {
|
RunInNewThread([](mm::ThreadData& threadData) {
|
||||||
EXPECT_DEATH(mm::SwitchThreadState(&threadData, kotlin::mm::ThreadState::kRunnable),
|
EXPECT_DEATH(mm::SwitchThreadState(&threadData, kotlin::mm::ThreadState::kRunnable),
|
||||||
"runtime assert: Illegal thread state switch. Old state: RUNNABLE. New state: RUNNABLE");
|
"runtime assert: Illegal thread state switch. Old state: RUNNABLE. New state: RUNNABLE");
|
||||||
EXPECT_DEATH(Kotlin_mm_switchThreadStateRunnable(),
|
EXPECT_DEATH(Kotlin_mm_switchThreadStateRunnable(),
|
||||||
|
|||||||
Reference in New Issue
Block a user