/* * 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 #include #include #include #include #include "gmock/gmock.h" #include "gtest/gtest.h" #include "Format.h" #include "KAssert.h" using namespace kotlin; namespace { template std::string threadName(pthread_t thread) { static_assert( std::is_invocable_r_v, "Invalid pthread_getname_np signature"); std::array 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 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 doExit = false; std::atomic 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()) { } }