2424#ifndef UNIFIEDCACHE_INFRA_THREAD_POOL_H
2525#define UNIFIEDCACHE_INFRA_THREAD_POOL_H
2626
27+ #include < atomic>
2728#include < condition_variable>
2829#include < functional>
2930#include < future>
3031#include < list>
32+ #include < memory>
3133#include < mutex>
34+ #include < sys/syscall.h>
3235#include < thread>
36+ #include < unistd.h>
37+ #include < vector>
3338
3439namespace UC {
3540
3641template <class Task , class WorkerArgs = void *>
3742class ThreadPool {
3843 using WorkerInitFn = std::function<bool (WorkerArgs&)>;
3944 using WorkerFn = std::function<void (Task&, const WorkerArgs&)>;
45+ using WorkerTimeoutFn = std::function<void (Task&, const ssize_t )>;
4046 using WorkerExitFn = std::function<void (WorkerArgs&)>;
4147
48+ class StopToken {
49+ std::shared_ptr<std::atomic<bool >> flag_ = std::make_shared<std::atomic<bool >>(false );
50+
51+ public:
52+ void RequestStop () noexcept { this ->flag_ ->store (true , std::memory_order_relaxed); }
53+ bool StopRequested () const noexcept { return this ->flag_ ->load (std::memory_order_relaxed); }
54+ };
55+
56+ struct Worker {
57+ ssize_t tid;
58+ std::thread th;
59+ StopToken stop;
60+ std::weak_ptr<Task> current;
61+ std::atomic<std::chrono::steady_clock::time_point> tp{};
62+ };
63+
4264public:
4365 ThreadPool () = default ;
4466 ThreadPool (const ThreadPool&) = delete ;
4567 ThreadPool& operator =(const ThreadPool&) = delete ;
4668 ~ThreadPool ()
4769 {
4870 {
49- std::unique_lock <std::mutex> lk (this ->mtx_ );
71+ std::lock_guard <std::mutex> lock (this ->taskMtx_ );
5072 this ->stop_ = true ;
5173 this ->cv_ .notify_all ();
5274 }
53- for (auto & w : this ->workers_ ) {
54- if (w.joinable ()) { w.join (); }
75+ if (this ->monitor_ .joinable ()) { this ->monitor_ .join (); }
76+ for (auto & worker : this ->workers_ ) {
77+ if (worker->th .joinable ()) { worker->th .join (); }
5578 }
5679 }
5780 ThreadPool& SetWorkerFn (WorkerFn&& fn)
@@ -69,6 +92,14 @@ class ThreadPool {
6992 this ->exitFn_ = std::move (fn);
7093 return *this ;
7194 }
95+ ThreadPool& SetWorkerTimeoutFn (WorkerTimeoutFn&& fn, const size_t timeoutMs,
96+ const size_t intervalMs = 1000 )
97+ {
98+ this ->timeoutFn_ = std::move (fn);
99+ this ->timeoutMs_ = timeoutMs;
100+ this ->intervalMs_ = intervalMs;
101+ return *this ;
102+ }
72103 ThreadPool& SetNWorker (const size_t nWorker)
73104 {
74105 this ->nWorker_ = nWorker;
@@ -77,61 +108,123 @@ class ThreadPool {
77108 bool Run ()
78109 {
79110 if (this ->nWorker_ == 0 ) { return false ; }
80- if (!this ->fn_ ) { return false ; }
81- std::list<std::promise<bool >> start (this ->nWorker_ );
82- std::list<std::future<bool >> fut;
83- for (auto & s : start) {
84- fut.push_back (s.get_future ());
85- this ->workers_ .emplace_back ([&] { this ->Worker (s); });
111+ if (this ->fn_ == nullptr ) { return false ; }
112+ this ->workers_ .reserve (this ->nWorker_ );
113+ for (size_t i = 0 ; i < this ->nWorker_ ; i++) {
114+ if (!this ->AddOneWorker ()) { return false ; }
86115 }
87- auto success = true ;
88- for (auto & f : fut) {
89- if (!f.get ()) { success = false ; }
116+ if (this ->timeoutMs_ > 0 ) {
117+ this ->monitor_ = std::thread ([this ] { this ->MonitorLoop (); });
90118 }
91- return success ;
119+ return true ;
92120 }
93121 void Push (std::list<Task>& tasks) noexcept
94122 {
95- std::unique_lock<std::mutex> lk (this ->mtx_ );
123+ std::unique_lock<std::mutex> lock (this ->taskMtx_ );
96124 this ->taskQ_ .splice (this ->taskQ_ .end (), tasks);
97125 this ->cv_ .notify_all ();
98126 }
99127 void Push (Task&& task) noexcept
100128 {
101- std::unique_lock<std::mutex> lk (this ->mtx_ );
129+ std::unique_lock<std::mutex> lock (this ->taskMtx_ );
102130 this ->taskQ_ .push_back (std::move (task));
103131 this ->cv_ .notify_one ();
104132 }
105133
106134private:
107- void Worker (std::promise<bool >& started) noexcept
135+ bool AddOneWorker ()
136+ {
137+ try {
138+ auto worker = std::make_shared<Worker>();
139+ std::promise<bool > prom;
140+ auto fut = prom.get_future ();
141+ worker->th = std::thread ([this , worker, &prom] { this ->WorkerLoop (prom, worker); });
142+ auto success = fut.get ();
143+ if (!success) { return false ; }
144+ this ->workers_ .push_back (worker);
145+ return true ;
146+ } catch (...) {
147+ return false ;
148+ }
149+ }
150+ void WorkerLoop (std::promise<bool >& prom, std::shared_ptr<Worker> worker)
108151 {
152+ worker->tid = syscall (SYS_gettid);
109153 WorkerArgs args = nullptr ;
110154 auto success = true ;
111155 if (this ->initFn_ ) { success = this ->initFn_ (args); }
112- started .set_value (success);
156+ prom .set_value (success);
113157 while (success) {
114- std::unique_lock<std::mutex> lk (this ->mtx_ );
115- this ->cv_ .wait (lk, [this ] { return this ->stop_ || !this ->taskQ_ .empty (); });
116- if (this ->stop_ ) { break ; }
117- if (this ->taskQ_ .empty ()) { continue ; }
118- auto task = std::make_shared<Task>(std::move (this ->taskQ_ .front ()));
119- this ->taskQ_ .pop_front ();
120- lk.unlock ();
158+ std::shared_ptr<Task> task = nullptr ;
159+ {
160+ std::unique_lock<std::mutex> lock (this ->taskMtx_ );
161+ this ->cv_ .wait (lock, [this , worker] {
162+ return this ->stop_ || worker->stop .StopRequested () || !this ->taskQ_ .empty ();
163+ });
164+ if (this ->stop_ || worker->stop .StopRequested ()) { break ; }
165+ if (this ->taskQ_ .empty ()) { continue ; }
166+ task = std::make_shared<Task>(std::move (this ->taskQ_ .front ()));
167+ this ->taskQ_ .pop_front ();
168+ }
169+ worker->current = task;
170+ worker->tp .store (std::chrono::steady_clock::now (), std::memory_order_relaxed);
121171 this ->fn_ (*task, args);
172+ if (worker->stop .StopRequested ()) { break ; }
173+ worker->current .reset ();
174+ worker->tp .store ({}, std::memory_order_relaxed);
122175 }
123176 if (this ->exitFn_ ) { this ->exitFn_ (args); }
124177 }
125178
179+ void MonitorLoop ()
180+ {
181+ const auto interval = std::chrono::milliseconds (this ->intervalMs_ );
182+ while (true ) {
183+ {
184+ std::unique_lock<std::mutex> lock (this ->taskMtx_ );
185+ this ->cv_ .wait_for (lock, interval, [this ] { return this ->stop_ ; });
186+ if (this ->stop_ ) { break ; }
187+ }
188+ size_t nWorker = this ->Monitor ();
189+ for (size_t i = nWorker; i < this ->nWorker_ ; i++) { (void )this ->AddOneWorker (); }
190+ }
191+ }
192+
193+ size_t Monitor ()
194+ {
195+ using namespace std ::chrono;
196+ const auto timeout = milliseconds (this ->timeoutMs_ );
197+ size_t nWorker = 0 ;
198+ for (auto it = this ->workers_ .begin (); it != this ->workers_ .end ();) {
199+ auto tp = (*it)->tp .load (std::memory_order_relaxed);
200+ auto task = (*it)->current .lock ();
201+ auto now = steady_clock::now ();
202+ if (task && tp != steady_clock::time_point{} && now - tp > timeout) {
203+ if (this ->timeoutFn_ ) { this ->timeoutFn_ (*task, (*it)->tid ); }
204+ (*it)->stop .RequestStop ();
205+ if ((*it)->th .joinable ()) { (*it)->th .detach (); }
206+ it = this ->workers_ .erase (it);
207+ } else {
208+ it++;
209+ }
210+ nWorker++;
211+ }
212+ return nWorker;
213+ }
214+
126215private:
216+ WorkerInitFn initFn_{nullptr };
217+ WorkerFn fn_{nullptr };
218+ WorkerTimeoutFn timeoutFn_{nullptr };
219+ WorkerExitFn exitFn_{nullptr };
220+ size_t timeoutMs_{0 };
221+ size_t intervalMs_{0 };
222+ size_t nWorker_{0 };
127223 bool stop_{false };
128- size_t nWorker_{1 };
129- std::list<std::thread> workers_;
130- WorkerInitFn initFn_;
131- WorkerFn fn_;
132- WorkerExitFn exitFn_;
224+ std::vector<std::shared_ptr<Worker>> workers_;
225+ std::thread monitor_;
226+ std::mutex taskMtx_;
133227 std::list<Task> taskQ_;
134- std::mutex mtx_;
135228 std::condition_variable cv_;
136229};
137230
0 commit comments