In folly/system/ThreadName.h, we have the following functions for setting thread names:
bool canSetCurrentThreadName();
bool canSetOtherThreadName();
bool setThreadName(std::thread::id tid, StringPiece name);
bool setThreadName(pthread_t pid, StringPiece name);
bool setThreadName(StringPiece name);
Let’s run something really simple
#include <folly/init/Init.h>
#include <folly/system/ThreadName.h>
#include <glog/logging.h>
int main(int argc, char **argv) {
folly::init(&argc, &argv);
auto thr = std::thread([]() {
folly::setThreadName("newThread");
LOG(INFO) << "new thread exits";
});
auto name = folly::getThreadName(thr.get_id());
LOG(INFO) << "Main waiting on "
<< (name.hasValue() ? name.value() : " the other thread");
thr.join();
}
The main thread pulls thr‘s name, which is racy. When the setThreadName is not called, what is the default thread name?
~ $ ./a.out -logtostderr
I0922 16:50:37.111975 957 thread.cc:11] new thread exits
I0922 16:50:37.111953 956 thread.cc:14] Main waiting on a.out
~ $ ./a.out -logtostderr
I0922 16:50:37.515489 958 thread.cc:14] Main waiting on newThread
I0922 16:50:37.515491 959 thread.cc:11] new thread exits
It looks like the default thread name is the program name. Actually if we take a look at pthread_setname_np(3), the manual says
By default, all the threads created using pthread_create() inherit the program name.
That looks simple. Let’s poke around to see how they’re implemented.
Implementation
static constexpr size_t kMaxThreadNameLength = 16;
bool setThreadName(std::thread::id tid, StringPiece name) {
name = name.subpiece(0, kMaxThreadNameLength - 1);
char buf[kMaxThreadNameLength] = {};
std::memcpy(buf, name.data(), name.size());
auto id = stdTidToPthreadId(tid);
return 0 == pthread_setname_np(id, buf);
}
The other two setThreadName eventually calls the above function.
In pthread_setname_np(3), the manual says the length of name is restricted to 16 chars, including the null byte (‘\0’). That’s why this implementation creates an array of 16 bytes.
The confusing part here is the conversion between pthread_t, pid_t and std::thread::id.
If your Linux distribution is different, the header file location might be different, try to find it using
~ $ find /usr/include -name 'pthreadtypes.h'
/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h
If we take a look at the header file on our local Linux machine, the definition is
typedef unsigned long int pthread_t;
And the std::thread::id is in /usr/include/c++/7/thread
/// thread
class thread
{
typedef __gthread_t native_handle_type;
class id
{
native_handle_type _M_thread;
...
};
private:
id _M_id;
...
}
where the __gthread_t is defined in /usr/include/x86_64-linux-gnu/c++/7/bits/. For POSIX, it’s an alias of pthread_t. So on POSIX compiliant system, std::thread::id is pthread_t.
As a library, how does folly guarantee the memcpy copies the same type on all platform? In stdTidToPthreadId, it has compile-time checks:
#if FOLLY_HAVE_PTHREAD && !_WIN32
pthread_t stdTidToPthreadId(std::thread::id tid) {
static_assert(
std::is_same<pthread_t, std::thread::native_handle_type>::value,
"This assumes that the native handle type is pthread_t");
static_assert(
sizeof(std::thread::native_handle_type) == sizeof(std::thread::id),
"This assumes std::thread::id is a thin wrapper around "
"std::thread::native_handle_type, but that doesn't appear to be true.");
...
}
...