diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index be432d487a..58406fddc7 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -46,6 +46,7 @@ #include "libslic3r/Format/SL1.hpp" #include "libslic3r/Utils.hpp" #include "libslic3r/AppConfig.hpp" +#include "libslic3r/Thread.hpp" #include "PrusaSlicer.hpp" @@ -62,6 +63,9 @@ using namespace Slic3r; int CLI::run(int argc, char **argv) { + // Mark the main thread for the debugger and for runtime checks. + set_current_thread_name("slic3r_main"); + #ifdef __WXGTK__ // On Linux, wxGTK has no support for Wayland, and the app crashes on // startup if gtk3 is used. This env var has to be set explicitly to diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 61f77209e1..9b9e8618ae 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -201,6 +201,8 @@ add_library(libslic3r STATIC Utils.hpp Time.cpp Time.hpp + Thread.cpp + Thread.hpp TriangleSelector.cpp TriangleSelector.hpp MTUtils.hpp diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 51682e8a70..0844e9732f 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -10,6 +10,7 @@ #include "I18N.hpp" #include "ShortestPath.hpp" #include "SupportMaterial.hpp" +#include "Thread.hpp" #include "GCode.hpp" #include "GCode/WipeTower.hpp" #include "Utils.hpp" @@ -1604,6 +1605,8 @@ void Print::auto_assign_extruders(ModelObject* model_object) const // Slicing process, running at a background thread. void Print::process() { + name_tbb_thread_pool_threads(); + BOOST_LOG_TRIVIAL(info) << "Starting the slicing process." << log_memory_info(); for (PrintObject *obj : m_objects) obj->make_perimeters(); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index d08ea61615..d2652bac52 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1688,12 +1688,6 @@ void PrintObject::_slice(const std::vector &layer_height_profile) m_typed_slices = false; -#ifdef SLIC3R_PROFILE - // Disable parallelization so the Shiny profiler works - static tbb::task_scheduler_init *tbb_init = nullptr; - tbb_init = new tbb::task_scheduler_init(1); -#endif - // 1) Initialize layers and their slice heights. std::vector slice_zs; { diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 54cf55caba..f36e48aa66 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -4,6 +4,7 @@ #include "ClipperUtils.hpp" #include "Geometry.hpp" #include "MTUtils.hpp" +#include "Thread.hpp" #include #include @@ -689,7 +690,10 @@ bool SLAPrint::invalidate_step(SLAPrintStep step) void SLAPrint::process() { - if(m_objects.empty()) return; + if (m_objects.empty()) + return; + + name_tbb_thread_pool_threads(); // Assumption: at this point the print objects should be populated only with // the model objects we have to process and the instances are also filtered diff --git a/src/libslic3r/Thread.cpp b/src/libslic3r/Thread.cpp new file mode 100644 index 0000000000..878130df5c --- /dev/null +++ b/src/libslic3r/Thread.cpp @@ -0,0 +1,158 @@ +#ifdef _WIN32 + #include + #include + #include +#else + // any posix system + #include +#endif + +#include +#include +#include +#include + +#define SLIC3R_THREAD_NAME_WIN32_MODERN + +#include "Thread.hpp" + +#ifdef _WIN32 + +namespace Slic3r { + +#ifdef SLIC3R_THREAD_NAME_WIN32_MODERN + + static void WindowsSetThreadName(HANDLE hThread, const char *thread_name) + { + size_t len = strlen(thread_name); + if (len < 1024) { + // Allocate the temp string on stack. + wchar_t buf[1024]; + ::SetThreadDescription(hThread, boost::nowide::widen(buf, 1024, thread_name)); + } else { + // Allocate dynamically. + ::SetThreadDescription(hThread, boost::nowide::widen(thread_name).c_str()); + } + } + +#else // SLIC3R_THREAD_NAME_WIN32_MODERN + // use the old way by throwing an exception + + 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) + static void WindowsSetThreadName(HANDLE hThread, const char *thread_name) + { + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = threadName; + info.dwThreadID = ::GetThreadId(hThread); + info.dwFlags = 0; + + __try + { + RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + } + } + +#endif // SLIC3R_THREAD_NAME_WIN32_MODERN + +// posix +void set_thread_name(std::thread &thread, const char *thread_name) +{ + WindowsSetThreadName(static_cast(thread.native_handle()), thread_name); +} + +void set_thread_name(boost::thread &thread, const char *thread_name) +{ + WindowsSetThreadName(static_cast(thread.native_handle()), thread_name); +} + +void set_current_thread_name(const char *thread_name) +{ + WindowsSetThreadName(::GetCurrentThread(), thread_name); +} + +#else // _WIN32 + +// posix +void set_thread_name(std::thread &thread, const char *thread_name) +{ + pthread_setname_np(thread->native_handle(), thread_name); +} + +void set_thread_name(boost::thread &thread, const char *thread_name) +{ + pthread_setname_np(thread->native_handle(), thread_name); +} + +void set_current_thread_name(const char *thread_name) +{ + set_thread_name(pthread_self(), thread_name); +} + +#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; + + const size_t nthreads_hw = std::thread::hardware_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) { + static tbb::task_scheduler_init *tbb_init = nullptr; + tbb_init = new tbb::task_scheduler_init(nthreads); + } + + std::atomic 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(0, nthreads, 1), + [&nthreads_running, nthreads, &master_thread_id, &cv, &cv_m](const tbb::blocked_range &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 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_tbbpool_" << range.begin() << "_" << thread_id; + set_current_thread_name(name.str()); + } + }); +} + +} diff --git a/src/libslic3r/Thread.hpp b/src/libslic3r/Thread.hpp new file mode 100644 index 0000000000..f2cb5b1f0a --- /dev/null +++ b/src/libslic3r/Thread.hpp @@ -0,0 +1,40 @@ +#ifndef GUI_THREAD_HPP +#define GUI_THREAD_HPP + +#include +#include +#include + +namespace Slic3r { + +void set_thread_name(std::thread &thread, const char *thread_name); +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()); } + +// To be called somewhere before the TBB threads are spinned for the first time, to +// give them names recognizible in the debugger. +void name_tbb_thread_pool_threads(); + +template +inline boost::thread create_thread(boost::thread::attributes &attrs, Fn &&fn) +{ + // Duplicating the stack allocation size of Thread Building Block worker + // threads of the thread pool: allocate 4MB on a 64bit system, allocate 2MB + // on a 32bit system by default. + + attrs.set_stack_size((sizeof(void*) == 4) ? (2048 * 1024) : (4096 * 1024)); + return boost::thread{attrs, std::forward(fn)}; +} + +template inline boost::thread create_thread(Fn &&fn) +{ + boost::thread::attributes attrs; + return create_thread(attrs, std::forward(fn)); +} + +} + +#endif // GUI_THREAD_HPP diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 7e78f700d3..6f22771699 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -204,7 +204,6 @@ set(SLIC3R_GUI_SOURCES Utils/UndoRedo.hpp Utils/HexFile.cpp Utils/HexFile.hpp - Utils/Thread.hpp ) if (APPLE) diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index c6b5ff233a..d868362f37 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -23,6 +23,7 @@ #include "libslic3r/GCode/PreviewData.hpp" #endif // !ENABLE_GCODE_VIEWER #include "libslic3r/Format/SL1.hpp" +#include "libslic3r/Thread.hpp" #include "libslic3r/libslic3r.h" #include @@ -36,7 +37,6 @@ #include "I18N.hpp" #include "RemovableDriveManager.hpp" -#include "slic3r/Utils/Thread.hpp" #include "slic3r/GUI/Plater.hpp" namespace Slic3r { @@ -224,6 +224,9 @@ void BackgroundSlicingProcess::process_sla() void BackgroundSlicingProcess::thread_proc() { + set_current_thread_name("slic3r_BackgroundSlicingProcess"); + name_tbb_thread_pool_threads(); + assert(m_print != nullptr); assert(m_print == m_fff_print || m_print == m_sla_print); std::unique_lock lck(m_mutex); diff --git a/src/slic3r/GUI/Jobs/Job.cpp b/src/slic3r/GUI/Jobs/Job.cpp index cc2cb75f13..fedb6f4aca 100644 --- a/src/slic3r/GUI/Jobs/Job.cpp +++ b/src/slic3r/GUI/Jobs/Job.cpp @@ -1,6 +1,7 @@ #include #include "Job.hpp" +#include #include namespace Slic3r { diff --git a/src/slic3r/GUI/Jobs/Job.hpp b/src/slic3r/GUI/Jobs/Job.hpp index 130ca2ed9a..aea7692e7f 100644 --- a/src/slic3r/GUI/Jobs/Job.hpp +++ b/src/slic3r/GUI/Jobs/Job.hpp @@ -3,7 +3,8 @@ #include -#include +#include "libslic3r/libslic3r.h" + #include #include "ProgressIndicator.hpp" diff --git a/src/slic3r/Utils/Thread.hpp b/src/slic3r/Utils/Thread.hpp deleted file mode 100644 index 194971c9eb..0000000000 --- a/src/slic3r/Utils/Thread.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef GUI_THREAD_HPP -#define GUI_THREAD_HPP - -#include -#include - -namespace Slic3r { - -template -inline boost::thread create_thread(boost::thread::attributes &attrs, Fn &&fn) -{ - // Duplicating the stack allocation size of Thread Building Block worker - // threads of the thread pool: allocate 4MB on a 64bit system, allocate 2MB - // on a 32bit system by default. - - attrs.set_stack_size((sizeof(void*) == 4) ? (2048 * 1024) : (4096 * 1024)); - return boost::thread{attrs, std::forward(fn)}; -} - -template inline boost::thread create_thread(Fn &&fn) -{ - boost::thread::attributes attrs; - return create_thread(attrs, std::forward(fn)); -} - -} - -#endif // GUI_THREAD_HPP