Mantid
Loading...
Searching...
No Matches
ThreadSchedulerMutexes.h
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 +
7#pragma once
8
9#include "MantidKernel/DllConfig.h"
11
12#include <algorithm>
13#include <memory>
14#include <mutex>
15#include <numeric>
16#include <set>
17
18namespace Mantid {
19namespace Kernel {
20
38public:
40
42
43 //-------------------------------------------------------------------------------
44 void push(std::shared_ptr<Task> newTask) override {
45 // Cache the total cost
46 std::lock_guard<std::mutex> lock(m_queueLock);
47 m_cost += newTask->cost();
48
49 std::shared_ptr<std::mutex> mut = newTask->getMutex();
50 m_supermap[mut].emplace(newTask->cost(), newTask);
51 }
52
53 //-------------------------------------------------------------------------------
54 std::shared_ptr<Task> pop(size_t threadnum) override {
55 UNUSED_ARG(threadnum);
56
57 std::shared_ptr<Task> temp = nullptr;
58
59 std::lock_guard<std::mutex> lock(m_queueLock);
60 // Check the size within the same locking block; otherwise the size may
61 // change before you get the next item.
62 if (!m_supermap.empty()) {
63 // We iterate in reverse as to take the NULL mutex last, even if no mutex
64 // is busy
65 for (auto &mutexedMap : m_supermap) {
66 // The key is the mutex associated with the inner map
67 std::shared_ptr<std::mutex> mapMutex = mutexedMap.first;
68 if ((!mapMutex) || (m_mutexes.empty()) || (m_mutexes.find(mapMutex) == m_mutexes.end())) {
69 // The mutex of this map is free!
70 InnerMap &map = mutexedMap.second;
71
72 if (!map.empty()) {
73 // Look for the largest cost item in it.
74 auto it2 = map.end();
75 --it2;
76 // Great, we found something.
77 temp = std::move(it2->second);
78 // Take it out of the map (popped)
79 map.erase(it2);
80 break;
81 }
82 }
83 }
84 if (temp == nullptr) {
85 // Nothing was found, meaning all mutexes are in use
86 // Try the first non-empty map
87 for (auto &mutexedMap : m_supermap) {
88 if (!mutexedMap.second.empty()) {
89 InnerMap &map = mutexedMap.second;
90 // Use the first one
91 temp = std::move(map.begin()->second);
92 // And erase that item (pop it)
93 map.erase(map.begin());
94 break;
95 }
96 }
97 }
98 // If temp is still NULL, then no tasks are left.
99 }
100
101 // --- Add the mutex (if any) to the list of "busy" ones ---
102 if (temp) {
103 std::shared_ptr<std::mutex> mut = temp->getMutex();
104 if (mut)
105 m_mutexes.insert(std::move(mut));
106 }
107
108 return temp;
109 }
110
111 //-----------------------------------------------------------------------------------
117 void finished(Task *task, size_t threadnum) override {
118 UNUSED_ARG(threadnum);
119 std::shared_ptr<std::mutex> mut = task->getMutex();
120 if (mut) {
121 std::lock_guard<std::mutex> lock(m_queueLock);
122 // We take this mutex off the list of used ones.
123 m_mutexes.erase(mut);
124 }
125 }
126
127 //-------------------------------------------------------------------------------
128 size_t size() override {
129 std::lock_guard<std::mutex> lock(m_queueLock);
130 // Add up the sizes of all contained maps.
131 return std::accumulate(
132 m_supermap.cbegin(), m_supermap.cend(), size_t{0},
133 [](size_t total, const std::pair<const std::shared_ptr<std::mutex> &, const InnerMap &> &mutexedMap) {
134 return total + mutexedMap.second.size();
135 });
136 }
137
138 //-------------------------------------------------------------------------------
140 bool empty() override {
141 std::lock_guard<std::mutex> lock(m_queueLock);
142 auto mapWithTasks =
143 std::find_if_not(m_supermap.cbegin(), m_supermap.cend(),
144 [](const std::pair<const std::shared_ptr<std::mutex>, const InnerMap &> &mutexedMap) {
145 return mutexedMap.second.empty();
146 });
147 return mapWithTasks == m_supermap.cend();
148 }
149
150 //-------------------------------------------------------------------------------
151 void clear() override final {
152 std::lock_guard<std::mutex> lock(m_queueLock);
153
154 // Empty out the queue and delete the pointers!
155 for (auto &it : m_supermap) {
156 InnerMap &map = it.second;
157 map.clear();
158 }
159 m_supermap.clear();
160 m_cost = 0;
161 m_costExecuted = 0;
162 }
163
164protected:
166 using InnerMap = std::multimap<double, std::shared_ptr<Task>>;
168 using SuperMap = std::map<std::shared_ptr<std::mutex>, InnerMap>;
169
173
175 std::set<std::shared_ptr<std::mutex>> m_mutexes;
176};
177
178} // namespace Kernel
179} // 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
std::shared_ptr< std::mutex > getMutex()
Get the mutex object for this Task.
Definition Task.h:72
ThreadSchedulerMutexes : Version of a ThreadSchedulerLargestCost that also makes sure to not try to s...
SuperMap m_supermap
A super map; first key = a Mutex * Inside it: second key = the cost.
std::set< std::shared_ptr< std::mutex > > m_mutexes
Vector of currently used mutexes.
std::map< std::shared_ptr< std::mutex >, InnerMap > SuperMap
Map to maps, sorted by Mutex*.
void clear() override final
Empty out the queue.
std::shared_ptr< Task > pop(size_t threadnum) override
Retrieves the next Task to execute.
void finished(Task *task, size_t threadnum) override
Signal to the scheduler that a task is complete.
std::multimap< double, std::shared_ptr< Task > > InnerMap
Map to tasks, sorted by cost.
void push(std::shared_ptr< Task > newTask) override
Add a Task to the queue.
size_t size() override
Returns the size of the queue.
The ThreadScheduler object defines how tasks are allocated to threads and in what order.
double m_costExecuted
Accumulated cost of tasks that have been executed (popped)
std::mutex m_queueLock
Mutex to prevent simultaneous access to the queue.
double m_cost
Total cost of all tasks.
Helper class which provides the Collimation Length for SANS instruments.