Mantid
Loading...
Searching...
No Matches
ThreadScheduler.h
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2011 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
7#pragma once
8
9#include "MantidKernel/DllConfig.h"
11#include "MantidKernel/Task.h"
12#include <atomic>
13#include <deque>
14#include <map>
15#include <memory>
16#include <vector>
17
18namespace Mantid {
19
20namespace Kernel {
21
30//===========================================================================
31//===========================================================================
32//===========================================================================
33class MANTID_KERNEL_DLL ThreadScheduler {
34public:
37 ThreadScheduler() : m_cost(0), m_costExecuted(0), m_abortException(""), m_aborted(false) {}
38
40 virtual ~ThreadScheduler() = default;
41
42 //-----------------------------------------------------------------------------------
46 virtual void push(std::shared_ptr<Task> newTask) = 0;
47
48 //-----------------------------------------------------------------------------------
53 virtual std::shared_ptr<Task> pop(size_t threadnum) = 0;
54
55 //-----------------------------------------------------------------------------------
61 virtual void finished(Task *task, size_t threadnum) {
62 UNUSED_ARG(task);
63 UNUSED_ARG(threadnum);
64 }
65
66 //-----------------------------------------------------------------------------------
72 virtual void abort(std::runtime_error exception) {
73 // Save the exception for re-throwing
74 m_abortException = exception;
75 m_aborted = true;
76 // Clear (and delete) the queue
77 clear();
78 }
79
80 //-----------------------------------------------------------------------------------
82 virtual size_t size() = 0;
83
85 virtual bool empty() = 0;
86
88 virtual void clear() = 0;
89
90 //-------------------------------------------------------------------------------
92 double totalCost() { return m_cost; }
93
94 //-------------------------------------------------------------------------------
96 double totalCostExecuted() { return m_costExecuted; }
97
98 //-------------------------------------------------------------------------------
100 std::runtime_error getAbortException() { return m_abortException; }
101 //-------------------------------------------------------------------------------
103 bool getAborted() { return m_aborted; }
104
105 //-------------------------------------------------------------------------------
109 void markBusy() { ++m_numBusyThreads; }
110
112 void markIdle() { --m_numBusyThreads; }
113
117 bool hasBusyThreads() const { return m_numBusyThreads.load() > 0; }
118
119protected:
121 double m_cost;
125 std::mutex m_queueLock;
127 std::runtime_error m_abortException;
131 std::atomic<size_t> m_numBusyThreads{0};
132};
133
134//===========================================================================
135//===========================================================================
136//===========================================================================
143class MANTID_KERNEL_DLL ThreadSchedulerFIFO : public ThreadScheduler {
144public:
146
148 ~ThreadSchedulerFIFO() override { clear(); }
149
150 //-------------------------------------------------------------------------------
152 bool empty() override {
153 std::lock_guard<std::mutex> _lock(m_queueLock);
154 return m_queue.empty();
155 }
156
157 //-------------------------------------------------------------------------------
158 void push(std::shared_ptr<Task> newTask) override {
159 // Cache the total cost
160 m_queueLock.lock();
161 m_cost += newTask->cost();
162 m_queue.emplace_back(newTask);
163 m_queueLock.unlock();
164 }
165
166 //-------------------------------------------------------------------------------
167 std::shared_ptr<Task> pop(size_t threadnum) override {
168 UNUSED_ARG(threadnum);
169 std::shared_ptr<Task> temp = nullptr;
170 m_queueLock.lock();
171 // Check the size within the same locking block; otherwise the size may
172 // change before you get the next item.
173 if (!m_queue.empty()) {
174 // TODO: Would a try/catch block be smart here?
175 temp = m_queue.front();
176 m_queue.pop_front();
177 }
178 m_queueLock.unlock();
179 return temp;
180 }
181
182 //-------------------------------------------------------------------------------
183 size_t size() override {
184 m_queueLock.lock();
185 size_t temp = m_queue.size();
186 m_queueLock.unlock();
187 return temp;
188 }
189
190 //-------------------------------------------------------------------------------
191 void clear() override final {
192 m_queueLock.lock();
193 // Empty out the queue
194 m_queue.clear();
195 m_cost = 0;
196 m_costExecuted = 0;
197 m_queueLock.unlock();
198 }
199
200protected:
202 std::deque<std::shared_ptr<Task>> m_queue;
203};
204
205//===========================================================================
206//===========================================================================
207//===========================================================================
214class MANTID_KERNEL_DLL ThreadSchedulerLIFO : public ThreadSchedulerFIFO {
215
216 //-------------------------------------------------------------------------------
217 std::shared_ptr<Task> pop(size_t threadnum) override {
218 UNUSED_ARG(threadnum);
219 std::shared_ptr<Task> temp = nullptr;
220 m_queueLock.lock();
221 // Check the size within the same locking block; otherwise the size may
222 // change before you get the next item.
223 if (!m_queue.empty()) {
224 // TODO: Would a try/catch block be smart here?
225 temp = m_queue.back();
226 m_queue.pop_back();
227 }
228 m_queueLock.unlock();
229 return temp;
230 }
231};
232
233//===========================================================================
234//===========================================================================
235//===========================================================================
245class MANTID_KERNEL_DLL ThreadSchedulerLargestCost : public ThreadScheduler {
246public:
248
250 ~ThreadSchedulerLargestCost() override { clear(); }
251
252 //-------------------------------------------------------------------------------
254 bool empty() override {
255 std::lock_guard<std::mutex> _lock(m_queueLock);
256 return m_map.empty();
257 }
258
259 //-------------------------------------------------------------------------------
260 void push(std::shared_ptr<Task> newTask) override {
261 // Cache the total cost
262 m_queueLock.lock();
263 m_cost += newTask->cost();
264 m_map.emplace(newTask->cost(), newTask);
265 m_queueLock.unlock();
266 }
267
268 //-------------------------------------------------------------------------------
269 std::shared_ptr<Task> pop(size_t threadnum) override {
270 UNUSED_ARG(threadnum);
271 std::shared_ptr<Task> temp = nullptr;
272 m_queueLock.lock();
273 // Check the size within the same locking block; otherwise the size may
274 // change before you get the next item.
275 if (!m_map.empty()) {
276 // Since the map is sorted by cost, we want the LAST item.
277 auto it = m_map.end();
278 it--;
279 temp = it->second;
280 m_map.erase(it);
281 }
282 m_queueLock.unlock();
283 return temp;
284 }
285
286 //-------------------------------------------------------------------------------
287 size_t size() override {
288 m_queueLock.lock();
289 size_t temp = m_map.size();
290 m_queueLock.unlock();
291 return temp;
292 }
293
294 //-------------------------------------------------------------------------------
295 void clear() override final {
296 m_queueLock.lock();
297 // Empty out the queue and delete the pointers!
298 m_map.clear();
299 m_cost = 0;
300 m_costExecuted = 0;
301 m_queueLock.unlock();
302 }
303
304protected:
306 std::multimap<double, std::shared_ptr<Task>> m_map;
307};
308
309} // namespace Kernel
310} // namespace Mantid
#define UNUSED_ARG(x)
Function arguments are sometimes unused in certain implmentations but are required for documentation ...
Definition System.h:44
A Task is a unit of work to be scheduled and run by a ThreadPool.
Definition Task.h:29
A First-In-First-Out Thread Scheduler.
size_t size() override
Returns the size of the queue.
~ThreadSchedulerFIFO() override
Destructor.
void push(std::shared_ptr< Task > newTask) override
Add a Task to the queue.
void clear() override final
Empty out the queue.
std::shared_ptr< Task > pop(size_t threadnum) override
Retrieves the next Task to execute.
std::deque< std::shared_ptr< Task > > m_queue
Queue of tasks.
A Last-In-First-Out Thread Scheduler.
std::shared_ptr< Task > pop(size_t threadnum) override
Retrieves the next Task to execute.
A Largest Cost Thread Scheduler.
std::shared_ptr< Task > pop(size_t threadnum) override
Retrieves the next Task to execute.
void clear() override final
Empty out the queue.
size_t size() override
Returns the size of the queue.
std::multimap< double, std::shared_ptr< Task > > m_map
A multimap keeps tasks sorted by the key (cost)
void push(std::shared_ptr< Task > newTask) override
Add a Task to the queue.
The ThreadScheduler object defines how tasks are allocated to threads and in what order.
virtual void abort(std::runtime_error exception)
Signal to the scheduler that a task is complete.
bool getAborted()
Returns true if the execution was aborted.
bool m_aborted
The run was aborted due to an exception.
std::runtime_error m_abortException
The exception that aborted the run.
void markIdle()
Mark a thread as no longer busy. See markBusy().
virtual ~ThreadScheduler()=default
Destructor.
double m_costExecuted
Accumulated cost of tasks that have been executed (popped)
double totalCostExecuted()
Returns the total cost of all Task's in the queue.
std::mutex m_queueLock
Mutex to prevent simultaneous access to the queue.
virtual size_t size()=0
Returns the size of the queue.
virtual void finished(Task *task, size_t threadnum)
Signal to the scheduler that a task is complete.
void markBusy()
Mark a thread as busy.
virtual std::shared_ptr< Task > pop(size_t threadnum)=0
Retrieves the next Task to execute.
double totalCost()
Returns the total cost of all Task's in the queue.
std::runtime_error getAbortException()
Returns the exception that was caught, if any.
virtual void push(std::shared_ptr< Task > newTask)=0
Add a Task to the queue.
double m_cost
Total cost of all tasks.
bool hasBusyThreads() const
Returns true if any thread is busy.
virtual void clear()=0
Empty out the queue.
virtual bool empty()=0
Returns true if the queue is empty.
Helper class which provides the Collimation Length for SANS instruments.