Commit 1674ee82 authored by Orvid King's avatar Orvid King Committed by Facebook Github Bot

Support setThreadName on Windows

Summary: Windows doesn't have a nice function to do this, so we have to do it ourself, throwing the right exception with a struct that no public header defines, because Windows.

Reviewed By: akrieger

Differential Revision: D5100900

fbshipit-source-id: 4f6840e50c5c1aef36adade098739201c97b9af7
parent 511ddcc6
......@@ -21,6 +21,7 @@
#include <folly/Portability.h>
#include <folly/Traits.h>
#include <folly/portability/PThread.h>
#include <folly/portability/Windows.h>
namespace folly {
......@@ -64,7 +65,7 @@ pthread_t stdTidToPthreadId(std::thread::id tid) {
bool canSetCurrentThreadName() {
#if FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME || \
FOLLY_HAS_PTHREAD_SETNAME_NP_NAME
FOLLY_HAS_PTHREAD_SETNAME_NP_NAME || _WIN32
return true;
#else
return false;
......@@ -72,7 +73,7 @@ bool canSetCurrentThreadName() {
}
bool canSetOtherThreadName() {
#if FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME
#if FOLLY_HAS_PTHREAD_SETNAME_NP_THREAD_NAME || _WIN32
return true;
#else
return false;
......@@ -90,6 +91,8 @@ Optional<std::string> getThreadName(std::thread::id id) {
}
return folly::make_optional(std::string(buf.data()));
#else
// There's not actually a way to get the thread name on Windows because
// thread names are a concept managed by the debugger, not the runtime.
return Optional<std::string>();
#endif
}
......@@ -99,8 +102,47 @@ Optional<std::string> getCurrentThreadName() {
}
bool setThreadName(std::thread::id tid, StringPiece name) {
#if !FOLLY_HAVE_PTHREAD || _WIN32
return false;
auto trimmedName = name.subpiece(0, kMaxThreadNameLength - 1).str();
#if _WIN32
static_assert(
sizeof(unsigned int) == sizeof(std::thread::id),
"This assumes std::thread::id is a thin wrapper around "
"the thread id as an unsigned int, but that doesn't appear to be true.");
// http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
#pragma pack(push, 8)
struct THREADNAME_INFO {
DWORD dwType; // Must be 0x1000
LPCSTR szName; // Pointer to name (in user address space)
DWORD dwThreadID; // Thread ID (-1 for caller thread)
DWORD dwFlags; // Reserved for future use; must be zero
};
union TNIUnion {
THREADNAME_INFO tni;
ULONG_PTR upArray[4];
};
#pragma pack(pop)
static constexpr DWORD kMSVCException = 0x406D1388;
// std::thread::id is a thin wrapper around an integral thread id,
// so just extract the ID.
unsigned int id;
std::memcpy(&id, &tid, sizeof(id));
TNIUnion tniUnion = {0x1000, trimmedName.data(), id, 0};
// This has to be in a separate stack frame from trimmedName, which requires
// C++ object destruction semantics.
return [&]() {
__try {
RaiseException(kMSVCException, 0, 4, tniUnion.upArray);
} __except (
GetExceptionCode() == kMSVCException ? EXCEPTION_CONTINUE_EXECUTION
: EXCEPTION_EXECUTE_HANDLER) {
// Swallow the exception when a debugger isn't attached.
}
return true;
}();
#else
name = name.subpiece(0, kMaxThreadNameLength - 1);
char buf[kMaxThreadNameLength] = {};
......@@ -125,8 +167,17 @@ bool setThreadName(std::thread::id tid, StringPiece name) {
#if FOLLY_HAVE_PTHREAD
bool setThreadName(pthread_t pid, StringPiece name) {
#if _WIN32
// Not currently supported on Windows.
return false;
static_assert(
sizeof(unsigned int) == sizeof(std::thread::id),
"This assumes std::thread::id is a thin wrapper around "
"the thread id as an unsigned int, but that doesn't appear to be true.");
// std::thread::id is a thin wrapper around an integral thread id,
// so just stick the ID in.
unsigned int tid = pthread_getw32threadid_np(pid);
std::thread::id id;
std::memcpy(&id, &tid, sizeof(id));
return setThreadName(id, name);
#else
static_assert(
std::is_same<pthread_t, std::thread::native_handle_type>::value,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment