Add wxThread::SetName for naming threads for debugging purposes
Such thread names can be shown by some debuggers, and depending on the OS and compiler versions used, they can be visible in process lists and crash dumps. Co-authored-by: PB <PBforDev@gmail.com>
This commit is contained in:
parent
9cc0c9a082
commit
fc756d06a6
@ -597,6 +597,9 @@ public:
|
||||
virtual ~wxThread();
|
||||
|
||||
protected:
|
||||
// sets name to assist debugging
|
||||
bool SetName(const wxString &name);
|
||||
|
||||
// exits from the current thread - can be called only from this thread
|
||||
void Exit(ExitCode exitcode = NULL);
|
||||
|
||||
|
@ -1385,6 +1385,28 @@ protected:
|
||||
OnExit() will be called just before exiting.
|
||||
*/
|
||||
void Exit(ExitCode exitcode = 0);
|
||||
|
||||
/**
|
||||
Sets an internal name for the thread, which enables the debugger to
|
||||
show the name along with the list of threads, as long as both the OS
|
||||
and the debugger support this. The thread name may also be visible
|
||||
in list of processes and in crash dumps (also in release builds).
|
||||
|
||||
This function is protected, as it should be called from a derived
|
||||
class, in the context of the derived thread. A good place to call this
|
||||
function is at the beginning of your Entry() function.
|
||||
|
||||
For portable code, the name should be in ASCII. On Linux the length
|
||||
is truncated to 15 characters, on other platforms the name can be
|
||||
longer than that.
|
||||
|
||||
@return Either:
|
||||
- @false: Failure or missing implementation for this OS.
|
||||
- @true: Success or undetectable failure.
|
||||
|
||||
@since 3.1.6
|
||||
*/
|
||||
bool SetName(const wxString &name);
|
||||
};
|
||||
|
||||
|
||||
|
@ -919,6 +919,10 @@ MyThread::~MyThread()
|
||||
|
||||
wxThread::ExitCode MyThread::Entry()
|
||||
{
|
||||
// setting thread name helps with debugging, as the debugger
|
||||
// may be able to show thread names along with the list of threads.
|
||||
SetName("My Thread");
|
||||
|
||||
wxLogMessage("Thread started (priority = %u).", GetPriority());
|
||||
|
||||
for ( m_count = 0; m_count < 10; m_count++ )
|
||||
@ -970,6 +974,10 @@ void MyWorkerThread::OnExit()
|
||||
|
||||
wxThread::ExitCode MyWorkerThread::Entry()
|
||||
{
|
||||
// setting thread name helps with debugging, as the debugger
|
||||
// may be able to show thread names along with the list of threads.
|
||||
SetName("Worker Thread");
|
||||
|
||||
#if TEST_YIELD_RACE_CONDITION
|
||||
if ( TestDestroy() )
|
||||
return NULL;
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "wx/msw/seh.h"
|
||||
|
||||
#include "wx/except.h"
|
||||
#include "wx/dynlib.h"
|
||||
|
||||
// must have this symbol defined to get _beginthread/_endthread declarations
|
||||
#ifndef _MT
|
||||
@ -1121,6 +1122,90 @@ wxThreadError wxThread::Kill()
|
||||
return rc;
|
||||
}
|
||||
|
||||
// At least MSVC 2017 version 15.6 is required for observing the
|
||||
// thread names set using this method.
|
||||
// Windows 10 version 1607 is required for the SetThreadDescription
|
||||
// function.
|
||||
static bool wxSetThreadNameOnWindows10(const WCHAR *threadName)
|
||||
{
|
||||
typedef HRESULT(WINAPI* SetThreadDescription_t)(HANDLE, PCWSTR);
|
||||
static SetThreadDescription_t s_pfnSetThreadDescription = NULL;
|
||||
|
||||
static bool s_initDone = false;
|
||||
if ( !s_initDone )
|
||||
{
|
||||
wxLoadedDLL dllKernel32("kernel32.dll");
|
||||
wxDL_INIT_FUNC(s_pfn, SetThreadDescription, dllKernel32);
|
||||
s_initDone = true;
|
||||
}
|
||||
|
||||
if ( s_pfnSetThreadDescription )
|
||||
{
|
||||
HRESULT r = s_pfnSetThreadDescription(GetCurrentThread(), threadName);
|
||||
if (SUCCEEDED(r))
|
||||
return true;
|
||||
else
|
||||
wxLogApiError("SetThreadDescription", r);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// This function works with all MSVC versions.
|
||||
static bool wxSetThreadNameOnAnyMSVC(const char* threadName)
|
||||
{
|
||||
// This implementation is taken almost verbatim from:
|
||||
// https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
|
||||
|
||||
const DWORD MS_VC_EXCEPTION = 0x406D1388;
|
||||
#pragma pack(push,8)
|
||||
typedef struct tagTHREADNAME_INFO
|
||||
{
|
||||
DWORD dwType; // Must be 0x1000.
|
||||
LPCSTR szName; // Pointer to name (in user addr space).
|
||||
DWORD dwThreadID; // Thread ID (-1=caller thread).
|
||||
DWORD dwFlags; // Reserved for future use, must be zero.
|
||||
} THREADNAME_INFO;
|
||||
#pragma pack(pop)
|
||||
|
||||
THREADNAME_INFO info;
|
||||
info.dwType = 0x1000;
|
||||
info.szName = threadName;
|
||||
info.dwThreadID = (DWORD)-1;
|
||||
info.dwFlags = 0;
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 6320 6322)
|
||||
__try
|
||||
{
|
||||
RaiseException(MS_VC_EXCEPTION, 0,
|
||||
sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
|
||||
return true;
|
||||
}
|
||||
__except (EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
}
|
||||
#pragma warning(pop)
|
||||
return false;
|
||||
}
|
||||
#endif // MSC_VER
|
||||
|
||||
bool wxThread::SetName(const wxString &name)
|
||||
{
|
||||
wxCHECK_MSG(this == This(), false,
|
||||
"SetName() must be called in the context of the thread to be named");
|
||||
|
||||
bool retval = wxSetThreadNameOnWindows10(name.wc_str());
|
||||
|
||||
// Even if the method above succeeded, we can set
|
||||
// the name through this other, independent way also.
|
||||
#ifdef _MSC_VER
|
||||
retval |= wxSetThreadNameOnAnyMSVC(name.c_str());
|
||||
#endif
|
||||
|
||||
// return true if at least one call succeeded
|
||||
return retval;
|
||||
}
|
||||
|
||||
void wxThread::Exit(ExitCode status)
|
||||
{
|
||||
wxThreadInternal::DoThreadOnExit(this);
|
||||
|
@ -1680,6 +1680,33 @@ wxThreadError wxThread::Kill()
|
||||
}
|
||||
}
|
||||
|
||||
bool wxThread::SetName(const wxString &name)
|
||||
{
|
||||
wxCHECK_MSG(this == This(), false,
|
||||
"SetName() must be called from inside the thread to be named");
|
||||
|
||||
|
||||
// the API is nearly the same on different *nix, but not quite:
|
||||
|
||||
#if defined(__DARWIN__)
|
||||
pthread_setname_np(name.utf8_str());
|
||||
return true;
|
||||
#elif defined(__LINUX__)
|
||||
// Linux doesn't allow names longer than 15 bytes.
|
||||
char truncatedName[16] = { 0 };
|
||||
strncpy(truncatedName, name.utf8_str(), 15);
|
||||
|
||||
return pthread_setname_np(pthread_self(), truncatedName) == 0;
|
||||
#else
|
||||
wxLogDebug("No implementation for wxThread::SetName() on this OS.");
|
||||
return false;
|
||||
#endif
|
||||
// TODO: #elif defined(__FREEBSD__) || defined(__OPENBSD__)
|
||||
// TODO: These two BSDs would need #include <pthread_np.h>
|
||||
// and the function call would be:
|
||||
// pthread_set_name_np(pthread_self(), name.utf8_str());
|
||||
}
|
||||
|
||||
void wxThread::Exit(ExitCode status)
|
||||
{
|
||||
wxASSERT_MSG( This() == this,
|
||||
|
Loading…
Reference in New Issue
Block a user