Developer documentation
Version 3.0.3-105-gd3941f44
thread.h
Go to the documentation of this file.
1/* Copyright (c) 2008-2022 the MRtrix3 contributors.
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 *
7 * Covered Software is provided under this License on an "as is"
8 * basis, without warranty of any kind, either expressed, implied, or
9 * statutory, including, without limitation, warranties that the
10 * Covered Software is free of defects, merchantable, fit for a
11 * particular purpose or non-infringing.
12 * See the Mozilla Public License v. 2.0 for more details.
13 *
14 * For more details, see http://www.mrtrix.org/.
15 */
16
17#ifndef __mrtrix_thread_h__
18#define __mrtrix_thread_h__
19
20#include <thread>
21#include <future>
22#include <mutex>
23
24#include "debug.h"
25#include "mrtrix.h"
26#include "exception.h"
27
56namespace MR
57{
58 namespace Thread
59 {
60
62 public:
65
66 static void register_thread () {
67 std::lock_guard<std::mutex> lock (mutex);
68 if (!backend)
69 backend = new __Backend;
71 }
72 static void unregister_thread () {
73 assert (backend);
74 std::lock_guard<std::mutex> lock (mutex);
75 if (!(--backend->refcount)) {
76 delete backend;
77 backend = nullptr;
78 }
79 }
80
81 static bool valid() { return backend; }
82
83 static void thread_print_func (const std::string& msg);
84 static void thread_report_to_user_func (const std::string& msg, int type);
85
86 static void (*previous_print_func) (const std::string& msg);
87 static void (*previous_report_to_user_func) (const std::string& msg, int type);
88
89 protected:
90 size_t refcount;
91
93 static std::mutex mutex;
94 };
95
96
97 namespace {
98
99 class __thread_base { NOMEMALIGN
100 public:
101 __thread_base (const std::string& name = "unnamed") : name (name) { __Backend::register_thread(); }
102 __thread_base (const __thread_base&) = delete;
103 __thread_base (__thread_base&&) = default;
104 ~__thread_base () { __Backend::unregister_thread(); }
105
106
107 protected:
108 const std::string name;
109 };
110
111
112 class __single_thread : public __thread_base { NOMEMALIGN
113 public:
114 template <class Functor>
115 __single_thread (Functor&& functor, const std::string& name = "unnamed") :
116 __thread_base (name) {
117 DEBUG ("launching thread \"" + name + "\"...");
118 using F = typename std::remove_reference<Functor>::type;
119 thread = std::async (std::launch::async, &F::execute, &functor);
120 }
121 __single_thread (const __single_thread&) = delete;
122 __single_thread (__single_thread&&) = default;
123
124 bool finished () const
125 {
126 return thread.wait_for(std::chrono::microseconds(0)) == std::future_status::ready;
127 }
128
129 void wait () noexcept (false) {
130 DEBUG ("waiting for completion of thread \"" + name + "\"...");
131 thread.get();
132 DEBUG ("thread \"" + name + "\" completed OK");
133 }
134
135 ~__single_thread () {
136 if (thread.valid()) {
137 try { wait(); }
138 catch (Exception& E) { E.display(); }
139 }
140 }
141
142 protected:
143 std::future<void> thread;
144 };
145
146
147 template <class Functor>
148 class __multi_thread : public __thread_base { NOMEMALIGN
149 public:
150 __multi_thread (Functor& functor, size_t nthreads, const std::string& name = "unnamed") :
151 __thread_base (name), functors ( (nthreads>0 ? nthreads-1 : 0), functor) {
152 DEBUG ("launching " + str (nthreads) + " threads \"" + name + "\"...");
153 using F = typename std::remove_reference<Functor>::type;
154 threads.reserve (nthreads);
155 for (auto& f : functors)
156 threads.push_back (std::async (std::launch::async, &F::execute, &f));
157 threads.push_back (std::async (std::launch::async, &F::execute, &functor));
158 }
159
160 __multi_thread (const __multi_thread&) = delete;
161 __multi_thread (__multi_thread&&) = default;
162
163 void wait () noexcept (false) {
164 DEBUG ("waiting for completion of threads \"" + name + "\"...");
165 bool exception_thrown = false;
166 for (auto& t : threads) {
167 if (!t.valid())
168 continue;
169 try { t.get(); }
170 catch (Exception& E) {
171 exception_thrown = true;
172 E.display();
173 }
174 }
175 if (exception_thrown)
176 throw Exception ("exception thrown from one or more threads \"" + name + "\"");
177 DEBUG ("threads \"" + name + "\" completed OK");
178 }
179
180 bool finished () const {
181 for (auto& t : threads)
182 if (t.wait_for (std::chrono::microseconds(0)) != std::future_status::ready)
183 return false;
184 return true;
185 }
186
187 bool any_valid () const {
188 for (auto& t : threads)
189 if (t.valid())
190 return true;
191 return false;
192 }
193
194 ~__multi_thread () {
195 if (any_valid()) {
196 try { wait(); }
197 catch (Exception& E) { E.display(); }
198 }
199 }
200 protected:
201 vector<std::future<void>> threads;
202 vector<typename std::remove_reference<Functor>::type> functors;
203
204 };
205
206
207 template <class Functor>
208 class __Multi { NOMEMALIGN
209 public:
210 __Multi (Functor& object, size_t number) : functor (object), num (number) { }
211 __Multi (__Multi&& m) = default;
212 template <class X> bool operator() (const X&) { assert (0); return false; }
213 template <class X> bool operator() (X&) { assert (0); return false; }
214 template <class X, class Y> bool operator() (const X&, Y&) { assert (0); return false; }
215 typename std::remove_reference<Functor>::type& functor;
216 size_t num;
217 };
218
219 template <class Functor>
220 class __run { NOMEMALIGN
221 public:
222 using type = __single_thread;
223 type operator() (Functor& functor, const std::string& name) {
224 return { functor, name };
225 }
226 };
227
228 template <class Functor>
229 class __run<__Multi<Functor>> { NOMEMALIGN
230 public:
231 using type = __multi_thread<Functor>;
232 type operator() (__Multi<Functor>& functor, const std::string& name) {
233 return { functor.functor, functor.num, name };
234 }
235 };
236
237 }
238
239
240
259
266
273
274
275
277
283 template <class Functor>
284 inline __Multi<typename std::remove_reference<Functor>::type>
285 multi (Functor&& functor, size_t nthreads = threads_to_execute())
286 {
287 return { functor, nthreads };
288 }
289
290
291
293
372 template <class Functor>
373 inline typename __run<Functor>::type run (Functor&& functor, const std::string& name = "unnamed")
374 {
375 return __run<typename std::remove_reference<Functor>::type>() (functor, name);
376 }
377
380 }
381}
382
383#endif
384
385
static void register_thread()
Definition: thread.h:66
static void(* previous_report_to_user_func)(const std::string &msg, int type)
Definition: thread.h:87
static bool valid()
Definition: thread.h:81
static std::mutex mutex
Definition: thread.h:93
static void thread_report_to_user_func(const std::string &msg, int type)
static void(* previous_print_func)(const std::string &msg)
Definition: thread.h:86
static void unregister_thread()
Definition: thread.h:72
static void thread_print_func(const std::string &msg)
static __Backend * backend
Definition: thread.h:92
#define DEBUG(msg)
Definition: exception.h:75
__run< Functor >::type run(Functor &&functor, const std::string &name="unnamed")
Execute the functor's execute method in a separate thread.
Definition: thread.h:373
size_t threads_to_execute()
nthreads_t type_nthreads()
size_t number_of_threads()
__Multi< typename std::remove_reference< Functor >::type > multi(Functor &&functor, size_t nthreads=threads_to_execute())
used to request multiple threads of the corresponding functor
Definition: thread.h:285
#define NOMEMALIGN
Definition: memory.h:22
Definition: base.h:24
std::string str(const T &value, int precision=0)
Definition: mrtrix.h:247
size_t num
Definition: thread.h:216
std::future< void > thread
Definition: thread.h:143
vector< typename std::remove_reference< Functor >::type > functors
Definition: thread.h:202
const std::string name
Definition: thread.h:108
vector< std::future< void > > threads
Definition: thread.h:201
std::remove_reference< Functor >::type & functor
Definition: thread.h:215