Implement pthread_getname_np_gcompat for musl (#255)

This commit is contained in:
Andy Weidenbaum 2019-03-18 12:53:20 +13:00 committed by Sebastian Meßmer
parent ae680a5bdc
commit 9ee345e16a
3 changed files with 85 additions and 29 deletions

View File

@ -9,7 +9,11 @@ namespace cpputils {
void set_thread_name(const char* name); void set_thread_name(const char* name);
std::string get_thread_name(); std::string get_thread_name();
#if defined(__GLIBC__) || defined(__APPLE__) || defined(_MSC_VER)
// this is not supported on musl systems, that's why we ifdef it for glibc only.
std::string get_thread_name(std::thread* thread); std::string get_thread_name(std::thread* thread);
#endif
} }

View File

@ -5,6 +5,14 @@
#include <thread> #include <thread>
#include <pthread.h> #include <pthread.h>
#include <cpp-utils/assert/assert.h> #include <cpp-utils/assert/assert.h>
#if !(defined(__GLIBC__) || defined(__APPLE__))
// for pthread_getname_np_gcompat
#include <errno.h> // errno
#include <fcntl.h> // O_CLOEXEC, O_RDONLY
#include <unistd.h> // open, read
#include <boost/filesystem/path.hpp>
#include <sys/types.h>
#endif
namespace cpputils { namespace cpputils {
@ -28,9 +36,47 @@ void set_thread_name(const char* name) {
} }
namespace { namespace {
#if !(defined(__GLIBC__) || defined(__APPLE__))
struct OpenFileRAII final {
explicit OpenFileRAII(const char* filename) : fd(::open(filename, O_RDONLY | O_CLOEXEC)) {}
~OpenFileRAII() {
int result = close(fd);
if (result != 0) {
throw std::runtime_error("Error closing file. Errno: " + std::to_string(errno));
}
}
int fd;
};
// get the name of a thread
int pthread_getname_np_gcompat(pthread_t thread, char *name, size_t len) {
ASSERT(thread == pthread_self(), "On musl systems, it's only supported to get the name of the current thread.");
auto file = OpenFileRAII("/proc/thread-self/comm");
ssize_t n;
if (file.fd < 0)
return errno;
n = read(file.fd, name, len);
if (n < 0)
return errno;
// if the trailing newline was not read, the buffer was too small
if (n == 0 || name[n - 1] != '\n')
return ERANGE;
// null-terminate string
name[n - 1] = '\0';
return 0;
}
#endif
std::string get_thread_name(pthread_t thread) { std::string get_thread_name(pthread_t thread) {
char name[MAX_NAME_LEN]; char name[MAX_NAME_LEN];
#if defined(__GLIBC__) || defined(__APPLE__)
int result = pthread_getname_np(thread, name, MAX_NAME_LEN); int result = pthread_getname_np(thread, name, MAX_NAME_LEN);
#else
int result = pthread_getname_np_gcompat(thread, name, MAX_NAME_LEN);
#endif
if (0 != result) { if (0 != result) {
throw std::runtime_error("Error getting thread name with pthread_getname_np. Code: " + std::to_string(result)); throw std::runtime_error("Error getting thread name with pthread_getname_np. Code: " + std::to_string(result));
} }
@ -39,16 +85,19 @@ std::string get_thread_name(pthread_t thread) {
name[MAX_NAME_LEN - 1] = '\0'; name[MAX_NAME_LEN - 1] = '\0';
return name; return name;
} }
} }
std::string get_thread_name() { std::string get_thread_name() {
return get_thread_name(pthread_self()); return get_thread_name(pthread_self());
} }
#if defined(__GLIBC__) || defined(__APPLE__)
std::string get_thread_name(std::thread* thread) { std::string get_thread_name(std::thread* thread) {
ASSERT(thread->joinable(), "Thread not running"); ASSERT(thread->joinable(), "Thread not running");
return get_thread_name(thread->native_handle()); return get_thread_name(thread->native_handle());
} }
#endif
} }

View File

@ -11,6 +11,24 @@ TEST(ThreadDebuggingTest_ThreadName, givenMainThread_whenSettingAndGetting_thenD
get_thread_name(); get_thread_name();
} }
TEST(ThreadDebuggingTest_ThreadName, givenMainThread_whenGettingFromInside_thenIsCorrect) {
set_thread_name("my_thread_name");
string name = get_thread_name();
EXPECT_EQ("my_thread_name", name);
}
TEST(ThreadDebuggingTest_ThreadName, givenChildThread_whenGettingFromInside_thenIsCorrect) {
std::thread child([] {
set_thread_name("my_thread_name");
string name = get_thread_name();
EXPECT_EQ("my_thread_name", name);
});
child.join();
}
#if defined(__GLIBC__) || defined(__APPLE__) || defined(_MSC_VER)
// disabled on musl because getting the thread name for a child thread doesn't work there
TEST(ThreadDebuggingTest_ThreadName, givenChildThread_whenSettingAndGetting_thenDoesntCrash) { TEST(ThreadDebuggingTest_ThreadName, givenChildThread_whenSettingAndGetting_thenDoesntCrash) {
ConditionBarrier nameIsChecked; ConditionBarrier nameIsChecked;
@ -27,37 +45,22 @@ TEST(ThreadDebuggingTest_ThreadName, givenChildThread_whenSettingAndGetting_then
EXPECT_TRUE(child_didnt_crash); EXPECT_TRUE(child_didnt_crash);
} }
TEST(ThreadDebuggingTest_ThreadName, givenMainThread_whenGettingFromInside_thenIsCorrect) {
set_thread_name("my_thread_name");
string name = get_thread_name();
EXPECT_EQ("my_thread_name", name);
}
TEST(ThreadDebuggingTest_ThreadName, givenChildThread_whenGettingFromInside_thenIsCorrect) {
std::thread child([] {
set_thread_name("my_thread_name");
string name = get_thread_name();
EXPECT_EQ("my_thread_name", name);
});
child.join();
}
TEST(ThreadDebuggingTest_ThreadName, givenChildThread_whenGettingFromOutside_thenIsCorrect) { TEST(ThreadDebuggingTest_ThreadName, givenChildThread_whenGettingFromOutside_thenIsCorrect) {
ConditionBarrier nameIsSet; ConditionBarrier nameIsSet;
ConditionBarrier nameIsChecked; ConditionBarrier nameIsChecked;
std::thread child([&] { std::thread child([&] {
set_thread_name("my_thread_name"); set_thread_name("my_thread_name");
nameIsSet.release(); nameIsSet.release();
nameIsChecked.wait(); nameIsChecked.wait();
}); });
nameIsSet.wait(); nameIsSet.wait();
set_thread_name("outer_thread_name"); // just to make sure the next line doesn't read the outer thread name set_thread_name("outer_thread_name"); // just to make sure the next line doesn't read the outer thread name
string name = get_thread_name(&child); string name = get_thread_name(&child);
EXPECT_EQ("my_thread_name", name); EXPECT_EQ("my_thread_name", name);
nameIsChecked.release(); nameIsChecked.release();
child.join(); child.join();
} }
#endif