Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/libslic3r/Thread.cpp')
-rw-r--r--src/libslic3r/Thread.cpp242
1 files changed, 242 insertions, 0 deletions
diff --git a/src/libslic3r/Thread.cpp b/src/libslic3r/Thread.cpp
new file mode 100644
index 000000000..d203acb90
--- /dev/null
+++ b/src/libslic3r/Thread.cpp
@@ -0,0 +1,242 @@
+#ifdef _WIN32
+ #include <windows.h>
+ #include <boost/nowide/convert.hpp>
+#else
+ // any posix system
+ #include <pthread.h>
+#endif
+
+#include <atomic>
+#include <condition_variable>
+#include <mutex>
+#include <tbb/parallel_for.h>
+#include <tbb/tbb_thread.h>
+#include <tbb/task_arena.h>
+#include <tbb/task_scheduler_init.h>
+
+#include "Thread.hpp"
+
+namespace Slic3r {
+
+#ifdef _WIN32
+// 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.
+
+typedef HRESULT(__stdcall* SetThreadDescriptionType)(HANDLE, PCWSTR);
+typedef HRESULT(__stdcall* GetThreadDescriptionType)(HANDLE, PWSTR*);
+
+static bool s_SetGetThreadDescriptionInitialized = false;
+static HMODULE s_hKernel32 = nullptr;
+static SetThreadDescriptionType s_fnSetThreadDescription = nullptr;
+static GetThreadDescriptionType s_fnGetThreadDescription = nullptr;
+
+static bool WindowsGetSetThreadNameAPIInitialize()
+{
+ 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;
+}
+
+#ifndef NDEBUG
+ // 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;
+
+#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)
+
+ static void WindowsSetThreadNameSEH(HANDLE hThread, const char* thread_name)
+ {
+ THREADNAME_INFO info;
+ info.dwType = 0x1000;
+ info.szName = thread_name;
+ info.dwThreadID = ::GetThreadId(hThread);
+ info.dwFlags = 0;
+ __try {
+ RaiseException(MSVC_SEH_EXCEPTION_NAME_THREAD, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ }
+ }
+#endif // NDEBUG
+
+static bool WindowsSetThreadName(HANDLE hThread, const char *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;
+}
+
+bool set_thread_name(std::thread &thread, const char *thread_name)
+{
+ return WindowsSetThreadName(static_cast<HANDLE>(thread.native_handle()), thread_name);
+}
+
+bool set_thread_name(boost::thread &thread, const char *thread_name)
+{
+ return WindowsSetThreadName(static_cast<HANDLE>(thread.native_handle()), 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;
+ s_fnGetThreadDescription(::GetCurrentThread(), &ptr);
+ return (ptr == nullptr) ? std::string() : boost::nowide::narrow(ptr);
+}
+
+#else // _WIN32
+
+#ifdef __APPLE__
+
+// Appe screwed the Posix norm.
+bool set_thread_name(std::thread &thread, const char *thread_name)
+{
+// not supported
+// pthread_setname_np(thread.native_handle(), thread_name);
+ return false;
+}
+
+bool set_thread_name(boost::thread &thread, const char *thread_name)
+{
+// not supported
+// pthread_setname_np(thread.native_handle(), thread_name);
+ return false;
+}
+
+bool set_current_thread_name(const char *thread_name)
+{
+ pthread_setname_np(thread_name);
+ return true;
+}
+
+std::optional<std::string> get_current_thread_name()
+{
+// not supported
+// char buf[16];
+// return std::string(thread_getname_np(buf, 16) == 0 ? buf : "");
+ return std::nullopt;
+}
+
+#else
+
+// posix
+bool set_thread_name(std::thread &thread, const char *thread_name)
+{
+ pthread_setname_np(thread.native_handle(), thread_name);
+ return true;
+}
+
+bool set_thread_name(boost::thread &thread, const char *thread_name)
+{
+ pthread_setname_np(thread.native_handle(), thread_name);
+ return true;
+}
+
+bool set_current_thread_name(const char *thread_name)
+{
+ pthread_setname_np(pthread_self(), thread_name);
+ return true;
+}
+
+std::optional<std::string> get_current_thread_name()
+{
+ char buf[16];
+ return std::string(pthread_getname_np(pthread_self(), buf, 16) == 0 ? buf : "");
+}
+
+#endif
+
+#endif // _WIN32
+
+// Spawn (n - 1) worker threads on Intel TBB thread pool and name them by an index and a system thread ID.
+void name_tbb_thread_pool_threads()
+{
+ static bool initialized = false;
+ if (initialized)
+ return;
+ initialized = true;
+
+ // see GH issue #5661 PrusaSlicer hangs on Linux when run with non standard task affinity
+ // TBB will respect the task affinity mask on Linux and spawn less threads than std::thread::hardware_concurrency().
+// const size_t nthreads_hw = std::thread::hardware_concurrency();
+ const size_t nthreads_hw = tbb::this_task_arena::max_concurrency();
+ size_t nthreads = nthreads_hw;
+
+#ifdef SLIC3R_PROFILE
+ // Shiny profiler is not thread safe, thus disable parallelization.
+ nthreads = 1;
+#endif
+
+ if (nthreads != nthreads_hw)
+ new tbb::task_scheduler_init(int(nthreads));
+
+ std::atomic<size_t> nthreads_running(0);
+ std::condition_variable cv;
+ std::mutex cv_m;
+ auto master_thread_id = tbb::this_tbb_thread::get_id();
+ tbb::parallel_for(
+ tbb::blocked_range<size_t>(0, nthreads, 1),
+ [&nthreads_running, nthreads, &master_thread_id, &cv, &cv_m](const tbb::blocked_range<size_t> &range) {
+ assert(range.begin() + 1 == range.end());
+ if (nthreads_running.fetch_add(1) + 1 == nthreads) {
+ // All threads are spinning.
+ // Wake them up.
+ cv.notify_all();
+ } else {
+ // Wait for the last thread to wake the others.
+ std::unique_lock<std::mutex> lk(cv_m);
+ cv.wait(lk, [&nthreads_running, nthreads]{return nthreads_running == nthreads;});
+ }
+ auto thread_id = tbb::this_tbb_thread::get_id();
+ if (thread_id == master_thread_id) {
+ // The calling thread runs the 0'th task.
+ assert(range.begin() == 0);
+ } else {
+ assert(range.begin() > 0);
+ std::ostringstream name;
+ name << "slic3r_tbb_" << range.begin();
+ set_current_thread_name(name.str().c_str());
+ }
+ });
+}
+
+}