Thread.hpp 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. ///|/ Copyright (c) Prusa Research 2020 - 2023 Vojtěch Bubník @bubnikv
  2. ///|/
  3. ///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
  4. ///|/
  5. #ifndef GUI_THREAD_HPP
  6. #define GUI_THREAD_HPP
  7. #include <utility>
  8. #include <string>
  9. #include <thread>
  10. #include <random>
  11. #include <boost/thread.hpp>
  12. #include <tbb/task_scheduler_observer.h>
  13. #include <tbb/enumerable_thread_specific.h>
  14. namespace Slic3r {
  15. // Set / get thread name.
  16. // Returns false if the API is not supported.
  17. //
  18. // It is a good idea to name the main thread before spawning children threads, because dynamic linking is used on Windows 10
  19. // to initialize Get/SetThreadDescription functions, which is not thread safe.
  20. //
  21. // pthread_setname_np supports maximum 15 character thread names! (16th character is the null terminator)
  22. //
  23. // Methods taking the thread as an argument are not supported by OSX.
  24. // Naming threads is only supported on newer Windows 10.
  25. bool set_thread_name(std::thread &thread, const char *thread_name);
  26. inline bool set_thread_name(std::thread &thread, const std::string &thread_name) { return set_thread_name(thread, thread_name.c_str()); }
  27. bool set_thread_name(boost::thread &thread, const char *thread_name);
  28. inline bool set_thread_name(boost::thread &thread, const std::string &thread_name) { return set_thread_name(thread, thread_name.c_str()); }
  29. bool set_current_thread_name(const char *thread_name);
  30. inline bool set_current_thread_name(const std::string &thread_name) { return set_current_thread_name(thread_name.c_str()); }
  31. // To be called at the start of the application to save the current thread ID as the main (UI) thread ID.
  32. void save_main_thread_id();
  33. // Retrieve the cached main (UI) thread ID.
  34. boost::thread::id get_main_thread_id();
  35. // Checks whether the main (UI) thread is active.
  36. bool is_main_thread_active();
  37. // OSX specific: Set Quality of Service to "user initiated", so that the threads will be scheduled to high performance
  38. // cores if available.
  39. void set_current_thread_qos();
  40. // Returns nullopt if not supported.
  41. // Not supported by OSX.
  42. // Naming threads is only supported on newer Windows 10.
  43. std::optional<std::string> get_current_thread_name();
  44. // To be called somewhere before the TBB threads are spinned for the first time, to
  45. // give them names recognizible in the debugger.
  46. // Also it sets locale of the worker threads to "C" for the G-code generator to produce "." as a decimal separator.
  47. void name_tbb_thread_pool_threads_set_locale();
  48. template<class Fn>
  49. inline boost::thread create_thread(boost::thread::attributes &attrs, Fn &&fn)
  50. {
  51. // Duplicating the stack allocation size of Thread Building Block worker
  52. // threads of the thread pool: allocate 4MB on a 64bit system, allocate 2MB
  53. // on a 32bit system by default.
  54. attrs.set_stack_size((sizeof(void*) == 4) ? (2048 * 1024) : (4096 * 1024));
  55. return boost::thread{attrs, std::forward<Fn>(fn)};
  56. }
  57. template<class Fn> inline boost::thread create_thread(Fn &&fn)
  58. {
  59. boost::thread::attributes attrs;
  60. return create_thread(attrs, std::forward<Fn>(fn));
  61. }
  62. #ifdef _DEBUGINFO
  63. // TODO: sort items idx by difficulty, so we can process the most difficult first.
  64. // for now, only used to swi
  65. void parallel_for(size_t begin, size_t size, std::function<void(size_t)> process_one_item);
  66. void not_parallel_for(size_t begin, size_t size, std::function<void(size_t)> process_one_item);
  67. #else
  68. using tbb::parallel_for;
  69. #endif
  70. class ThreadData {
  71. public:
  72. std::mt19937& random_generator() {
  73. if (! m_random_generator_initialized) {
  74. std::random_device rd;
  75. m_random_generator.seed(rd());
  76. m_random_generator_initialized = true;
  77. }
  78. return m_random_generator;
  79. }
  80. void tbb_worker_thread_set_c_locales();
  81. private:
  82. std::mt19937 m_random_generator;
  83. bool m_random_generator_initialized { false };
  84. bool m_tbb_worker_thread_c_locales_set { false };
  85. };
  86. ThreadData& thread_data();
  87. // For unknown reasons and in sporadic cases when GCode export is processing, some participating thread
  88. // in tbb::parallel_pipeline has not set locales to "C", probably because this thread is newly spawned.
  89. // So in this class method on_scheduler_entry is called for every thread before it starts participating
  90. // in tbb::parallel_pipeline to ensure that locales are set correctly
  91. //
  92. // For tbb::parallel_pipeline, it seems that on_scheduler_entry is called for every layer and every filter.
  93. // We ensure using thread-local storage that locales will be set to "C" just once for any participating thread.
  94. class TBBLocalesSetter : public tbb::task_scheduler_observer
  95. {
  96. public:
  97. TBBLocalesSetter() { this->observe(true); }
  98. ~TBBLocalesSetter() override { this->observe(false); };
  99. void on_scheduler_entry(bool /* is_worker */) override { thread_data().tbb_worker_thread_set_c_locales(); }
  100. };
  101. }
  102. #endif // GUI_THREAD_HPP