libDwm-0.9.45
DwmThreadPool.hh
Go to the documentation of this file.
1//===========================================================================
2// @(#) $DwmPath$
3// @(#) $Id$
4//===========================================================================
5// Copyright (c) Daniel W. McRobb 2024
6// All rights reserved.
7//
8// Redistribution and use in source and binary forms, with or without
9// modification, are permitted provided that the following conditions
10// are met:
11//
12// 1. Redistributions of source code must retain the above copyright
13// notice, this list of conditions and the following disclaimer.
14// 2. Redistributions in binary form must reproduce the above copyright
15// notice, this list of conditions and the following disclaimer in the
16// documentation and/or other materials provided with the distribution.
17// 3. The names of the authors and copyright holders may not be used to
18// endorse or promote products derived from this software without
19// specific prior written permission.
20//
21// IN NO EVENT SHALL DANIEL W. MCROBB BE LIABLE TO ANY PARTY FOR
22// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
23// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE,
24// EVEN IF DANIEL W. MCROBB HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
25// DAMAGE.
26//
27// THE SOFTWARE PROVIDED HEREIN IS ON AN "AS IS" BASIS, AND
28// DANIEL W. MCROBB HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT,
29// UPDATES, ENHANCEMENTS, OR MODIFICATIONS. DANIEL W. MCROBB MAKES NO
30// REPRESENTATIONS AND EXTENDS NO WARRANTIES OF ANY KIND, EITHER
31// IMPLIED OR EXPRESS, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
32// WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE,
33// OR THAT THE USE OF THIS SOFTWARE WILL NOT INFRINGE ANY PATENT,
34// TRADEMARK OR OTHER RIGHTS.
35//===========================================================================
36
37//---------------------------------------------------------------------------
40//---------------------------------------------------------------------------
41
42#ifndef _DWMTHREADPOOL_HH_
43#define _DWMTHREADPOOL_HH_
44
45#include <concepts>
46#include <condition_variable>
47#include <deque>
48#include <mutex>
49#include <thread>
50#include <type_traits>
51#include <vector>
52
53namespace Dwm {
54
55 //--------------------------------------------------------------------------
60 //--------------------------------------------------------------------------
61 template <size_t N, typename F, typename... Args>
62 requires (std::invocable<F,Args...> && std::is_copy_assignable_v<F>
63 && (std::is_copy_assignable_v<Args> && ...))
65 {
66 public:
67 //------------------------------------------------------------------------
69 //------------------------------------------------------------------------
71 : _workers(), _tasks(), _mtx(), _cv(), _run(false)
72 {
73 Start();
74 }
75
76 //------------------------------------------------------------------------
80 //------------------------------------------------------------------------
81 bool Start()
82 {
83 bool rc = false;
84 std::lock_guard<std::mutex> lock(_mtx);
85 if (_workers.empty()) {
86 _run = true;
87 for (size_t i = 0; i < N; ++i) {
88 _workers.emplace_back(std::thread(&ThreadPool::WorkerThread, this));
89 }
90 rc = true;
91 }
92 return rc;
93 }
94
95 //------------------------------------------------------------------------
98 //------------------------------------------------------------------------
99 void AddTask(F fn, Args ...args)
100 {
101 {
102 std::lock_guard<std::mutex> lock(_mtx);
103 _tasks.emplace_back(Task(fn,std::make_tuple(args...)));
104 }
105 _cv.notify_one();
106 return;
107 }
108
109 //------------------------------------------------------------------------
112 //------------------------------------------------------------------------
113 void Stop()
114 {
115 {
116 std::lock_guard<std::mutex> lock(_mtx);
117 _run = false;
118 }
119 _cv.notify_all();
120 for (std::thread & worker : _workers) {
121 worker.join();
122 }
123 {
124 std::lock_guard<std::mutex> lock(_mtx);
125 _workers.clear();
126 }
127 return;
128 }
129
130 //------------------------------------------------------------------------
133 //------------------------------------------------------------------------
135 {
136 {
137 std::lock_guard<std::mutex> lock(_mtx);
138 _tasks.clear();
139 }
140 return;
141 }
142
143 //------------------------------------------------------------------------
145 //------------------------------------------------------------------------
147 {
148 if (_run) {
149 Stop();
150 }
151 }
152
153 private:
154 //------------------------------------------------------------------------
157 //------------------------------------------------------------------------
158 class Task
159 {
160 public:
161 Task() = default;
162 Task(const Task & t) = default;
163 Task(Task && t) = default;
164 Task(F f, std::tuple<Args...> && a) : _fn(f), _args(a) { }
165 Task & operator = (const Task &) = default;
166 Task & operator = (Task &&) = default;
167
168 //----------------------------------------------------------------------
171 //----------------------------------------------------------------------
172 inline void Execute()
173 {
174 return CallFunc(std::index_sequence_for<Args...>());
175 }
176
177 private:
178 F _fn;
179 std::tuple<Args...> _args;
180
181 //----------------------------------------------------------------------
183 //----------------------------------------------------------------------
184 template<size_t ...S>
185 inline void CallFunc(std::index_sequence<S...>)
186 {
187 _fn(std::get<S>(_args) ...);
188 return;
189 }
190 };
191
192 std::vector<std::thread> _workers;
193 std::deque<Task> _tasks;
194 std::mutex _mtx;
195 std::condition_variable _cv;
196 bool _run;
197
198 //------------------------------------------------------------------------
200 //------------------------------------------------------------------------
201 void WorkerThread()
202 {
203 // Just a lambda we use when waiting on the condition variable.
204 // We want to wait for a request to shut down or work to do.
205 auto waitFor =
206 [this] { return ((! this->_run) || (! this->_tasks.empty())); };
207
208 for (;;) {
209 Task task;
210 {
211 std::unique_lock<std::mutex> lock(this->_mtx);
212 this->_cv.wait(lock, waitFor);
213 if ((! this->_run) && this->_tasks.empty()) {
214 // We've been asked to stop and there are no more tasks.
215 return;
216 }
217 // Get the task at the front of the queue.
218 task = this->_tasks.front();
219 this->_tasks.pop_front();
220 }
221 // Execute the task.
222 task.Execute();
223 }
224 }
225
226 };
227
228} // namespace Dwm
229
230#endif // _DWMTHREADPOOL_HH_
Encapsulate a trivial thread pool.
Definition DwmThreadPool.hh:65
void Stop()
Stops the threads.
Definition DwmThreadPool.hh:113
bool Start()
Starts the threads.
Definition DwmThreadPool.hh:81
ThreadPool()
Constructs the thread pool and starts the threads.
Definition DwmThreadPool.hh:70
~ThreadPool()
Destructor.
Definition DwmThreadPool.hh:146
void ClearTasks()
Clears all the tasks from the task queue.
Definition DwmThreadPool.hh:134
void AddTask(F fn, Args ...args)
Adds a task to be executed by the thread pool.
Definition DwmThreadPool.hh:99