1414#include < future>
1515#include < vector>
1616#include < algorithm>
17+ #include < cassert>
1718#include " SharedUtil.Misc.h"
1819
1920namespace SharedUtil
@@ -39,8 +40,17 @@ namespace SharedUtil
3940 task = std::move (m_tasks.front ());
4041 m_tasks.pop ();
4142 }
42- // Run the task
43- task (false );
43+ // Run the task (catch exceptions to prevent thread death)
44+ try
45+ {
46+ task (false );
47+ }
48+ catch (...)
49+ {
50+ // Exception is automatically captured by std::packaged_task
51+ // and will be re-thrown when future.get() is called.
52+ // We must catch here to prevent the worker thread from terminating.
53+ }
4454 }
4555 });
4656 }
@@ -49,22 +59,33 @@ namespace SharedUtil
4959 template <typename Func, typename ... Args>
5060 auto enqueue (Func&& f, Args&&... args)
5161 {
52- using ReturnT = std::invoke_result_t <Func, Args...>;
53- auto ff = std::bind (std::forward<Func>(f), std::forward<Args>(args)...);
54- auto * task = new std::packaged_task<ReturnT ()>(ff);
5562
56- // Package the task in a wrapper with a common void result
57- // plus a skip flag for destruction without running the task
58- std::packaged_task<void (bool )> resultTask ([task](bool skip) {
59- if (!skip)
60- (*task)();
61- delete task;
62- });
63+ #if __cplusplus < 201703L // C++17
64+ using ReturnT = typename std::result_of<Func (Args...)>::type;
65+ #else
66+ using ReturnT = std::invoke_result_t <Func, Args...>;
67+ #endif
68+
69+ auto ff = std::bind (std::forward<Func>(f), std::forward<Args>(args)...);
70+ auto task = std::make_shared<std::packaged_task<ReturnT ()>>(ff);
71+
72+ // Package the task in a wrapper with a common void result
73+ // plus a skip flag for destruction without running the task
74+ std::packaged_task<void (bool )> resultTask ([task](bool skip) {
75+ if (!skip)
76+ (*task)();
77+ // task automatically deleted when shared_ptr goes out of scope
78+ });
6379
6480 // Add task to queue and return future
6581 std::future<ReturnT> res = task->get_future ();
6682 {
6783 std::unique_lock<std::mutex> lock (m_mutex);
84+ if (m_exit)
85+ {
86+ // Pool is shutting down - reject new tasks
87+ throw std::runtime_error (" Cannot enqueue task: thread pool is shutting down" );
88+ }
6889 m_tasks.emplace (std::move (resultTask));
6990 }
7091 m_cv.notify_one ();
@@ -73,19 +94,36 @@ namespace SharedUtil
7394
7495 void shutdown ()
7596 {
76- if (m_exit)
77- return ;
78-
79- // Ensure every thread receives the exit state, and discard all remaining tasks.
8097 {
8198 std::unique_lock<std::mutex> lock (m_mutex);
99+
100+ // Already shutting down or shut down
101+ if (m_exit)
102+ return ;
103+
82104 m_exit = true ;
83105
106+ // Discard all remaining tasks
84107 while (!m_tasks.empty ())
85108 {
86- // Run each task but skip execution of the actual function (-> just delete the task)
109+ // Run each task with skip flag to clean up without executing
87110 auto task = std::move (m_tasks.front ());
88- task (true );
111+ m_tasks.pop (); // Important: Remove from queue to avoid infinite loop
112+
113+ // Execute cleanup outside the critical section to reduce lock contention
114+ lock.unlock ();
115+ try
116+ {
117+ task (true ); // Cleanup the shared_ptr
118+ }
119+ catch (...)
120+ {
121+ // Exceptions during cleanup indicate a serious bug (e.g., corrupted lambda)
122+ // We cannot propagate this exception as we're mid-shutdown with the lock released.
123+ // In debug builds, this should be logged/asserted.
124+ dassert (false && " Exception during thread pool task cleanup" );
125+ }
126+ lock.lock ();
89127 }
90128 }
91129
@@ -94,12 +132,24 @@ namespace SharedUtil
94132
95133 // Wait for threads to end
96134 for (std::thread& worker : m_vecThreads)
97- worker.join ();
135+ {
136+ if (worker.joinable ())
137+ worker.join ();
138+ }
98139 }
99140
100- ~CThreadPool ()
141+ ~CThreadPool () noexcept
101142 {
102- shutdown ();
143+ try
144+ {
145+ shutdown ();
146+ }
147+ catch (...)
148+ {
149+ // Must suppress exceptions to prevent std::terminate().
150+ // This should only happen if mutex operations fail (system error).
151+ dassert (false && " Exception during thread pool destruction" );
152+ }
103153 }
104154
105155 static CThreadPool& getDefaultThreadPool ()
0 commit comments