29a0bbca4d
Merge-request: KT-MR-6744 Merged-by: Alexander Shabalin <Alexander.Shabalin@jetbrains.com>
110 lines
3.2 KiB
C++
110 lines
3.2 KiB
C++
/*
|
|
* 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) {
|
|
// Not atomic for TSAN to complain if we didn't synchronize with join.
|
|
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, true);
|
|
}
|
|
|
|
TEST(ScopedThreadTest, ManualJoin) {
|
|
// Not atomic for TSAN to complain if we didn't synchronize with join.
|
|
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, true);
|
|
}
|
|
|
|
TEST(ScopedThreadTest, Detach) {
|
|
std::atomic<bool> doExit = false;
|
|
std::atomic<bool> exited = false;
|
|
{
|
|
ScopedThread thread([&doExit, &exited] {
|
|
while (!doExit.load()) {}
|
|
exited = true;
|
|
});
|
|
EXPECT_THAT(thread.joinable(), true);
|
|
thread.detach();
|
|
EXPECT_THAT(thread.joinable(), false);
|
|
}
|
|
EXPECT_THAT(exited.load(), false);
|
|
doExit = true;
|
|
while (!exited.load()) {
|
|
}
|
|
}
|