[K/N] Add ScopedThread
* thread that by default joins in the destructor (like C++20 jthread) * can be given a name Merge-request: KT-MR-5619 Merged-by: Alexander Shabalin <Alexander.Shabalin@jetbrains.com>
This commit is contained in:
committed by
Space
parent
9826172720
commit
434213f416
@@ -91,7 +91,7 @@ gc::ConcurrentMarkAndSweep::ConcurrentMarkAndSweep(
|
||||
NativeOrUnregisteredThreadGuard guard(/* reentrant = */ true);
|
||||
state_.schedule();
|
||||
});
|
||||
gcThread_ = std::thread([this] {
|
||||
gcThread_ = ScopedThread(ScopedThread::attributes().name("GC thread"), [this] {
|
||||
while (true) {
|
||||
auto epoch = state_.waitScheduled();
|
||||
if (epoch.has_value()) {
|
||||
@@ -106,7 +106,6 @@ gc::ConcurrentMarkAndSweep::ConcurrentMarkAndSweep(
|
||||
|
||||
gc::ConcurrentMarkAndSweep::~ConcurrentMarkAndSweep() {
|
||||
state_.shutdown();
|
||||
gcThread_.join();
|
||||
}
|
||||
|
||||
void gc::ConcurrentMarkAndSweep::StartFinalizerThreadIfNeeded() noexcept {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "Allocator.hpp"
|
||||
#include "GCScheduler.hpp"
|
||||
#include "ObjectFactory.hpp"
|
||||
#include "ScopedThread.hpp"
|
||||
#include "Types.h"
|
||||
#include "Utils.hpp"
|
||||
#include "GCState.hpp"
|
||||
@@ -85,7 +86,7 @@ private:
|
||||
|
||||
uint64_t lastGCTimestampUs_ = 0;
|
||||
GCStateHolder state_;
|
||||
std::thread gcThread_;
|
||||
ScopedThread gcThread_;
|
||||
KStdUniquePtr<FinalizerProcessor> finalizerProcessor_;
|
||||
};
|
||||
|
||||
|
||||
@@ -7,12 +7,10 @@
|
||||
#include "ObjectFactory.hpp"
|
||||
#include "Runtime.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
|
||||
void kotlin::gc::FinalizerProcessor::StartFinalizerThreadIfNone() noexcept {
|
||||
if (finalizerThread_.joinable()) return;
|
||||
finalizerThread_ = std::thread([this] {
|
||||
finalizerThread_ = ScopedThread(ScopedThread::attributes().name("GC finalizer processor"), [this] {
|
||||
Kotlin_initRuntimeIfNeeded();
|
||||
{
|
||||
std::unique_lock guard(initializedMutex_);
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ObjectFactory.hpp"
|
||||
#include "ConcurrentMarkAndSweep.hpp"
|
||||
#include "GCState.hpp"
|
||||
#include "ObjectFactory.hpp"
|
||||
#include "ScopedThread.hpp"
|
||||
|
||||
namespace kotlin::gc {
|
||||
|
||||
@@ -25,7 +26,7 @@ public:
|
||||
~FinalizerProcessor();
|
||||
|
||||
private:
|
||||
std::thread finalizerThread_;
|
||||
ScopedThread finalizerThread_;
|
||||
Queue finalizerQueue_;
|
||||
std::condition_variable finalizerQueueCondVar_;
|
||||
std::mutex finalizerQueueMutex_;
|
||||
|
||||
@@ -11,10 +11,13 @@
|
||||
#include "GlobalData.hpp"
|
||||
#include "KAssert.h"
|
||||
#include "Porting.h"
|
||||
#include "RepeatedTimer.hpp"
|
||||
#include "ThreadRegistry.hpp"
|
||||
#include "ThreadData.hpp"
|
||||
|
||||
#ifndef KONAN_NO_THREADS
|
||||
#include "RepeatedTimer.hpp"
|
||||
#endif
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
namespace {
|
||||
@@ -91,6 +94,8 @@ class GCEmptySchedulerData : public gc::GCSchedulerData {
|
||||
void UpdateAliveSetBytes(size_t bytes) noexcept override {}
|
||||
};
|
||||
|
||||
#ifndef KONAN_NO_THREADS
|
||||
|
||||
class GCSchedulerDataWithTimer : public gc::GCSchedulerData {
|
||||
public:
|
||||
GCSchedulerDataWithTimer(
|
||||
@@ -101,7 +106,7 @@ public:
|
||||
heapGrowthController_(config),
|
||||
regularIntervalPacer_(config, currentTimeProvider),
|
||||
scheduleGC_(std::move(scheduleGC)),
|
||||
timer_(config_.regularGcInterval(), [this]() {
|
||||
timer_("GC Timer thread", config_.regularGcInterval(), [this]() {
|
||||
if (regularIntervalPacer_.NeedsGC()) {
|
||||
scheduleGC_();
|
||||
}
|
||||
@@ -130,6 +135,8 @@ private:
|
||||
RepeatedTimer timer_;
|
||||
};
|
||||
|
||||
#endif // !KONAN_NO_THREADS
|
||||
|
||||
class GCSchedulerDataOnSafepoints : public gc::GCSchedulerData {
|
||||
public:
|
||||
GCSchedulerDataOnSafepoints(
|
||||
@@ -190,8 +197,12 @@ KStdUniquePtr<gc::GCSchedulerData> kotlin::gc::internal::MakeGCSchedulerData(
|
||||
RuntimeLogDebug({kTagGC}, "GC scheduler disabled");
|
||||
return ::make_unique<GCEmptySchedulerData>();
|
||||
case SchedulerType::kWithTimer:
|
||||
#ifndef KONAN_NO_THREADS
|
||||
RuntimeLogDebug({kTagGC}, "Initializing timer-based GC scheduler");
|
||||
return ::make_unique<GCSchedulerDataWithTimer>(config, std::move(scheduleGC), std::move(currentTimeProvider));
|
||||
#else
|
||||
RuntimeFail("GC scheduler with timer is not supported on this platform");
|
||||
#endif
|
||||
case SchedulerType::kOnSafepoints:
|
||||
RuntimeLogDebug({kTagGC}, "Initializing safe-point-based GC scheduler");
|
||||
return ::make_unique<GCSchedulerDataOnSafepoints>(config, std::move(scheduleGC), std::move(currentTimeProvider));
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#include "Memory.h"
|
||||
#include "ObjectTestSupport.hpp"
|
||||
#include "ScopedThread.hpp"
|
||||
#include "TestSupportCompilerGenerated.hpp"
|
||||
#include "TestSupport.hpp"
|
||||
|
||||
@@ -417,7 +418,7 @@ TEST(TerminationThreadStateDeathTest, UnhandledKotlinExceptionInRunnableState) {
|
||||
|
||||
// Do not use RunInNewThread because the termination handler will check initiliazation
|
||||
// of the whole runtime while RunInNewThread initializes the memory only.
|
||||
std::thread thread([]() {
|
||||
ScopedThread([]() {
|
||||
Kotlin_initRuntimeIfNeeded();
|
||||
SwitchThreadState(mm::GetMemoryState(), ThreadState::kRunnable);
|
||||
|
||||
@@ -425,7 +426,6 @@ TEST(TerminationThreadStateDeathTest, UnhandledKotlinExceptionInRunnableState) {
|
||||
ObjHeader exception{};
|
||||
ExceptionObjHolder::Throw(&exception);
|
||||
});
|
||||
thread.join();
|
||||
};
|
||||
|
||||
EXPECT_DEATH(testBlock(), AllOf(ASSERTS_PASSED, KOTLIN_HANDLER_RAN, Not(NATIVE_HANDLER_RAN)));
|
||||
@@ -441,14 +441,13 @@ TEST(TerminationThreadStateDeathTest, UnhandledKotlinExceptionInNativeState) {
|
||||
|
||||
// Do not use RunInNewThread because the termination handler will check initiliazation
|
||||
// of the whole runtime while RunInNewThread initializes the memory only.
|
||||
std::thread thread([]() {
|
||||
ScopedThread([]() {
|
||||
Kotlin_initRuntimeIfNeeded();
|
||||
|
||||
loggingAssert(GetThreadState() == ThreadState::kNative, "Expected kNative thread state before throwing");
|
||||
ObjHeader exception{};
|
||||
ExceptionObjHolder::Throw(&exception);
|
||||
});
|
||||
thread.join();
|
||||
};
|
||||
|
||||
EXPECT_DEATH(testBlock(), AllOf(ASSERTS_PASSED, KOTLIN_HANDLER_RAN, Not(NATIVE_HANDLER_RAN)));
|
||||
@@ -461,7 +460,7 @@ TEST(TerminationThreadStateDeathTest, UnhandledKotlinExceptionInForeignThread) {
|
||||
|
||||
// It is possible if a Kotlin exception thrown by a Kotlin callback is re-thrown in
|
||||
// another thread which is not attached to the Kotlin runtime at all.
|
||||
std::thread foreignThread([]() {
|
||||
ScopedThread([]() {
|
||||
loggingAssert(!mm::IsCurrentThreadRegistered(), "Expected unregistered thread before throwing");
|
||||
|
||||
auto future = std::async(std::launch::async, []() {
|
||||
@@ -476,7 +475,6 @@ TEST(TerminationThreadStateDeathTest, UnhandledKotlinExceptionInForeignThread) {
|
||||
// Re-throw the Kotlin exception in a foreign thread.
|
||||
future.get();
|
||||
});
|
||||
foreignThread.join();
|
||||
};
|
||||
|
||||
EXPECT_DEATH(testBlock(), AllOf(ASSERTS_PASSED, KOTLIN_HANDLER_RAN, Not(NATIVE_HANDLER_RAN)));
|
||||
@@ -503,11 +501,10 @@ TEST(TerminationThreadStateDeathTest, UnhandledForeignExceptionInForeignThread)
|
||||
auto testBlock = []() {
|
||||
setupMocks(/* expectRegisteredThread = */ false);
|
||||
|
||||
std::thread foreignThread([]() {
|
||||
ScopedThread([]() {
|
||||
loggingAssert(!mm::IsCurrentThreadRegistered(), "Expected unregistered thread before throwing");
|
||||
throw std::runtime_error("Foreign exception");
|
||||
});
|
||||
foreignThread.join();
|
||||
};
|
||||
|
||||
EXPECT_DEATH(testBlock(), AllOf(ASSERTS_PASSED, NATIVE_HANDLER_RAN, Not(KOTLIN_HANDLER_RAN)));
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
#include "MultiSourceQueue.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "ScopedThread.hpp"
|
||||
#include "TestSupport.hpp"
|
||||
#include "Types.h"
|
||||
|
||||
@@ -193,7 +193,7 @@ TEST(MultiSourceQueueTest, ConcurrentPublish) {
|
||||
constexpr int kThreadCount = kDefaultThreadCount;
|
||||
std::atomic<bool> canStart(false);
|
||||
std::atomic<int> readyCount(0);
|
||||
KStdVector<std::thread> threads;
|
||||
KStdVector<ScopedThread> threads;
|
||||
KStdVector<int> expected;
|
||||
|
||||
for (int i = 0; i < kThreadCount; ++i) {
|
||||
@@ -211,9 +211,7 @@ TEST(MultiSourceQueueTest, ConcurrentPublish) {
|
||||
while (readyCount < kThreadCount) {
|
||||
}
|
||||
canStart = true;
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
}
|
||||
threads.clear();
|
||||
|
||||
auto actual = Collect(queue);
|
||||
EXPECT_THAT(actual, testing::UnorderedElementsAreArray(expected));
|
||||
@@ -237,7 +235,7 @@ TEST(MultiSourceQueueTest, IterWhileConcurrentPublish) {
|
||||
std::atomic<bool> canStart(false);
|
||||
std::atomic<int> readyCount(0);
|
||||
std::atomic<int> startedCount(0);
|
||||
KStdVector<std::thread> threads;
|
||||
KStdVector<ScopedThread> threads;
|
||||
for (int i = 0; i < kThreadCount; ++i) {
|
||||
int j = i + kStartCount;
|
||||
expectedAfter.push_back(j);
|
||||
@@ -266,9 +264,7 @@ TEST(MultiSourceQueueTest, IterWhileConcurrentPublish) {
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
}
|
||||
threads.clear();
|
||||
|
||||
EXPECT_THAT(actualBefore, testing::ElementsAreArray(expectedBefore));
|
||||
|
||||
@@ -283,7 +279,7 @@ TEST(MultiSourceQueueTest, ConcurrentPublishAndApplyDeletions) {
|
||||
std::atomic<bool> canStart(false);
|
||||
std::atomic<int> readyCount(0);
|
||||
std::atomic<int> startedCount(0);
|
||||
KStdVector<std::thread> threads;
|
||||
KStdVector<ScopedThread> threads;
|
||||
for (int i = 0; i < kThreadCount; ++i) {
|
||||
threads.emplace_back([&queue, i, &canStart, &readyCount, &startedCount]() {
|
||||
IntQueue::Producer producer(queue);
|
||||
@@ -306,9 +302,7 @@ TEST(MultiSourceQueueTest, ConcurrentPublishAndApplyDeletions) {
|
||||
|
||||
queue.ApplyDeletions();
|
||||
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
}
|
||||
threads.clear();
|
||||
|
||||
// We do not know which elements were deleted at this point. Expecting not to crash by this point.
|
||||
|
||||
|
||||
@@ -6,9 +6,10 @@
|
||||
#include "Mutex.hpp"
|
||||
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "ScopedThread.hpp"
|
||||
#include "TestSupport.hpp"
|
||||
|
||||
using namespace kotlin;
|
||||
@@ -23,13 +24,13 @@ TYPED_TEST(MutexTest, SmokeDetachedThread) {
|
||||
using LockUnderTest = TypeParam;
|
||||
|
||||
LockUnderTest lock;
|
||||
std::thread secondThread;
|
||||
ScopedThread secondThread;
|
||||
std::atomic<bool> started = false;
|
||||
std::atomic<int32_t> protectedCounter = 0;
|
||||
|
||||
{
|
||||
std::unique_lock guard1(lock);
|
||||
secondThread = std::thread([&lock, &started, &protectedCounter]() {
|
||||
secondThread = ScopedThread([&lock, &started, &protectedCounter]() {
|
||||
started = true;
|
||||
std::unique_lock guard2(lock);
|
||||
protectedCounter++;
|
||||
|
||||
@@ -3,15 +3,17 @@
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef RUNTIME_REPEATED_TIMER_H
|
||||
#define RUNTIME_REPEATED_TIMER_H
|
||||
#pragma once
|
||||
|
||||
#ifndef KONAN_NO_THREADS
|
||||
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <string_view>
|
||||
|
||||
#include "KAssert.h"
|
||||
#include "ScopedThread.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
namespace kotlin {
|
||||
@@ -19,8 +21,13 @@ namespace kotlin {
|
||||
class RepeatedTimer : private Pinned {
|
||||
public:
|
||||
template <typename Rep, typename Period, typename F>
|
||||
RepeatedTimer(std::chrono::duration<Rep, Period> interval, F f) noexcept :
|
||||
thread_([this, interval, f]() noexcept { Run(interval, f); }) {}
|
||||
RepeatedTimer(std::string_view name, std::chrono::duration<Rep, Period> interval, F&& f) noexcept :
|
||||
thread_(ScopedThread::attributes().name(name), &RepeatedTimer::Run<Rep, Period, F>, this, std::move(interval), std::forward<F>(f)) {
|
||||
}
|
||||
|
||||
template <typename Rep, typename Period, typename F>
|
||||
RepeatedTimer(std::chrono::duration<Rep, Period> interval, F&& f) noexcept :
|
||||
RepeatedTimer("Timer thread", interval, std::forward<F>(f)) {}
|
||||
|
||||
~RepeatedTimer() {
|
||||
{
|
||||
@@ -28,7 +35,6 @@ public:
|
||||
run_ = false;
|
||||
}
|
||||
wait_.notify_all();
|
||||
thread_.join();
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -50,9 +56,9 @@ private:
|
||||
std::mutex mutex_;
|
||||
std::condition_variable wait_;
|
||||
bool run_ = true;
|
||||
std::thread thread_;
|
||||
ScopedThread thread_;
|
||||
};
|
||||
|
||||
} // namespace kotlin
|
||||
|
||||
#endif // RUNTIME_REPEATED_TIMER_H
|
||||
#endif // !KONAN_NO_THREADS
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.
|
||||
*/
|
||||
|
||||
#ifndef KONAN_NO_THREADS
|
||||
|
||||
#include "ScopedThread.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <pthread.h>
|
||||
#include <type_traits>
|
||||
|
||||
#include "Logging.hpp"
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
void internal::setCurrentThreadName(std::string_view name) noexcept {
|
||||
#if KONAN_MACOSX || KONAN_IOS || KONAN_WATCHOS || KONAN_TVOS
|
||||
static_assert(std::is_invocable_r_v<void, decltype(pthread_setname_np), const char*>, "Invalid pthread_setname_np signature");
|
||||
pthread_setname_np(name.data());
|
||||
#else
|
||||
static_assert(std::is_invocable_r_v<int, decltype(pthread_setname_np), pthread_t, const char*>, "Invalid pthread_setname_np signature");
|
||||
// TODO: On Linux the maximum thread name is 16 characters. Handle automatically?
|
||||
int result = pthread_setname_np(pthread_self(), name.data());
|
||||
if (result != 0) {
|
||||
RuntimeLogWarning({"rt"}, "Failed to set thread name: %s", std::strerror(result));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // !KONAN_NO_THREADS
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef KONAN_NO_THREADS
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
|
||||
#include "Types.h"
|
||||
#include "Utils.hpp"
|
||||
|
||||
namespace kotlin {
|
||||
namespace internal {
|
||||
|
||||
void setCurrentThreadName(std::string_view name) noexcept;
|
||||
|
||||
} // namespace internal
|
||||
|
||||
// This is a combination of `std::jthread` and http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2019r0.pdf
|
||||
// Missing stop token handling from `std::jthread`, and only a name attribute is supported from the paper.
|
||||
class ScopedThread : private MoveOnly {
|
||||
public:
|
||||
using id = std::thread::id;
|
||||
using native_handle_type = std::thread::native_handle_type;
|
||||
|
||||
// There's no guarantee when and if these attributes will be applied. Use them as hints.
|
||||
class attributes {
|
||||
public:
|
||||
attributes() noexcept = default;
|
||||
attributes(const attributes&) noexcept = default;
|
||||
attributes(attributes&&) noexcept = default;
|
||||
attributes& operator=(const attributes&) noexcept = default;
|
||||
attributes& operator=(attributes&&) noexcept = default;
|
||||
~attributes() = default;
|
||||
|
||||
attributes& name(std::string_view name) noexcept {
|
||||
name_ = name;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class ScopedThread;
|
||||
std::optional<KStdString> name_;
|
||||
};
|
||||
|
||||
ScopedThread() noexcept = default;
|
||||
|
||||
template <
|
||||
typename F,
|
||||
typename... Args,
|
||||
typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<F>>, attributes>>>
|
||||
explicit ScopedThread(F&& f, Args&&... args) : ScopedThread(attributes(), std::forward<F>(f), std::forward<Args>(args)...) {}
|
||||
|
||||
template <typename F, typename... Args>
|
||||
explicit ScopedThread(const attributes& attr, F&& f, Args&&... args) :
|
||||
thread_(&ScopedThread::Run<F, Args...>, attr, std::forward<F>(f), std::forward<Args>(args)...) {}
|
||||
|
||||
ScopedThread(ScopedThread&& rhs) noexcept = default;
|
||||
ScopedThread& operator=(ScopedThread&& rhs) noexcept = default;
|
||||
|
||||
~ScopedThread() {
|
||||
if (thread_.joinable()) {
|
||||
thread_.join();
|
||||
}
|
||||
}
|
||||
|
||||
void swap(ScopedThread& rhs) noexcept { thread_.swap(rhs.thread_); }
|
||||
|
||||
[[nodiscard]] static unsigned int hardware_concurrency() noexcept { return std::thread::hardware_concurrency(); }
|
||||
|
||||
[[nodiscard]] bool joinable() const noexcept { return thread_.joinable(); }
|
||||
|
||||
[[nodiscard]] id get_id() const noexcept { return thread_.get_id(); }
|
||||
|
||||
[[nodiscard]] native_handle_type native_handle() { return thread_.native_handle(); }
|
||||
|
||||
void join() { thread_.join(); }
|
||||
|
||||
void detach() { thread_.detach(); }
|
||||
|
||||
private:
|
||||
template <typename F, typename... Args>
|
||||
static std::invoke_result_t<F, Args...> Run(attributes attr, F&& f, Args&&... args) {
|
||||
if (attr.name_) {
|
||||
internal::setCurrentThreadName(*attr.name_);
|
||||
}
|
||||
return std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
std::thread thread_;
|
||||
};
|
||||
|
||||
} // namespace kotlin
|
||||
|
||||
#endif // !KONAN_NO_THREADS
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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 "ScopedThread.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
#include <pthread.h>
|
||||
#include <type_traits>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "Format.h"
|
||||
#include "KAssert.h"
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
namespace {
|
||||
|
||||
template <size_t NAME_SIZE = 100>
|
||||
std::string threadName(pthread_t thread) {
|
||||
static_assert(
|
||||
std::is_invocable_r_v<int, decltype(pthread_getname_np), pthread_t, char*, size_t>, "Invalid pthread_getname_np signature");
|
||||
std::array<char, NAME_SIZE> name;
|
||||
int result = pthread_getname_np(thread, name.data(), name.size());
|
||||
RuntimeAssert(result == 0, "failed to get thread name: %s\n", std::strerror(result));
|
||||
// Make sure name is null-terminated.
|
||||
name[name.size() - 1] = '\0';
|
||||
return std::string(name.data());
|
||||
}
|
||||
|
||||
__attribute__((format(printf, 1, 2))) std::string format(const char* format, ...) {
|
||||
std::array<char, 20> buffer;
|
||||
std::va_list args;
|
||||
va_start(args, format);
|
||||
VFormatToSpan(buffer, format, args);
|
||||
va_end(args);
|
||||
// `buffer` is guaranteed to be 0-terminated.
|
||||
return std::string(buffer.data());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(ScopedThreadTest, Default) {
|
||||
// Do not check name by default, since the default may be set by the system.
|
||||
ScopedThread thread([] {});
|
||||
}
|
||||
|
||||
TEST(ScopedThreadTest, ThreadName) {
|
||||
ScopedThread thread(ScopedThread::attributes().name("some thread"), [] { EXPECT_THAT(threadName(pthread_self()), "some thread"); });
|
||||
}
|
||||
|
||||
TEST(ScopedThreadTest, DynamicThreadName) {
|
||||
ScopedThread thread(
|
||||
ScopedThread::attributes().name(format("thread %d", 42)), [] { EXPECT_THAT(threadName(pthread_self()), "thread 42"); });
|
||||
}
|
||||
|
||||
TEST(ScopedThreadTest, EmptyThreadName) {
|
||||
ScopedThread thread(ScopedThread::attributes().name(""), [] { EXPECT_THAT(threadName(pthread_self()), ""); });
|
||||
}
|
||||
|
||||
TEST(ScopedThreadTest, JoinsInDestructor) {
|
||||
std::atomic<bool> exited = false;
|
||||
{
|
||||
ScopedThread([&exited] {
|
||||
// Give a chance for the outer scope to go on.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
exited = true;
|
||||
});
|
||||
}
|
||||
EXPECT_THAT(exited.load(), true);
|
||||
}
|
||||
|
||||
TEST(ScopedThreadTest, ManualJoin) {
|
||||
std::atomic<bool> exited = false;
|
||||
ScopedThread thread([&exited] {
|
||||
// Give a chance for the outer scope to go on.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
exited = true;
|
||||
});
|
||||
EXPECT_THAT(thread.joinable(), true);
|
||||
thread.join();
|
||||
EXPECT_THAT(thread.joinable(), false);
|
||||
EXPECT_THAT(exited.load(), true);
|
||||
}
|
||||
|
||||
TEST(ScopedThreadTest, Detach) {
|
||||
std::atomic<bool> exited = false;
|
||||
{
|
||||
ScopedThread thread([&exited] {
|
||||
// Give a chance for the outer scope to go on.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
exited = true;
|
||||
});
|
||||
EXPECT_THAT(thread.joinable(), true);
|
||||
thread.detach();
|
||||
EXPECT_THAT(thread.joinable(), false);
|
||||
}
|
||||
EXPECT_THAT(exited.load(), false);
|
||||
// Wait for the thread to set `exited` before terminating test, otherwise the thread will write
|
||||
// into freed `exited`.
|
||||
while (!exited.load()) {
|
||||
}
|
||||
}
|
||||
@@ -7,11 +7,11 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "ScopedThread.hpp"
|
||||
#include "TestSupport.hpp"
|
||||
#include "Types.h"
|
||||
|
||||
@@ -111,7 +111,7 @@ TEST(SingleLockListTest, ConcurrentEmplace) {
|
||||
constexpr int kThreadCount = kDefaultThreadCount;
|
||||
std::atomic<bool> canStart(false);
|
||||
std::atomic<int> readyCount(0);
|
||||
KStdVector<std::thread> threads;
|
||||
KStdVector<ScopedThread> threads;
|
||||
KStdVector<int> expected;
|
||||
for (int i = 0; i < kThreadCount; ++i) {
|
||||
expected.push_back(i);
|
||||
@@ -126,9 +126,7 @@ TEST(SingleLockListTest, ConcurrentEmplace) {
|
||||
while (readyCount < kThreadCount) {
|
||||
}
|
||||
canStart = true;
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
}
|
||||
threads.clear();
|
||||
|
||||
KStdVector<int> actual;
|
||||
for (int element : list.LockForIter()) {
|
||||
@@ -148,7 +146,7 @@ TEST(SingleLockListTest, ConcurrentErase) {
|
||||
|
||||
std::atomic<bool> canStart(false);
|
||||
std::atomic<int> readyCount(0);
|
||||
KStdVector<std::thread> threads;
|
||||
KStdVector<ScopedThread> threads;
|
||||
for (auto* item : items) {
|
||||
threads.emplace_back([item, &list, &canStart, &readyCount]() {
|
||||
++readyCount;
|
||||
@@ -161,9 +159,7 @@ TEST(SingleLockListTest, ConcurrentErase) {
|
||||
while (readyCount < kThreadCount) {
|
||||
}
|
||||
canStart = true;
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
}
|
||||
threads.clear();
|
||||
|
||||
KStdVector<int> actual;
|
||||
for (int element : list.LockForIter()) {
|
||||
@@ -188,7 +184,7 @@ TEST(SingleLockListTest, IterWhileConcurrentEmplace) {
|
||||
|
||||
std::atomic<bool> canStart(false);
|
||||
std::atomic<int> startedCount(0);
|
||||
KStdVector<std::thread> threads;
|
||||
KStdVector<ScopedThread> threads;
|
||||
for (int i = 0; i < kThreadCount; ++i) {
|
||||
int j = i + kStartCount;
|
||||
expectedAfter.push_back(j);
|
||||
@@ -212,9 +208,7 @@ TEST(SingleLockListTest, IterWhileConcurrentEmplace) {
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
}
|
||||
threads.clear();
|
||||
|
||||
EXPECT_THAT(actualBefore, testing::ElementsAreArray(expectedBefore));
|
||||
|
||||
@@ -239,7 +233,7 @@ TEST(SingleLockListTest, IterWhileConcurrentErase) {
|
||||
|
||||
std::atomic<bool> canStart(false);
|
||||
std::atomic<int> startedCount(0);
|
||||
KStdVector<std::thread> threads;
|
||||
KStdVector<ScopedThread> threads;
|
||||
for (auto* item : items) {
|
||||
threads.emplace_back([item, &list, &canStart, &startedCount]() {
|
||||
while (!canStart) {
|
||||
@@ -261,9 +255,7 @@ TEST(SingleLockListTest, IterWhileConcurrentErase) {
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
}
|
||||
threads.clear();
|
||||
|
||||
EXPECT_THAT(actualBefore, testing::ElementsAreArray(expectedBefore));
|
||||
|
||||
@@ -279,7 +271,7 @@ TEST(SingleLockListTest, LockAndEmplace) {
|
||||
SingleLockList<int, std::recursive_mutex> list;
|
||||
constexpr int kThreadCount = kDefaultThreadCount;
|
||||
|
||||
KStdVector<std::thread> threads;
|
||||
KStdVector<ScopedThread> threads;
|
||||
KStdVector<int> actualLocked;
|
||||
KStdVector<int> actualUnlocked;
|
||||
KStdVector<int> expectedUnlocked;
|
||||
@@ -306,9 +298,7 @@ TEST(SingleLockListTest, LockAndEmplace) {
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
threads.clear();
|
||||
for (int element : list.LockForIter()) {
|
||||
actualUnlocked.push_back(element);
|
||||
}
|
||||
@@ -322,7 +312,7 @@ TEST(SingleLockListTest, LockAndErase) {
|
||||
|
||||
KStdVector<SingleLockList<int, std::recursive_mutex>::Node*> items;
|
||||
KStdVector<int> expectedLocked;
|
||||
KStdVector<std::thread> threads;
|
||||
KStdVector<ScopedThread> threads;
|
||||
KStdVector<int> actualLocked;
|
||||
KStdVector<int> actualUnlocked;
|
||||
std::atomic<int> startedCount(0);
|
||||
@@ -350,9 +340,7 @@ TEST(SingleLockListTest, LockAndErase) {
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
threads.clear();
|
||||
for (int element : list.LockForIter()) {
|
||||
actualUnlocked.push_back(element);
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
#include <future>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <thread>
|
||||
|
||||
#include "ScopedThread.hpp"
|
||||
#include "Types.h"
|
||||
#include "Utils.hpp"
|
||||
|
||||
@@ -54,7 +54,7 @@ public:
|
||||
}
|
||||
|
||||
// Id of the worker thread.
|
||||
std::thread::id threadId() const noexcept { return thread_.get_id(); }
|
||||
ScopedThread::id threadId() const noexcept { return thread_.get_id(); }
|
||||
|
||||
// Schedule task execution on the worker thread. The returned future is resolved when the task has completed.
|
||||
// If `this` is destroyed before the task manages to complete, the returned future will fail with exception upon `.get()`.
|
||||
@@ -104,7 +104,7 @@ private:
|
||||
KStdDeque<std::packaged_task<void()>> queue_;
|
||||
bool shutdownRequested_ = false;
|
||||
|
||||
std::thread thread_;
|
||||
ScopedThread thread_;
|
||||
};
|
||||
|
||||
} // namespace kotlin
|
||||
|
||||
@@ -55,7 +55,7 @@ testing::MockFunction<void(PinnedContext&)>* PinnedContext::dtorMock = nullptr;
|
||||
TEST(SingleThreadExecutorTest, ContextThreadBound) {
|
||||
PinnedContext::ScopedMocks mocks;
|
||||
PinnedContext* createdContext = nullptr;
|
||||
std::thread::id createdThread;
|
||||
ScopedThread::id createdThread;
|
||||
EXPECT_CALL(mocks.ctorMock, Call(_)).WillOnce([&](PinnedContext& context) {
|
||||
createdContext = &context;
|
||||
createdThread = std::this_thread::get_id();
|
||||
@@ -172,7 +172,7 @@ TEST(SingleThreadExecutorTest, ExecuteFromManyThreads) {
|
||||
std::atomic_bool canStart = false;
|
||||
|
||||
KStdVector<int> expected;
|
||||
KStdVector<std::thread> threads;
|
||||
KStdVector<ScopedThread> threads;
|
||||
for (int i = 0; i < kDefaultThreadCount; ++i) {
|
||||
expected.push_back(i);
|
||||
threads.emplace_back([&, i] {
|
||||
@@ -184,9 +184,7 @@ TEST(SingleThreadExecutorTest, ExecuteFromManyThreads) {
|
||||
|
||||
canStart = true;
|
||||
|
||||
for (auto& thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
threads.clear();
|
||||
|
||||
EXPECT_THAT(executor.context().result, testing::UnorderedElementsAreArray(expected));
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
|
||||
#include "Memory.h"
|
||||
#include "Runtime.h"
|
||||
#include "ScopedThread.hpp"
|
||||
|
||||
namespace kotlin {
|
||||
|
||||
@@ -48,10 +48,10 @@ private:
|
||||
|
||||
// Runs the given function in a separate thread with minimally initialized runtime.
|
||||
inline void RunInNewThread(std::function<void(MemoryState*)> f) {
|
||||
std::thread([&f]() {
|
||||
ScopedThread([&f]() {
|
||||
ScopedMemoryInit init;
|
||||
f(init.memoryState());
|
||||
}).join();
|
||||
});
|
||||
}
|
||||
|
||||
// Runs the given function in a separate thread with minimally initialized runtime.
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
#include "ExtraObjectData.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "ObjectTestSupport.hpp"
|
||||
#include "ScopedThread.hpp"
|
||||
#include "TestSupport.hpp"
|
||||
|
||||
using namespace kotlin;
|
||||
@@ -68,7 +68,7 @@ TEST_F(ExtraObjectDataTest, ConcurrentInstall) {
|
||||
|
||||
std::atomic<bool> canStart(false);
|
||||
std::atomic<int> readyCount(0);
|
||||
std::vector<std::thread> threads;
|
||||
std::vector<ScopedThread> threads;
|
||||
std::vector<mm::ExtraObjectData*> actual(kThreadCount, nullptr);
|
||||
|
||||
for (int i = 0; i < kThreadCount; ++i) {
|
||||
@@ -86,9 +86,7 @@ TEST_F(ExtraObjectDataTest, ConcurrentInstall) {
|
||||
while (readyCount < kThreadCount) {
|
||||
}
|
||||
canStart = true;
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
}
|
||||
threads.clear();
|
||||
|
||||
std::vector<mm::ExtraObjectData*> expected(kThreadCount, actual[0]);
|
||||
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
#include "InitializationScheme.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "ObjectTestSupport.hpp"
|
||||
#include "ScopedThread.hpp"
|
||||
#include "TestSupport.hpp"
|
||||
#include "ThreadData.hpp"
|
||||
#include "Types.h"
|
||||
@@ -200,7 +200,7 @@ TEST_F(InitSingletonTest, InitSingletonConcurrent) {
|
||||
constexpr size_t kThreadCount = kDefaultThreadCount;
|
||||
std::atomic<bool> canStart(false);
|
||||
std::atomic<size_t> readyCount(0);
|
||||
KStdVector<std::thread> threads;
|
||||
KStdVector<ScopedThread> threads;
|
||||
ObjHeader* location = nullptr;
|
||||
KStdVector<ObjHeader*> stackLocations(kThreadCount, nullptr);
|
||||
KStdVector<ObjHeader*> actual(kThreadCount, nullptr);
|
||||
@@ -222,9 +222,7 @@ TEST_F(InitSingletonTest, InitSingletonConcurrent) {
|
||||
// Constructor is called exactly once.
|
||||
EXPECT_CALL(constructor(), Call(_));
|
||||
canStart = true;
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
}
|
||||
threads.clear();
|
||||
testing::Mock::VerifyAndClearExpectations(&constructor());
|
||||
|
||||
EXPECT_THAT(location, testing::Not(testing::Truly(isNullOrMarker)));
|
||||
@@ -236,7 +234,7 @@ TEST_F(InitSingletonTest, InitSingletonConcurrentFailing) {
|
||||
constexpr size_t kThreadCount = kDefaultThreadCount;
|
||||
std::atomic<bool> canStart(false);
|
||||
std::atomic<size_t> readyCount(0);
|
||||
KStdVector<std::thread> threads;
|
||||
KStdVector<ScopedThread> threads;
|
||||
constexpr int kException = 42;
|
||||
ObjHeader* location = nullptr;
|
||||
KStdVector<ObjHeader*> stackLocations(kThreadCount, nullptr);
|
||||
@@ -263,9 +261,7 @@ TEST_F(InitSingletonTest, InitSingletonConcurrentFailing) {
|
||||
// Constructor is called exactly `kThreadCount` times.
|
||||
EXPECT_CALL(constructor(), Call(_)).Times(kThreadCount).WillRepeatedly([]() { throw kException; });
|
||||
canStart = true;
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
}
|
||||
threads.clear();
|
||||
testing::Mock::VerifyAndClearExpectations(&constructor());
|
||||
|
||||
EXPECT_THAT(location, nullptr);
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
|
||||
#include "Mutex.hpp"
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "ScopedThread.hpp"
|
||||
#include "TestSupport.hpp"
|
||||
#include "ThreadState.hpp"
|
||||
|
||||
@@ -42,14 +42,14 @@ TYPED_TEST(MutexTestNewMM, SmokeAttachedThread) {
|
||||
ThreadState initialThreadState = TypeParam::initialState;
|
||||
|
||||
LockUnderTest lock;
|
||||
std::thread secondThread;
|
||||
ScopedThread secondThread;
|
||||
std::atomic<bool> started = false;
|
||||
std::atomic<int32_t> protectedCounter = 0;
|
||||
mm::ThreadData* secondThreadData = nullptr;
|
||||
|
||||
{
|
||||
std::unique_lock guard1(lock);
|
||||
secondThread = std::thread([&lock, &started, &protectedCounter, &secondThreadData, &initialThreadState]() {
|
||||
secondThread = ScopedThread([&lock, &started, &protectedCounter, &secondThreadData, &initialThreadState]() {
|
||||
ScopedMemoryInit init;
|
||||
SwitchThreadState(init.memoryState(), initialThreadState, /* reentrant = */ true);
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "ObjectFactory.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
@@ -16,6 +15,7 @@
|
||||
#include "FinalizerHooksTestSupport.hpp"
|
||||
#include "ObjectOps.hpp"
|
||||
#include "ObjectTestSupport.hpp"
|
||||
#include "ScopedThread.hpp"
|
||||
#include "TestSupport.hpp"
|
||||
#include "Types.h"
|
||||
|
||||
@@ -630,7 +630,7 @@ TEST(ObjectFactoryStorageTest, ConcurrentPublish) {
|
||||
constexpr int kThreadCount = kDefaultThreadCount;
|
||||
std::atomic<bool> canStart(false);
|
||||
std::atomic<int> readyCount(0);
|
||||
KStdVector<std::thread> threads;
|
||||
KStdVector<ScopedThread> threads;
|
||||
KStdVector<int> expected;
|
||||
|
||||
for (int i = 0; i < kThreadCount; ++i) {
|
||||
@@ -648,9 +648,7 @@ TEST(ObjectFactoryStorageTest, ConcurrentPublish) {
|
||||
while (readyCount < kThreadCount) {
|
||||
}
|
||||
canStart = true;
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
}
|
||||
threads.clear();
|
||||
|
||||
auto actual = Collect<int>(storage);
|
||||
|
||||
@@ -676,7 +674,7 @@ TEST(ObjectFactoryStorageTest, IterWhileConcurrentPublish) {
|
||||
std::atomic<bool> canStart(false);
|
||||
std::atomic<int> readyCount(0);
|
||||
std::atomic<int> startedCount(0);
|
||||
KStdVector<std::thread> threads;
|
||||
KStdVector<ScopedThread> threads;
|
||||
for (int i = 0; i < kThreadCount; ++i) {
|
||||
int j = i + kStartCount;
|
||||
expectedAfter.push_back(j);
|
||||
@@ -706,9 +704,7 @@ TEST(ObjectFactoryStorageTest, IterWhileConcurrentPublish) {
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
}
|
||||
threads.clear();
|
||||
|
||||
EXPECT_THAT(actualBefore, testing::ElementsAreArray(expectedBefore));
|
||||
|
||||
@@ -736,7 +732,7 @@ TEST(ObjectFactoryStorageTest, EraseWhileConcurrentPublish) {
|
||||
std::atomic<bool> canStart(false);
|
||||
std::atomic<int> readyCount(0);
|
||||
std::atomic<int> startedCount(0);
|
||||
KStdVector<std::thread> threads;
|
||||
KStdVector<ScopedThread> threads;
|
||||
for (int i = 0; i < kThreadCount; ++i) {
|
||||
int j = i + kStartCount;
|
||||
expectedAfter.push_back(j);
|
||||
@@ -768,9 +764,7 @@ TEST(ObjectFactoryStorageTest, EraseWhileConcurrentPublish) {
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
}
|
||||
threads.clear();
|
||||
|
||||
auto actual = Collect<int>(storage);
|
||||
|
||||
@@ -1110,7 +1104,7 @@ TEST(ObjectFactoryTest, ConcurrentPublish) {
|
||||
constexpr int kThreadCount = kDefaultThreadCount;
|
||||
std::atomic<bool> canStart(false);
|
||||
std::atomic<int> readyCount(0);
|
||||
KStdVector<std::thread> threads;
|
||||
KStdVector<ScopedThread> threads;
|
||||
std::mutex expectedMutex;
|
||||
KStdVector<ObjHeader*> expected;
|
||||
|
||||
@@ -1135,9 +1129,7 @@ TEST(ObjectFactoryTest, ConcurrentPublish) {
|
||||
}
|
||||
testing::Mock::VerifyAndClearExpectations(&allocator);
|
||||
canStart = true;
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
}
|
||||
threads.clear();
|
||||
|
||||
auto iter = objectFactory.LockForIter();
|
||||
KStdVector<ObjHeader*> actual;
|
||||
|
||||
@@ -5,17 +5,15 @@
|
||||
|
||||
#include "ThreadRegistry.hpp"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <thread>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "ScopedThread.hpp"
|
||||
#include "ThreadData.hpp"
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
TEST(ThreadRegistryTest, RegisterCurrentThread) {
|
||||
std::thread t([]() {
|
||||
ScopedThread([]() {
|
||||
class ScopedRegistration {
|
||||
public:
|
||||
ScopedRegistration() : node(mm::ThreadRegistry::Instance().RegisterCurrentThread()) {}
|
||||
@@ -27,5 +25,4 @@ TEST(ThreadRegistryTest, RegisterCurrentThread) {
|
||||
EXPECT_EQ(konan::currentThreadId(), threadData->threadId());
|
||||
EXPECT_EQ(threadData, mm::ThreadRegistry::Instance().CurrentThreadData());
|
||||
});
|
||||
t.join();
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "Memory.h"
|
||||
#include "MemoryPrivate.hpp"
|
||||
#include "ScopedThread.hpp"
|
||||
#include "TestSupport.hpp"
|
||||
#include "ThreadData.hpp"
|
||||
#include "ThreadState.hpp"
|
||||
@@ -110,7 +111,7 @@ TEST_F(ThreadStateTest, StateGuardForCurrentThread) {
|
||||
}
|
||||
|
||||
TEST_F(ThreadStateTest, CalledFromNativeGuard_DetachedThread) {
|
||||
std::thread t([] {
|
||||
ScopedThread([] {
|
||||
ASSERT_FALSE(mm::IsCurrentThreadRegistered());
|
||||
{
|
||||
CalledFromNativeGuard guard;
|
||||
@@ -120,11 +121,10 @@ TEST_F(ThreadStateTest, CalledFromNativeGuard_DetachedThread) {
|
||||
EXPECT_TRUE(mm::IsCurrentThreadRegistered());
|
||||
EXPECT_EQ(mm::GetMemoryState()->GetThreadData()->state(), ThreadState::kNative);
|
||||
});
|
||||
t.join();
|
||||
}
|
||||
|
||||
TEST_F(ThreadStateTest, CalledFromNativeGuard_AttachedThread) {
|
||||
std::thread t([] {
|
||||
ScopedThread([] {
|
||||
// CalledFromNativeGuard checks that runtime is fully initialized under the hood.
|
||||
Kotlin_initRuntimeIfNeeded();
|
||||
|
||||
@@ -136,11 +136,10 @@ TEST_F(ThreadStateTest, CalledFromNativeGuard_AttachedThread) {
|
||||
}
|
||||
EXPECT_EQ(threadData->state(), ThreadState::kNative);
|
||||
});
|
||||
t.join();
|
||||
}
|
||||
|
||||
TEST_F(ThreadStateTest, NativeOrUnregisteredThreadGuard_DetachedThread) {
|
||||
std::thread t([] {
|
||||
ScopedThread([] {
|
||||
{
|
||||
ASSERT_FALSE(mm::IsCurrentThreadRegistered());
|
||||
NativeOrUnregisteredThreadGuard guard;
|
||||
@@ -148,7 +147,6 @@ TEST_F(ThreadStateTest, NativeOrUnregisteredThreadGuard_DetachedThread) {
|
||||
}
|
||||
EXPECT_FALSE(mm::IsCurrentThreadRegistered());
|
||||
});
|
||||
t.join();
|
||||
}
|
||||
|
||||
TEST_F(ThreadStateTest, NativeOrUnregisteredThreadGuard_AttachedThread) {
|
||||
@@ -341,7 +339,7 @@ TEST(ThreadStateDeathTest, ReentrantStateSwitch_NativeOrUnregisteredThreadGuard)
|
||||
}
|
||||
|
||||
TEST(ThreadStateDeathTest, ReentrantStateSwitch_CalledFromNativeGuard) {
|
||||
std::thread t([]() {
|
||||
ScopedThread([]() {
|
||||
// CalledFromNativeGuard checks that runtime is fully initialized under the hood.
|
||||
Kotlin_initRuntimeIfNeeded();
|
||||
auto* threadData = mm::GetMemoryState()->GetThreadData();
|
||||
@@ -367,7 +365,6 @@ TEST(ThreadStateDeathTest, ReentrantStateSwitch_CalledFromNativeGuard) {
|
||||
}
|
||||
EXPECT_EQ(threadData->state(), ThreadState::kNative);
|
||||
});
|
||||
t.join();
|
||||
}
|
||||
|
||||
|
||||
@@ -406,4 +403,4 @@ TEST(ThreadStateDeathTest, GuardForDetachedThread) {
|
||||
EXPECT_DEATH({ ThreadStateGuard guard(ThreadState::kNative, false); }, expectedError);
|
||||
EXPECT_DEATH({ ThreadStateGuard guard(ThreadState::kRunnable, true); }, expectedError);
|
||||
EXPECT_DEATH({ ThreadStateGuard guard(ThreadState::kNative, true); }, expectedError);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "MemoryPrivate.hpp"
|
||||
#include "Runtime.h"
|
||||
#include "RuntimePrivate.hpp"
|
||||
#include "ScopedThread.hpp"
|
||||
#include "ThreadSuspension.hpp"
|
||||
#include "ThreadState.hpp"
|
||||
|
||||
@@ -13,7 +14,6 @@
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
#include <future>
|
||||
#include <thread>
|
||||
#include <TestSupport.hpp>
|
||||
#include <TestSupportCompilerGenerated.hpp>
|
||||
|
||||
@@ -77,15 +77,12 @@ public:
|
||||
~ThreadSuspensionTest() {
|
||||
canStart = true;
|
||||
shouldStop = true;
|
||||
for (auto& thread : threads) {
|
||||
if (thread.joinable()) thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr size_t kThreadCount = kDefaultThreadCount;
|
||||
static constexpr size_t kIterations = kDefaultIterations;
|
||||
|
||||
KStdVector<std::thread> threads;
|
||||
KStdVector<ScopedThread> threads;
|
||||
std::array<std::atomic<bool>, kThreadCount> ready{false};
|
||||
std::atomic<bool> canStart{false};
|
||||
std::atomic<bool> shouldStop{false};
|
||||
@@ -270,4 +267,4 @@ TEST_F(ThreadSuspensionTest, FileInitializationWithSuspend) {
|
||||
for (auto& t : threads) {
|
||||
t.join();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user