Skip to content

Commit

Permalink
COMMON: Add ability to set thread names
Browse files Browse the repository at this point in the history
Does not work on all platforms, but is a no-op where it doesn't.
  • Loading branch information
clone2727 committed Mar 17, 2019
1 parent e13054f commit b932685
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 0 deletions.
91 changes: 91 additions & 0 deletions src/common/thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@
#include "src/common/thread.h"
#include "src/common/util.h"

#if defined(__linux__)
#include <sys/prctl.h>
#elif defined(_WIN32)
#include <boost/scope_exit.hpp>
#endif

namespace Common {

Thread::Thread() : _killThread(false), _threadRunning(false) {
Expand Down Expand Up @@ -104,6 +110,9 @@ int Thread::threadHelper(void *obj) {
// The thread is running.
thread->_threadRunning.store(true, std::memory_order_relaxed);

// Attempt to set the thread name
setCurrentThreadName(thread->_name);

// Run the thread
thread->threadMethod();

Expand All @@ -113,4 +122,86 @@ int Thread::threadHelper(void *obj) {
return 0;
}

void Thread::setCurrentThreadName(const Common::UString &name) {
#if defined(__linux__)
// We need to fit into a 16 byte array
char buffer[16];
size_t length = std::min(name.size(), sizeof(buffer) - 1);
memcpy(buffer, name.c_str(), length);
buffer[length] = '\0';

// Invoke prctl with PR_SET_NAME. This is slightly more portable than
// pthread_setname_np on Linux, but the end result is identical.
prctl(PR_SET_NAME, buffer);
#elif defined(__APPLE__)
// pthread_setname_np is available on 10.6+
typedef void (*SetNameFunc)(const char *);
SetNameFunc func = (SetNameFunc)dlsym(RTLD_DEFAULT, "pthread_setname_np");
if (func)
func(name.c_str());
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
pthread_set_name_np(pthread_self(), name.c_str());
#elif defined(_WIN32)
#ifdef _MSC_VER
// To set the name in the debugger, we need to use SEH. Use the non-header-defined
// struct. It has to be aligned on an 8 byte boundary, even on 32-bit.
struct alignas(8) {
DWORD dwType;
LPCSTR szName;
DWORD dwThreadID;
DWORD dwFlags;
} info;

// Fill with desired values
info.dwType = 0x1000;
info.szName = name.c_str();
info.dwThreadID = -1;
info.dwFlags = 0;

__try {
RaiseException(0x406D1388, 0, sizeof(info) / sizeof(ULONG_PTR), reinterpret_cast<ULONG_PTR *>(&info));
} __except (EXCEPTION_EXECUTE_HANDLER) {
// Do nothing
}
#else
// MinGW's pthreads implementation provides pthread_setname_np. All this
// basically wraps the above MSVC code into the pthread API. We could manually
// set up the exception handler and use it for both, but it's a major pain.
pthread_setname_np(pthread_self(), name.c_str());
#endif

// On Windows 10, there's another way to set the thread -- the SetThreadDescription
// function. Let's see if we have it available. Get the kernel32.dll handle.
// This is a different name from the above exception method.
HMODULE kernel32 = LoadLibraryA("kernel32.dll");
if (!kernel32)
return;

// Set it to destroy upon exit
BOOST_SCOPE_EXIT(kernel32) {
FreeLibrary(kernel32);
} BOOST_SCOPE_EXIT_END;

typedef HRESULT (*SetThreadDescriptionFunc)(HANDLE hThread, PCWSTR lpThreadDescription);
SetThreadDescriptionFunc func = (SetThreadDescriptionFunc)GetProcAddress(kernel32, "SetThreadDescription");
if (!func)
return;

// Need to convert to a wide char string. First, query the size.
int result = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, name.c_str(), name.size(), nullptr, 0);
if (!result)
return;

// Now do the actual conversion.
std::unique_ptr<wchar_t[]> buffer(new wchar_t[result + 1]);
MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, name.c_str(), name.size(), buffer.get(), result);

// Actually invoke the function
func(GetCurrentThread(), buffer.get());
#else
// Do nothing; silence the unused warning
(void)name;
#endif
}

} // End of namespace Common
7 changes: 7 additions & 0 deletions src/common/thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ class Thread : boost::noncopyable {
bool createThread(const UString &name = "");
bool destroyThread();

/**
* Set the name of the thread, if available on the platform.
* Otherwise, a no-op. If the name is longer than the platform
* supports, it is truncated to fit.
*/
static void setCurrentThreadName(const Common::UString &name);

protected:
std::atomic<bool> _killThread;

Expand Down

0 comments on commit b932685

Please sign in to comment.