libDwm-0.9.45
DwmLoadBalancer.hh
Go to the documentation of this file.
1//===========================================================================
2// @(#) $DwmPath$
3//===========================================================================
4// Copyright (c) Daniel W. McRobb 2008, 2009, 2016, 2018
5// All rights reserved.
6//
7// Redistribution and use in source and binary forms, with or without
8// modification, are permitted provided that the following conditions
9// are met:
10//
11// 1. Redistributions of source code must retain the above copyright
12// notice, this list of conditions and the following disclaimer.
13// 2. Redistributions in binary form must reproduce the above copyright
14// notice, this list of conditions and the following disclaimer in the
15// documentation and/or other materials provided with the distribution.
16// 3. The names of the authors and copyright holders may not be used to
17// endorse or promote products derived from this software without
18// specific prior written permission.
19//
20// IN NO EVENT SHALL DANIEL W. MCROBB BE LIABLE TO ANY PARTY FOR
21// DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
22// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE,
23// EVEN IF DANIEL W. MCROBB HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
24// DAMAGE.
25//
26// THE SOFTWARE PROVIDED HEREIN IS ON AN "AS IS" BASIS, AND
27// DANIEL W. MCROBB HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT,
28// UPDATES, ENHANCEMENTS, OR MODIFICATIONS. DANIEL W. MCROBB MAKES NO
29// REPRESENTATIONS AND EXTENDS NO WARRANTIES OF ANY KIND, EITHER
30// IMPLIED OR EXPRESS, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
31// WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE,
32// OR THAT THE USE OF THIS SOFTWARE WILL NOT INFRINGE ANY PATENT,
33// TRADEMARK OR OTHER RIGHTS.
34//===========================================================================
35
36//---------------------------------------------------------------------------
39//---------------------------------------------------------------------------
40
41#ifndef _DWMLOADBALANCER_HH_
42#define _DWMLOADBALANCER_HH_
43
44#include <algorithm>
45#include <atomic>
46#include <deque>
47#include <chrono>
48#include <thread>
49#include <vector>
50
51#include "DwmThreadQueue.hh"
52
53namespace Dwm {
54
55 //--------------------------------------------------------------------------
72 //--------------------------------------------------------------------------
73 template <typename ItemT>
75 {
76 public:
77 //------------------------------------------------------------------------
79 //------------------------------------------------------------------------
80 class Worker
81 {
82 public:
83 //----------------------------------------------------------------------
87 //----------------------------------------------------------------------
88 Worker(std::chrono::microseconds waitNotEmptyMicroseconds = std::chrono::microseconds(1000))
89 : _workQueue(), _keepRunning(false), _thread(),
90 _waitUsecs(waitNotEmptyMicroseconds)
91 {}
92
93 //----------------------------------------------------------------------
95 //----------------------------------------------------------------------
96 virtual ~Worker()
97 {
98 Stop();
99 }
100
101 //----------------------------------------------------------------------
104 //----------------------------------------------------------------------
105 bool AddWork(const ItemT & item)
106 {
107 return _workQueue.PushBack(item);
108 }
109
110 //----------------------------------------------------------------------
114 //----------------------------------------------------------------------
115 template <typename InputIterator>
116 bool AddWork(InputIterator firstIter, InputIterator lastIter)
117 {
118 return (_workQueue.PushBack(firstIter, lastIter) > 0);
119 }
120
121 //----------------------------------------------------------------------
123 //----------------------------------------------------------------------
124 uint32_t QueueLength() const
125 {
126 return _workQueue.Length();
127 }
128
129 //----------------------------------------------------------------------
132 //----------------------------------------------------------------------
133 bool ReadyForWork() const
134 {
135 return (_keepRunning
136 && ((! _workQueue.MaxLength())
137 || (_workQueue.Length() < _workQueue.MaxLength())));
138 }
139
140 //----------------------------------------------------------------------
143 //----------------------------------------------------------------------
144 bool ReadyForWork(size_t numEntries) const
145 {
146 return (_keepRunning
147 && ((! _workQueue.MaxLength())
148 || ((_workQueue.Length() + numEntries)
149 <= _workQueue.MaxLength())));
150 }
151
152 //----------------------------------------------------------------------
154 //----------------------------------------------------------------------
155 void MaxWork(uint32_t maxItems)
156 {
157 _workQueue.MaxLength(maxItems);
158 }
159
160 //----------------------------------------------------------------------
162 //----------------------------------------------------------------------
163 bool Start()
164 {
165 bool rc = false;
166 _keepRunning = true;
167 _thread = std::thread(&Worker::Run, this);
168 rc = true;
169 return rc;
170 }
171
172 //----------------------------------------------------------------------
174 //----------------------------------------------------------------------
175 void Stop()
176 {
177 _keepRunning = false;
178 if (_thread.joinable()) {
179 _thread.join();
180 }
181 return;
182 }
183
184 //----------------------------------------------------------------------
186 //----------------------------------------------------------------------
188 {
189 return (_thread.joinable());
190 }
191
192 //----------------------------------------------------------------------
194 //----------------------------------------------------------------------
195 void Run()
196 {
197 while (_keepRunning || (_workQueue.Length())) {
198 if (_workQueue.TimedWaitForNotEmpty(_waitUsecs.load())) {
199 std::deque<ItemT> myCopy;
200 _workQueue.Swap(myCopy);
201 // try processing in bulk
202 if (! ProcessWork(myCopy)) {
203 // else fall back to one at a time.
204 for (auto i : myCopy) {
205 ProcessWork(i);
206 }
207 }
208 }
209 }
210 return;
211 }
212
213 //----------------------------------------------------------------------
216 //----------------------------------------------------------------------
217 virtual void ProcessWork(ItemT & item) = 0;
218
219 //----------------------------------------------------------------------
223 //----------------------------------------------------------------------
224 virtual bool ProcessWork(std::deque<ItemT> & items)
225 {
226 return false;
227 }
228
229 //----------------------------------------------------------------------
233 //----------------------------------------------------------------------
234 std::chrono::microseconds WaitUsecs() const
235 {
236 return _waitUsecs;
237 }
238
239 protected:
240 Thread::Queue<ItemT> _workQueue;
241 std::atomic<bool> _keepRunning;
242 std::thread _thread;
243 std::atomic<std::chrono::microseconds> _waitUsecs;
244 };
245
246 //------------------------------------------------------------------------
250 //------------------------------------------------------------------------
251 void AddWorker(Worker *worker)
252 {
253 std::lock_guard<std::mutex> lock(_mtx);
254 _workers.push_back(std::unique_ptr<Worker>(worker));
255 return;
256 }
257
258 //------------------------------------------------------------------------
261 //------------------------------------------------------------------------
262 bool AddWork(ItemT item)
263 {
264 bool workAdded = false;
265 {
266 std::lock_guard<std::mutex> lock(_mtx);
267 if (_workers.empty()) {
268 // No workers to perform work!!!
269 return false;
270 }
271 }
272 while (! workAdded) {
273 {
274 // in separate scope so we don't hold lock for too long
275 std::lock_guard<std::mutex> lock(_mtx);
276 auto w = std::min_element(_workers.begin(), _workers.end(),
277 [&] (const std::unique_ptr<Worker> & a,
278 const std::unique_ptr<Worker> & b)
279 { return (a->QueueLength()
280 < b->QueueLength()); });
281 if (w != _workers.end()) {
282 workAdded = (*w)->AddWork(item);
283 }
284 }
285 if (! workAdded) {
286 std::this_thread::sleep_for(MinimumWaitForWorkerReady());
287 }
288 }
289 return workAdded;
290 }
291
292 //------------------------------------------------------------------------
294 //------------------------------------------------------------------------
295 template <typename InputIterator>
296 bool AddWork(InputIterator firstIter, InputIterator lastIter)
297 {
298 bool workAdded = false;
299 {
300 std::lock_guard<std::mutex> lock(_mtx);
301 if (_workers.empty()) {
302 // No workers to perform work!!!
303 return false;
304 }
305 }
306 while (! WorkerReady(lastIter - firstIter)) {
307 std::this_thread::sleep_for(std::chrono::microseconds(MinimumWaitForWorkerReady()));
308 }
309
310 std::lock_guard<std::mutex> lock(_mtx);
311 auto w = std::min_element(_workers.begin(), _workers.end(),
312 [&] (const std::unique_ptr<Worker> & a,
313 const std::unique_ptr<Worker> & b)
314 { return (a->QueueLength() < b->QueueLength()); });
315 return (*w)->AddWork(firstIter, lastIter);
316 }
317
318 //------------------------------------------------------------------------
320 //------------------------------------------------------------------------
321 void Stop()
322 {
323 std::lock_guard<std::mutex> lock(_mtx);
324 for (auto & w : _workers) {
325 w->Stop();
326 }
327 return;
328 }
329
330 //------------------------------------------------------------------------
333 //------------------------------------------------------------------------
334 const std::vector<std::unique_ptr<Worker>> & Workers() const
335 {
336 return _workers;
337 }
338
339 private:
340 mutable std::mutex _mtx;
341 std::vector<std::unique_ptr<Worker>> _workers;
342
343 //------------------------------------------------------------------------
345 //------------------------------------------------------------------------
346 bool WorkerReady()
347 {
348 bool rc = false;
349 std::lock_guard<std::mutex> lock(_mtx);
350 for (auto & w : _workers) {
351 if (w->ReadyForWork()) {
352 rc = true;
353 break;
354 }
355 }
356 return rc;
357 }
358
359 //------------------------------------------------------------------------
362 //------------------------------------------------------------------------
363 bool WorkerReady(size_t numEntries) const
364 {
365 bool rc = false;
366 std::lock_guard<std::mutex> lock(_mtx);
367 for (auto & w : _workers) {
368 if (w->ReadyForWork(numEntries)) {
369 rc = true;
370 break;
371 }
372 }
373 return rc;
374 }
375
376 //------------------------------------------------------------------------
378 //------------------------------------------------------------------------
379 std::chrono::microseconds MinimumWaitForWorkerReady() const
380 {
381 std::chrono::microseconds rc(1000);
382 std::lock_guard<std::mutex> lock(_mtx);
383 auto w = std::min_element(_workers.begin(), _workers.end(),
384 [&] (const std::unique_ptr<Worker> & a,
385 const std::unique_ptr<Worker> & b)
386 { return (a->WaitUsecs() < b->WaitUsecs())\
387; });
388 if (w != _workers.end()) {
389 rc = std::chrono::microseconds((*w)->WaitUsecs());
390 }
391 return rc;
392 }
393
394 };
395
396} // namespace Dwm
397
398
399#endif // _DWMLOADBALANCER_HH_
400
401
402//---------------------------- emacs settings -----------------------------
403// Local Variables:
404// mode: C++
405// tab-width: 2
406// indent-tabs-mode: nil
407// c-basic-offset: 2
408// End:
409//-------------------------------------------------------------------------
Dwm::Thread::Queue class template definition.
Worker class for LoadBalancer.
Definition DwmLoadBalancer.hh:81
bool AddWork(InputIterator firstIter, InputIterator lastIter)
Adds work items for the worker.
Definition DwmLoadBalancer.hh:116
virtual bool ProcessWork(std::deque< ItemT > &items)
Process a deque of work items.
Definition DwmLoadBalancer.hh:224
void Run()
Runs the worker thread.
Definition DwmLoadBalancer.hh:195
virtual void ProcessWork(ItemT &item)=0
Pure virtual member to process a single work item.
void MaxWork(uint32_t maxItems)
Sets the maximum length of the worker's work queue.
Definition DwmLoadBalancer.hh:155
void Stop()
Stops the worker.
Definition DwmLoadBalancer.hh:175
bool AddWork(const ItemT &item)
Adds a work item for the worker.
Definition DwmLoadBalancer.hh:105
virtual ~Worker()
Destructor. Stops the worker thread.
Definition DwmLoadBalancer.hh:96
Worker(std::chrono::microseconds waitNotEmptyMicroseconds=std::chrono::microseconds(1000))
Constructs the worker.
Definition DwmLoadBalancer.hh:88
bool ReadyForWork(size_t numEntries) const
Returns true if the worker is ready for numEntries units of work.
Definition DwmLoadBalancer.hh:144
uint32_t QueueLength() const
Returns the current length of the worker's work queue.
Definition DwmLoadBalancer.hh:124
std::chrono::microseconds WaitUsecs() const
Returns the microseconds we'll wait for queue to be non-empty in our worker thread.
Definition DwmLoadBalancer.hh:234
bool IsRunning()
Returns true if the worker's thread is running.
Definition DwmLoadBalancer.hh:187
bool ReadyForWork() const
Returns true if the worker is ready for more work (has room in its work queue and is running).
Definition DwmLoadBalancer.hh:133
bool Start()
Starts the worker.
Definition DwmLoadBalancer.hh:163
A simple load balancer class template which balances work across Worker objects that each run in thei...
Definition DwmLoadBalancer.hh:75
bool AddWork(ItemT item)
Adds work to be done with load balancing.
Definition DwmLoadBalancer.hh:262
bool AddWork(InputIterator firstIter, InputIterator lastIter)
Adds work to be done with load balancing.
Definition DwmLoadBalancer.hh:296
void AddWorker(Worker *worker)
Adds the given worker to the load balancer.
Definition DwmLoadBalancer.hh:251
const std::vector< std::unique_ptr< Worker > > & Workers() const
Returns a const reference to the encapsulated workers.
Definition DwmLoadBalancer.hh:334
void Stop()
Calls Worker::Stop() on all encapsulated Worker objects.
Definition DwmLoadBalancer.hh:321
This template provides inter-thread first-in first-out (FIFO) queueing.
Definition DwmThreadQueue.hh:68
std::deque< _ValueType >::size_type Length() const
Returns the current length of the queue.
Definition DwmThreadQueue.hh:111
uint32_t Swap(std::deque< _ValueType > &c)
This member is a simple optimization for fetching the contents of the queue.
Definition DwmThreadQueue.hh:386
uint32_t MaxLength() const
Returns the max length of the queue.
Definition DwmThreadQueue.hh:93
bool PushBack(const _ValueType &value)
Inserts value on the back of the queue.
Definition DwmThreadQueue.hh:121
bool TimedWaitForNotEmpty(const std::chrono::duration< Rep, Period > &timeToWait)
Waits timeToWait for the queue to be non-empty.
Definition DwmThreadQueue.hh:321