Mantid
Loading...
Searching...
No Matches
ThreadPoolRunnable.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 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 +
9#include "MantidKernel/Task.h"
11
12#include <Poco/Thread.h>
13
14namespace Mantid::Kernel {
15
16namespace {
18class BusyGuard {
19public:
20 explicit BusyGuard(ThreadScheduler &scheduler) : m_scheduler(&scheduler) { m_scheduler->markBusy(); }
21 BusyGuard(const BusyGuard &) = delete;
22 BusyGuard &operator=(const BusyGuard &) = delete;
23 ~BusyGuard() { release(); }
25 void release() {
26 if (m_scheduler) {
27 m_scheduler->markIdle();
28 m_scheduler = nullptr;
29 }
30 }
31
32private:
33 ThreadScheduler *m_scheduler;
34};
35} // namespace
36
37//-----------------------------------------------------------------------------------
48ThreadPoolRunnable::ThreadPoolRunnable(size_t threadnum, ThreadScheduler *scheduler, ProgressBase *prog, double waitSec)
49 : m_threadnum(threadnum), m_scheduler(scheduler), m_prog(prog), m_waitSec(waitSec) {
50 if (!m_scheduler)
51 throw std::invalid_argument("NULL ThreadScheduler passed to ThreadPoolRunnable::ctor()");
52}
53
54//-----------------------------------------------------------------------------------
57
58//-----------------------------------------------------------------------------------
63 std::shared_ptr<Task> task;
64
65 // If there are no tasks yet, wait up to m_waitSec for them to come up
66 while (m_scheduler->empty() && m_waitSec > 0.0) {
67 Poco::Thread::sleep(10); // millisec
68 m_waitSec -= 0.01; // Subtract ten millisec from the time left to wait.
69 }
70
71 // Keep going while there are tasks in the queue, or while another thread is
72 // running a task that may still add more.
74 // Mark this thread busy before asking for a task so another thread never
75 // sees an empty queue and no busy threads while this one holds the last task.
76 BusyGuard busy(*m_scheduler);
77
78 // Request the task from the scheduler.
79 // Will be NULL if not found.
81
82 if (task) {
83 // Task-specific mutex if specified?
84 std::shared_ptr<std::mutex> mutex = task->getMutex();
85 if (bool(mutex))
86 mutex->lock();
87
88 try {
89 // Run the task (synchronously within this thread)
90 task->run();
91 } catch (std::exception &e) {
92 // The task threw an exception!
93 // This will clear out the list of tasks, allowing all threads to
94 // finish.
95 m_scheduler->abort(std::runtime_error(e.what()));
96 }
97
98 // Tell the scheduler that we finished this task
99 m_scheduler->finished(task.get(), m_threadnum);
100
101 // Report progress, if specified.
102 if (m_prog)
103 m_prog->report();
104
105 // Unlock the mutex, if any.
106 if (mutex)
107 mutex->unlock();
108
109 // We now delete the task to free up memory
110 task.reset();
111 } else {
112 // No appropriate task for this thread (perhaps a mutex is locked, or
113 // another thread's task has not added its tasks yet).
114 // So we wait a bit before checking again.
115 busy.release();
116 Poco::Thread::sleep(10); // millisec
117 }
118 }
119 // Ran out of tasks that could be run.
120 // Thread now will exit
121}
122
123} // namespace Mantid::Kernel
ThreadScheduler * m_scheduler
void report()
Increments the loop counter by 1, then sends the progress notification on behalf of its algorithm.
double m_waitSec
How many seconds you are allowed to wait with no tasks before exiting.
void run() override
Thread method.
void clearWait()
Clear the wait time of the runnable so that it stops waiting for tasks.
size_t m_threadnum
ID of this thread.
ProgressBase * m_prog
Progress reporter.
ThreadScheduler * m_scheduler
The ThreadScheduler instance taking care of task scheduling.
ThreadPoolRunnable(size_t threadnum, ThreadScheduler *scheduler, ProgressBase *prog=nullptr, double waitSec=0.0)
Constructor.
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.
virtual void finished(Task *task, size_t threadnum)
Signal to the scheduler that a task is complete.
virtual std::shared_ptr< Task > pop(size_t threadnum)=0
Retrieves the next Task to execute.
bool hasBusyThreads() const
Returns true if any thread is busy.
virtual bool empty()=0
Returns true if the queue is empty.