mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-12 09:17:52 -06:00
Refactored loading of Get/SetThreadDescription() on Windows:
These new API functions are not available on Windows 7 and on older Windows 10, thus they are newly loaded dynamically and the functions using it retur a bool indicating whether the functionality is supported or not. Also the OSX variants that are not supported newly return false instead of throwing an exception. Fixes #4972 #4974
This commit is contained in:
parent
493cdbd069
commit
ce020781d3
3 changed files with 119 additions and 73 deletions
|
@ -213,11 +213,12 @@ std::string AppConfig::load()
|
||||||
|
|
||||||
void AppConfig::save()
|
void AppConfig::save()
|
||||||
{
|
{
|
||||||
#ifndef __APPLE__
|
{
|
||||||
// Apple does not implement thread_getname_np() correctly.
|
// Returns "undefined" if the thread naming functionality is not supported by the operating system.
|
||||||
if (get_current_thread_name() != "slic3r_main")
|
std::optional<std::string> current_thread_name = get_current_thread_name();
|
||||||
|
if (current_thread_name && *current_thread_name != "slic3r_main")
|
||||||
throw CriticalException("Calling AppConfig::save() from a worker thread!");
|
throw CriticalException("Calling AppConfig::save() from a worker thread!");
|
||||||
#endif
|
}
|
||||||
|
|
||||||
// The config is first written to a file with a PID suffix and then moved
|
// The config is first written to a file with a PID suffix and then moved
|
||||||
// to avoid race conditions with multiple instances of Slic3r
|
// to avoid race conditions with multiple instances of Slic3r
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <processthreadsapi.h>
|
|
||||||
#include <boost/nowide/convert.hpp>
|
#include <boost/nowide/convert.hpp>
|
||||||
#else
|
#else
|
||||||
// any posix system
|
// any posix system
|
||||||
|
@ -13,34 +12,43 @@
|
||||||
#include <tbb/tbb_thread.h>
|
#include <tbb/tbb_thread.h>
|
||||||
#include <tbb/task_scheduler_init.h>
|
#include <tbb/task_scheduler_init.h>
|
||||||
|
|
||||||
#define SLIC3R_THREAD_NAME_WIN32_MODERN
|
|
||||||
|
|
||||||
#include "Thread.hpp"
|
#include "Thread.hpp"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#ifdef SLIC3R_THREAD_NAME_WIN32_MODERN
|
// The new API is better than the old SEH style thread naming since the names also show up in crash dumpsand ETW traces.
|
||||||
|
// Because the new API is only available on newer Windows 10, look it up dynamically.
|
||||||
|
|
||||||
static void WindowsSetThreadName(HANDLE hThread, const char *thread_name)
|
typedef HRESULT(__stdcall* SetThreadDescriptionType)(HANDLE, PCWSTR);
|
||||||
{
|
typedef HRESULT(__stdcall* GetThreadDescriptionType)(HANDLE, PWSTR*);
|
||||||
size_t len = strlen(thread_name);
|
|
||||||
if (len < 1024) {
|
static bool s_SetGetThreadDescriptionInitialized = false;
|
||||||
// Allocate the temp string on stack.
|
static HMODULE s_hKernel32 = nullptr;
|
||||||
wchar_t buf[1024];
|
static SetThreadDescriptionType s_fnSetThreadDescription = nullptr;
|
||||||
::SetThreadDescription(hThread, boost::nowide::widen(buf, 1024, thread_name));
|
static GetThreadDescriptionType s_fnGetThreadDescription = nullptr;
|
||||||
} else {
|
|
||||||
// Allocate dynamically.
|
static bool WindowsGetSetThreadNameAPIInitialize()
|
||||||
::SetThreadDescription(hThread, boost::nowide::widen(thread_name).c_str());
|
{
|
||||||
|
if (! s_SetGetThreadDescriptionInitialized) {
|
||||||
|
// Not thread safe! It is therefore a good idea to name the main thread before spawning worker threads
|
||||||
|
// to initialize
|
||||||
|
s_hKernel32 = LoadLibraryW(L"Kernel32.dll");
|
||||||
|
if (s_hKernel32) {
|
||||||
|
s_fnSetThreadDescription = (SetThreadDescriptionType)::GetProcAddress(s_hKernel32, "SetThreadDescription");
|
||||||
|
s_fnGetThreadDescription = (GetThreadDescriptionType)::GetProcAddress(s_hKernel32, "GetThreadDescription");
|
||||||
}
|
}
|
||||||
|
s_SetGetThreadDescriptionInitialized = true;
|
||||||
}
|
}
|
||||||
|
return s_fnSetThreadDescription && s_fnGetThreadDescription;
|
||||||
|
}
|
||||||
|
|
||||||
#else // SLIC3R_THREAD_NAME_WIN32_MODERN
|
#ifndef NDEBUG
|
||||||
// use the old way by throwing an exception
|
// Use the old way by throwing an exception, so at least in Debug mode the thread names are shown by the debugger.
|
||||||
|
static constexpr DWORD MSVC_SEH_EXCEPTION_NAME_THREAD = 0x406D1388;
|
||||||
|
|
||||||
const DWORD MS_VC_EXCEPTION=0x406D1388;
|
#pragma pack(push,8)
|
||||||
|
|
||||||
#pragma pack(push,8)
|
|
||||||
typedef struct tagTHREADNAME_INFO
|
typedef struct tagTHREADNAME_INFO
|
||||||
{
|
{
|
||||||
DWORD dwType; // Must be 0x1000.
|
DWORD dwType; // Must be 0x1000.
|
||||||
|
@ -48,46 +56,69 @@ namespace Slic3r {
|
||||||
DWORD dwThreadID; // Thread ID (-1=caller thread).
|
DWORD dwThreadID; // Thread ID (-1=caller thread).
|
||||||
DWORD dwFlags; // Reserved for future use, must be zero.
|
DWORD dwFlags; // Reserved for future use, must be zero.
|
||||||
} THREADNAME_INFO;
|
} THREADNAME_INFO;
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
static void WindowsSetThreadName(HANDLE hThread, const char *thread_name)
|
|
||||||
|
static void WindowsSetThreadNameSEH(HANDLE hThread, const char* thread_name)
|
||||||
{
|
{
|
||||||
THREADNAME_INFO info;
|
THREADNAME_INFO info;
|
||||||
info.dwType = 0x1000;
|
info.dwType = 0x1000;
|
||||||
info.szName = threadName;
|
info.szName = thread_name;
|
||||||
info.dwThreadID = ::GetThreadId(hThread);
|
info.dwThreadID = ::GetThreadId(hThread);
|
||||||
info.dwFlags = 0;
|
info.dwFlags = 0;
|
||||||
|
__try {
|
||||||
__try
|
RaiseException(MSVC_SEH_EXCEPTION_NAME_THREAD, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
|
||||||
{
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
||||||
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info);
|
|
||||||
}
|
|
||||||
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
||||||
{
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif // NDEBUG
|
||||||
|
|
||||||
#endif // SLIC3R_THREAD_NAME_WIN32_MODERN
|
static bool WindowsSetThreadName(HANDLE hThread, const char *thread_name)
|
||||||
|
|
||||||
// posix
|
|
||||||
void set_thread_name(std::thread &thread, const char *thread_name)
|
|
||||||
{
|
{
|
||||||
WindowsSetThreadName(static_cast<HANDLE>(thread.native_handle()), thread_name);
|
if (! WindowsGetSetThreadNameAPIInitialize()) {
|
||||||
|
#ifdef NDEBUG
|
||||||
|
return false;
|
||||||
|
#else // NDEBUG
|
||||||
|
// Running on Windows 7 or old Windows 7 in debug mode,
|
||||||
|
// inform the debugger about the thread name by throwing an SEH.
|
||||||
|
WindowsSetThreadNameSEH(hThread, thread_name);
|
||||||
|
return true;
|
||||||
|
#endif // NDEBUG
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t len = strlen(thread_name);
|
||||||
|
if (len < 1024) {
|
||||||
|
// Allocate the temp string on stack.
|
||||||
|
wchar_t buf[1024];
|
||||||
|
s_fnSetThreadDescription(hThread, boost::nowide::widen(buf, 1024, thread_name));
|
||||||
|
} else {
|
||||||
|
// Allocate dynamically.
|
||||||
|
s_fnSetThreadDescription(hThread, boost::nowide::widen(thread_name).c_str());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_thread_name(boost::thread &thread, const char *thread_name)
|
bool set_thread_name(std::thread &thread, const char *thread_name)
|
||||||
{
|
{
|
||||||
WindowsSetThreadName(static_cast<HANDLE>(thread.native_handle()), thread_name);
|
return WindowsSetThreadName(static_cast<HANDLE>(thread.native_handle()), thread_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_current_thread_name(const char *thread_name)
|
bool set_thread_name(boost::thread &thread, const char *thread_name)
|
||||||
{
|
{
|
||||||
WindowsSetThreadName(::GetCurrentThread(), thread_name);
|
return WindowsSetThreadName(static_cast<HANDLE>(thread.native_handle()), thread_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string get_current_thread_name()
|
bool set_current_thread_name(const char *thread_name)
|
||||||
{
|
{
|
||||||
|
return WindowsSetThreadName(::GetCurrentThread(), thread_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> get_current_thread_name()
|
||||||
|
{
|
||||||
|
if (! WindowsGetSetThreadNameAPIInitialize())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
wchar_t *ptr = nullptr;
|
wchar_t *ptr = nullptr;
|
||||||
::GetThreadDescription(::GetCurrentThread(), &ptr);
|
s_fnGetThreadDescription(::GetCurrentThread(), &ptr);
|
||||||
return (ptr == nullptr) ? std::string() : boost::nowide::narrow(ptr);
|
return (ptr == nullptr) ? std::string() : boost::nowide::narrow(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,52 +127,56 @@ std::string get_current_thread_name()
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
|
|
||||||
// Appe screwed the Posix norm.
|
// Appe screwed the Posix norm.
|
||||||
void set_thread_name(std::thread &thread, const char *thread_name)
|
bool set_thread_name(std::thread &thread, const char *thread_name)
|
||||||
{
|
{
|
||||||
// not supported
|
// not supported
|
||||||
// pthread_setname_np(thread.native_handle(), thread_name);
|
// pthread_setname_np(thread.native_handle(), thread_name);
|
||||||
throw CriticalException("Not supported");
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_thread_name(boost::thread &thread, const char *thread_name)
|
bool set_thread_name(boost::thread &thread, const char *thread_name)
|
||||||
{
|
{
|
||||||
// not supported
|
// not supported
|
||||||
// pthread_setname_np(thread.native_handle(), thread_name);
|
// pthread_setname_np(thread.native_handle(), thread_name);
|
||||||
throw CriticalException("Not supported");
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_current_thread_name(const char *thread_name)
|
bool set_current_thread_name(const char *thread_name)
|
||||||
{
|
{
|
||||||
pthread_setname_np(thread_name);
|
pthread_setname_np(thread_name);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string get_current_thread_name()
|
std::optional<std::string> get_current_thread_name()
|
||||||
{
|
{
|
||||||
// not supported
|
// not supported
|
||||||
// char buf[16];
|
// char buf[16];
|
||||||
// return std::string(thread_getname_np(buf, 16) == 0 ? buf : "");
|
// return std::string(thread_getname_np(buf, 16) == 0 ? buf : "");
|
||||||
throw CriticalException("Not supported");
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
// posix
|
// posix
|
||||||
void set_thread_name(std::thread &thread, const char *thread_name)
|
bool set_thread_name(std::thread &thread, const char *thread_name)
|
||||||
{
|
{
|
||||||
pthread_setname_np(thread.native_handle(), thread_name);
|
pthread_setname_np(thread.native_handle(), thread_name);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_thread_name(boost::thread &thread, const char *thread_name)
|
bool set_thread_name(boost::thread &thread, const char *thread_name)
|
||||||
{
|
{
|
||||||
pthread_setname_np(thread.native_handle(), thread_name);
|
pthread_setname_np(thread.native_handle(), thread_name);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_current_thread_name(const char *thread_name)
|
bool set_current_thread_name(const char *thread_name)
|
||||||
{
|
{
|
||||||
pthread_setname_np(pthread_self(), thread_name);
|
pthread_setname_np(pthread_self(), thread_name);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string get_current_thread_name()
|
std::optional<std::string> get_current_thread_name()
|
||||||
{
|
{
|
||||||
char buf[16];
|
char buf[16];
|
||||||
return std::string(pthread_getname_np(pthread_self(), buf, 16) == 0 ? buf : "");
|
return std::string(pthread_getname_np(pthread_self(), buf, 16) == 0 ? buf : "");
|
||||||
|
@ -168,7 +203,7 @@ void name_tbb_thread_pool_threads()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (nthreads != nthreads_hw)
|
if (nthreads != nthreads_hw)
|
||||||
new tbb::task_scheduler_init(nthreads);
|
new tbb::task_scheduler_init(int(nthreads));
|
||||||
|
|
||||||
std::atomic<size_t> nthreads_running(0);
|
std::atomic<size_t> nthreads_running(0);
|
||||||
std::condition_variable cv;
|
std::condition_variable cv;
|
||||||
|
|
|
@ -9,17 +9,27 @@
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
// Set / get thread name.
|
// Set / get thread name.
|
||||||
|
// Returns false if the API is not supported.
|
||||||
|
//
|
||||||
|
// It is a good idea to name the main thread before spawning children threads, because dynamic linking is used on Windows 10
|
||||||
|
// to initialize Get/SetThreadDescription functions, which is not thread safe.
|
||||||
|
//
|
||||||
// pthread_setname_np supports maximum 15 character thread names! (16th character is the null terminator)
|
// pthread_setname_np supports maximum 15 character thread names! (16th character is the null terminator)
|
||||||
|
//
|
||||||
// Methods taking the thread as an argument are not supported by OSX.
|
// Methods taking the thread as an argument are not supported by OSX.
|
||||||
void set_thread_name(std::thread &thread, const char *thread_name);
|
// Naming threads is only supported on newer Windows 10.
|
||||||
inline void set_thread_name(std::thread &thread, const std::string &thread_name) { set_thread_name(thread, thread_name.c_str()); }
|
|
||||||
void set_thread_name(boost::thread &thread, const char *thread_name);
|
|
||||||
inline void set_thread_name(boost::thread &thread, const std::string &thread_name) { set_thread_name(thread, thread_name.c_str()); }
|
|
||||||
void set_current_thread_name(const char *thread_name);
|
|
||||||
inline void set_current_thread_name(const std::string &thread_name) { set_current_thread_name(thread_name.c_str()); }
|
|
||||||
|
|
||||||
|
bool set_thread_name(std::thread &thread, const char *thread_name);
|
||||||
|
inline bool set_thread_name(std::thread &thread, const std::string &thread_name) { return set_thread_name(thread, thread_name.c_str()); }
|
||||||
|
bool set_thread_name(boost::thread &thread, const char *thread_name);
|
||||||
|
inline bool set_thread_name(boost::thread &thread, const std::string &thread_name) { return set_thread_name(thread, thread_name.c_str()); }
|
||||||
|
bool set_current_thread_name(const char *thread_name);
|
||||||
|
inline bool set_current_thread_name(const std::string &thread_name) { return set_current_thread_name(thread_name.c_str()); }
|
||||||
|
|
||||||
|
// Returns nullopt if not supported.
|
||||||
// Not supported by OSX.
|
// Not supported by OSX.
|
||||||
std::string get_current_thread_name();
|
// Naming threads is only supported on newer Windows 10.
|
||||||
|
std::optional<std::string> get_current_thread_name();
|
||||||
|
|
||||||
// To be called somewhere before the TBB threads are spinned for the first time, to
|
// To be called somewhere before the TBB threads are spinned for the first time, to
|
||||||
// give them names recognizible in the debugger.
|
// give them names recognizible in the debugger.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue