Merge branch 'threadname' of https://github.com/lanurmi/wxWidgets
Add wxThread::SetName for naming threads for debugging purposes. See https://github.com/wxWidgets/wxWidgets/pull/2572
This commit is contained in:
@@ -596,7 +596,13 @@ public:
|
||||
// Delete() instead (or leave the thread terminate by itself)
|
||||
virtual ~wxThread();
|
||||
|
||||
// sets name to assist debugging
|
||||
static bool SetNameForCurrent(const wxString &name);
|
||||
|
||||
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,40 @@ 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);
|
||||
|
||||
/**
|
||||
Sets an internal name for the current thread, which may be a wxThread
|
||||
or any other kind of thread; e.g. an std::thread.
|
||||
|
||||
@return Either:
|
||||
- @false: Failure or missing implementation for this OS.
|
||||
- @true: Success or undetectable failure.
|
||||
|
||||
@since 3.1.6
|
||||
*/
|
||||
static bool SetNameForCurrent(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,96 @@ 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");
|
||||
|
||||
return SetNameForCurrent(name);
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool wxThread::SetNameForCurrent(const wxString &name)
|
||||
{
|
||||
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,38 @@ 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");
|
||||
|
||||
return SetNameForCurrent(name);
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool wxThread::SetNameForCurrent(const wxString &name)
|
||||
{
|
||||
// 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,
|
||||
|
Reference in New Issue
Block a user