Reference documentation for deal.II version 9.5.0
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
thread_management.h
Go to the documentation of this file.
1// ---------------------------------------------------------------------
2//
3// Copyright (C) 2000 - 2023 by the deal.II authors
4//
5// This file is part of the deal.II library.
6//
7// The deal.II library is free software; you can use it, redistribute
8// it, and/or modify it under the terms of the GNU Lesser General
9// Public License as published by the Free Software Foundation; either
10// version 2.1 of the License, or (at your option) any later version.
11// The full text of the license can be found in the file LICENSE.md at
12// the top level directory of deal.II.
13//
14// ---------------------------------------------------------------------
15
16#ifndef dealii_thread_management_h
17#define dealii_thread_management_h
18
19
20#include <deal.II/base/config.h>
21
24#include <deal.II/base/mutex.h>
27
28#include <atomic>
29#include <functional>
30#include <future>
31#include <list>
32#include <memory>
33#include <thread>
34#include <utility>
35#include <vector>
36
37#ifdef DEAL_II_HAVE_CXX20
38# include <concepts>
39#endif
40
41
42#ifdef DEAL_II_WITH_TBB
43# include <tbb/task_group.h>
44#endif
45
47
60namespace Threads
61{
76 template <typename ForwardIterator>
77 std::vector<std::pair<ForwardIterator, ForwardIterator>>
78 split_range(const ForwardIterator &begin,
79 const ForwardIterator &end,
80 const unsigned int n_intervals);
81
90 std::vector<std::pair<unsigned int, unsigned int>>
91 split_interval(const unsigned int begin,
92 const unsigned int end,
93 const unsigned int n_intervals);
94
104 namespace internal
105 {
121 [[noreturn]] void
122 handle_std_exception(const std::exception &exc);
123
131 [[noreturn]] void
133 } // namespace internal
134
139} // namespace Threads
140
141/* ----------- implementation of functions in namespace Threads ---------- */
142#ifndef DOXYGEN
143namespace Threads
144{
145 template <typename ForwardIterator>
146 std::vector<std::pair<ForwardIterator, ForwardIterator>>
147 split_range(const ForwardIterator &begin,
148 const ForwardIterator &end,
149 const unsigned int n_intervals)
150 {
151 using IteratorPair = std::pair<ForwardIterator, ForwardIterator>;
152
153 // in non-multithreaded mode, we often have the case that this
154 // function is called with n_intervals==1, so have a shortcut here
155 // to handle that case efficiently
156
157 if (n_intervals == 1)
158 return (std::vector<IteratorPair>(1, IteratorPair(begin, end)));
159
160 // if more than one interval requested, do the full work
161 const unsigned int n_elements = std::distance(begin, end);
162 const unsigned int n_elements_per_interval = n_elements / n_intervals;
163 const unsigned int residual = n_elements % n_intervals;
164
165 std::vector<IteratorPair> return_values(n_intervals);
166
167 return_values[0].first = begin;
168 for (unsigned int i = 0; i < n_intervals; ++i)
169 {
170 if (i != n_intervals - 1)
171 {
172 return_values[i].second = return_values[i].first;
173 // note: the cast is performed to avoid a warning of gcc
174 // that in the library `dist>=0' is checked (dist has a
175 // template type, which here is unsigned if no cast is
176 // performed)
177 std::advance(return_values[i].second,
178 static_cast<signed int>(n_elements_per_interval));
179 // distribute residual in division equally among the first
180 // few subintervals
181 if (i < residual)
182 ++return_values[i].second;
183
184 return_values[i + 1].first = return_values[i].second;
185 }
186 else
187 return_values[i].second = end;
188 }
189 return return_values;
190 }
191} // namespace Threads
192
193#endif // DOXYGEN
194
195namespace Threads
196{
197 namespace internal
198 {
217 template <typename RT>
219 {
220 private:
222
223 public:
224 using reference_type = RT &;
225
227 : value()
228 {}
229
230 inline reference_type
232 {
233 return value;
234 }
235
236 inline void
237 set(RT &&v)
238 {
239 value = std::move(v);
240 }
241
247 inline void
248 set_from(std::future<RT> &v)
249 {
250 value = std::move(v.get());
251 }
252 };
253
254
274 template <typename RT>
275 struct return_value<RT &>
276 {
277 private:
278 RT *value;
279
280 public:
281 using reference_type = RT &;
282
284 : value(nullptr)
285 {}
286
287 inline reference_type
288 get() const
289 {
290 return *value;
291 }
292
293 inline void
294 set(RT &v)
295 {
296 value = &v;
297 }
298
304 inline void
305 set_from(std::future<RT &> &v)
306 {
307 value = &v.get();
308 }
309 };
310
311
330 template <>
331 struct return_value<void>
332 {
333 using reference_type = void;
334
335 static inline void
337 {}
338
339
346 inline void
347 set_from(std::future<void> &)
348 {}
349 };
350 } // namespace internal
351
352
353
354 namespace internal
355 {
356 template <typename RT>
357 inline void
358 call(const std::function<RT()> & function,
360 {
361 ret_val.set(function());
362 }
363
364
365 inline void
366 call(const std::function<void()> &function, internal::return_value<void> &)
367 {
368 function();
369 }
370 } // namespace internal
371
372
373
374 namespace internal
375 {
386 template <typename RT>
388 {
392 std::thread thread;
393
402 std::shared_ptr<return_value<RT>> ret_val;
403
439 std::atomic<bool> thread_is_active;
440
445
450 : thread_is_active(false)
451 {}
452
454 {
455 if (!thread_is_active)
456 return;
457 thread.detach();
458 thread_is_active = false;
459 }
460
465 void
466 start(const std::function<RT()> &function)
467 {
468 thread_is_active = true;
469 ret_val = std::make_shared<return_value<RT>>();
470 thread = std::thread(thread_entry_point, function, ret_val);
471 }
472
473
477 void
479 {
480 // see if the thread hasn't been joined yet. if it has, then
481 // join() is a no-op. use schmidt's double-checking strategy
482 // to use the mutex only when necessary
483 if (thread_is_active == false)
484 return;
485
486 std::lock_guard<std::mutex> lock(thread_is_active_mutex);
487 if (thread_is_active == true)
488 {
489 Assert(thread.joinable(), ExcInternalError());
490 thread.join();
491 thread_is_active = false;
492 }
493 }
494
495 private:
499 static void
500 thread_entry_point(const std::function<RT()> & function,
501 std::shared_ptr<return_value<RT>> ret_val)
502 {
503 // call the function in question. since an exception that is
504 // thrown from one of the called functions will not propagate
505 // to the main thread, it will kill the program if not treated
506 // here before we return to the operating system's thread
507 // library
508 try
509 {
510 call(function, *ret_val);
511 }
512 catch (const std::exception &exc)
513 {
515 }
516 catch (...)
517 {
519 }
520 }
521 };
522 } // namespace internal
523
524
551 template <typename RT = void>
552 class Thread
553 {
554 public:
559 Thread(const std::function<RT()> &function)
560 : thread_descriptor(new internal::ThreadDescriptor<RT>())
561 {
562 // in a second step, start the thread.
563 thread_descriptor->start(function);
564 }
565
572 Thread() = default;
573
580 {}
581
587 void
588 join() const
589 {
591 thread_descriptor->join();
592 }
593
639 {
640 join();
641 return thread_descriptor->ret_val->get();
642 }
643
648 bool
649 valid() const
650 {
651 return static_cast<bool>(thread_descriptor);
652 }
653
654
660 bool
661 operator==(const Thread &t) const
662 {
664 }
665
666 private:
672 std::shared_ptr<internal::ThreadDescriptor<RT>> thread_descriptor;
673 };
674
675
676 namespace internal
677 {
685 template <typename T>
687 {
688 static T
689 act(T &t)
690 {
691 return t;
692 }
693 };
694
695
696
704 template <typename T>
705 struct maybe_make_ref<T &>
706 {
707 static std::reference_wrapper<T>
708 act(T &t)
709 {
710 return std::ref(t);
711 }
712 };
713 } // namespace internal
714
715
716
717 // ----------- thread starters for functions not taking any parameters
718
729 template <typename RT>
730 DEAL_II_DEPRECATED inline Thread<RT>
731 new_thread(const std::function<RT()> &function)
732 {
733 // Here and below we need to disable deprecation warnings for calling the
734 // constructor in this function - as this function itself is deprecated
735 // these warnings are not helpful. This problem only appears in some
736 // configurations (e.g., Debian 11 with GCC-10).
738 return Thread<RT>(function);
740 }
741
742
743
812 template <typename FunctionObjectType>
813 DEAL_II_CXX20_REQUIRES((std::invocable<FunctionObjectType>))
814 DEAL_II_DEPRECATED inline auto new_thread(FunctionObjectType function_object)
815 -> Thread<decltype(function_object())>
816 {
817 // See the comment in the first new_thread() implementation
819 using return_type = decltype(function_object());
820 return Thread<return_type>(std::function<return_type()>(function_object));
822 }
823
824
825
834 template <typename RT, typename... Args>
835 DEAL_II_DEPRECATED inline Thread<RT>
836 new_thread(RT (*fun_ptr)(Args...), std_cxx20::type_identity_t<Args>... args)
837 {
838 // See the comment in the first new_thread() implementation
840 auto dummy = std::make_tuple(internal::maybe_make_ref<Args>::act(args)...);
841 return new_thread(
842 [dummy, fun_ptr]() -> RT { return std_cxx17::apply(fun_ptr, dummy); });
844 }
845
846
847
855 template <typename RT, typename C, typename... Args>
856 DEAL_II_DEPRECATED inline Thread<RT>
857 new_thread(RT (C::*fun_ptr)(Args...),
858 std_cxx20::type_identity_t<C> &c,
859 std_cxx20::type_identity_t<Args>... args)
860 {
861 // NOLINTNEXTLINE(modernize-avoid-bind) silence clang-tidy
862 return new_thread(std::function<RT()>(std::bind(
863 fun_ptr, std::ref(c), internal::maybe_make_ref<Args>::act(args)...)));
864 }
865
873 template <typename RT, typename C, typename... Args>
874 DEAL_II_DEPRECATED inline Thread<RT>
875 new_thread(RT (C::*fun_ptr)(Args...) const,
876 std_cxx20::type_identity_t<const C> &c,
877 std_cxx20::type_identity_t<Args>... args)
878 {
879 // See the comment in the first new_thread() implementation
881 // NOLINTNEXTLINE(modernize-avoid-bind) silence clang-tidy
882 return new_thread(std::function<RT()>(std::bind(
883 fun_ptr, std::cref(c), internal::maybe_make_ref<Args>::act(args)...)));
885 }
886
887 // ------------------------ ThreadGroup -------------------------------------
888
898 template <typename RT = void>
900 {
901 public:
907 {
908 threads.push_back(t);
909 return *this;
910 }
911
918 void
919 join_all() const
920 {
921 for (auto &t : threads)
922 t.join();
923 }
924
925 private:
929 std::list<Thread<RT>> threads;
930 };
931
932
933 namespace internal
934 {
941 template <typename RT, typename Function>
943 (std::invocable<Function> &&
944 std::convertible_to<std::invoke_result_t<Function>, RT>))
945 void evaluate_and_set_promise(Function &function, std::promise<RT> &promise)
946 {
947 promise.set_value(function());
948 }
949
950
960 template <typename Function>
961 DEAL_II_CXX20_REQUIRES((std::invocable<Function>))
963 std::promise<void> &promise)
964 {
965 function();
966 promise.set_value();
967 }
968 } // namespace internal
969
970
971
998 template <typename RT = void>
999 class Task
1000 {
1001 public:
1013 Task(const std::function<RT()> &function_object)
1014 {
1016 {
1017#ifdef DEAL_II_WITH_TBB
1018 // Create a promise object and from it extract a future that
1019 // we can use to refer to the outcome of the task. For reasons
1020 // explained below, we can't just create a std::promise object,
1021 // but have to make do with a pointer to such an object.
1022 std::unique_ptr<std::promise<RT>> promise =
1023 std::make_unique<std::promise<RT>>();
1024 task_data =
1025 std::make_shared<TaskData>(std::move(promise->get_future()));
1026
1027 // Then start the task, using a task_group object (for just this one
1028 // task) that is associated with the TaskData object. Note that we
1029 // have to *copy* the function object being executed so that it is
1030 // guaranteed to live on the called thread as well -- the copying is
1031 // facilitated by capturing the 'function_object' variable by value.
1032 //
1033 // We also have to *move* the promise object into the new task's
1034 // memory space because promises can not be copied and we can't refer
1035 // to it by reference because it's a local variable of the current
1036 // (surrounding) function that may go out of scope before the promise
1037 // is ultimately set. This leads to a conundrum: if we had just
1038 // declared 'promise' as an object of type std::promise, then we could
1039 // capture it in the lambda function via
1040 // [..., promise=std::move(promise)]() {...}
1041 // and set the promise in the body of the lambda. But setting a
1042 // promise is a non-const operation on the promise, and so we would
1043 // actually have to declare the lambda function as 'mutable' because
1044 // by default, lambda captures are 'const'. That is, we would have
1045 // to write
1046 // [..., promise=std::move(promise)]() mutable {...}
1047 // But this leads to other problems: It turns out that the
1048 // tbb::task_group::run() function cannot take mutable lambdas as
1049 // argument :-(
1050 //
1051 // We work around this issue by not declaring the 'promise' variable
1052 // as an object of type std::promise, but as a pointer to such an
1053 // object. This pointer we can move, and the *pointer* itself can
1054 // be 'const' (meaning we can leave the lambda as non-mutable)
1055 // even though we modify the object *pointed to*. One would think
1056 // that a std::unique_ptr would be the right choice for this, but
1057 // that's not true: the resulting lambda function can then be
1058 // non-mutable, but the lambda function object is not copyable
1059 // and at least some TBB variants require that as well. So
1060 // instead we move the std::unique_ptr used above into a
1061 // std::shared_ptr to be stored within the lambda function object.
1062 task_data->task_group.run(
1063 [function_object,
1064 promise =
1065 std::shared_ptr<std::promise<RT>>(std::move(promise))]() {
1066 try
1067 {
1068 internal::evaluate_and_set_promise(function_object, *promise);
1069 }
1070 catch (...)
1071 {
1072 try
1073 {
1074 // store anything thrown in the promise
1075 promise->set_exception(std::current_exception());
1076 }
1077 catch (...)
1078 {
1079 // set_exception() may throw too. But ignore this on
1080 // the task.
1081 }
1082 }
1083 });
1084
1085#else
1086 // If no threading library is supported, just fall back onto C++11
1087 // facilities. The problem with this is that the standard does
1088 // not actually say what std::async should do. The first
1089 // argument to that function can be std::launch::async or
1090 // std::launch::deferred, or both. The *intent* of the standard's
1091 // authors was probably that if one sets it to
1092 // std::launch::async | std::launch::deferred,
1093 // that the task is run in a thread pool. But at least as of
1094 // 2021, GCC doesn't do that: It just runs it on a new thread.
1095 // If one chooses std::launch::deferred, it runs the task on
1096 // the same thread but only when one calls join() on the task's
1097 // std::future object. In the former case, this leads to
1098 // oversubscription, in the latter case to undersubscription of
1099 // resources. We choose oversubscription here.
1100 //
1101 // The issue illustrates why relying on external libraries
1102 // with task schedulers is the way to go.
1103 task_data = std::make_shared<TaskData>(
1104 std::async(std::launch::async | std::launch::deferred,
1105 function_object));
1106#endif
1107 }
1108 else
1109 {
1110 // Only one thread allowed. So let the task run to completion
1111 // and just emplace a 'ready' future.
1112 //
1113 // The design of std::promise/std::future is unclear, but it
1114 // seems that the intent is to obtain the std::future before
1115 // we set the std::promise. So create the TaskData object at
1116 // the top and then run the task and set the returned
1117 // value. Since everything here happens sequentially, it
1118 // really doesn't matter in which order all of this is
1119 // happening.
1120 std::promise<RT> promise;
1121 task_data = std::make_shared<TaskData>(promise.get_future());
1122 try
1123 {
1124 internal::evaluate_and_set_promise(function_object, promise);
1125 }
1126 catch (...)
1127 {
1128 try
1129 {
1130 // store anything thrown in the promise
1131 promise.set_exception(std::current_exception());
1132 }
1133 catch (...)
1134 {
1135 // set_exception() may throw too. But ignore this on
1136 // the task.
1137 }
1138 }
1139 }
1140 }
1141
1150 Task() = default;
1151
1183 void
1184 join() const
1185 {
1186 // Make sure we actually have a task that we can wait for.
1188
1189 task_data->wait();
1190 }
1191
1204 bool
1205 joinable() const
1206 {
1207 return (task_data != nullptr);
1208 }
1209
1210
1262 {
1263 // Make sure we actually have a task that we can wait for.
1265
1266 // Then return the promised object. If necessary, wait for the promise to
1267 // be set.
1268 return task_data->get();
1269 }
1270
1271
1281 "The current object is not associated with a task that "
1282 "can be joined. It may have been detached, or you "
1283 "may have already joined it in the past.");
1285 private:
1295 {
1296 public:
1301 TaskData(std::future<RT> &&future) noexcept
1302 : future(std::move(future))
1303 , task_has_finished(false)
1304 {}
1305
1310 TaskData(const TaskData &) = delete;
1311
1316 TaskData(TaskData &&) = delete;
1317
1322 TaskData &
1323 operator=(const TaskData &) = delete;
1324
1329 TaskData &
1330 operator=(TaskData &&) = delete;
1331
1339 ~TaskData() noexcept
1340 {
1341 // Explicitly wait for the results to be ready. This class stores
1342 // a std::future object, and we could just let the compiler generate
1343 // the destructor which would then call the destructor of std::future
1344 // which *may* block until the future is ready. As explained in
1345 // https://en.cppreference.com/w/cpp/thread/future/~future
1346 // this is only a *may*, not a *must*. (The standard does not
1347 // appear to say anything about it at all.) As a consequence,
1348 // let's be explicit about waiting.
1349 //
1350 // One of the corner cases we have to worry about is that if a task
1351 // ends by throwing an exception, then wait() will re-throw that
1352 // exception on the thread that calls it, the first time around
1353 // someone calls wait() (or the return_value() function of the
1354 // surrounding class). So if we get to this constructor and an exception
1355 // is thrown by wait(), then that means that the last Task object
1356 // referring to a task is going out of scope with nobody having
1357 // ever checked the return value of the task itself. In that case,
1358 // one could argue that they would also not have cared about whether
1359 // an exception is thrown, and that we should simply ignore the
1360 // exception. This is what we do here. It is also the simplest solution,
1361 // because we don't know what one should do with the exception to begin
1362 // with: destructors aren't allowed to throw exceptions, so we can't
1363 // just rethrow it here if one had been triggered.
1364 try
1365 {
1366 wait();
1367 }
1368 catch (...)
1369 {}
1370 }
1371
1377 void
1379 {
1380 // If we have previously already moved the result, then we don't
1381 // need a lock and can just return.
1383 return;
1384
1385 // Else, we need to go under a lock and try again. A different thread
1386 // may have waited and finished the task since then, so we have to try
1387 // a second time. (This is Schmidt's double-checking pattern.)
1388 std::lock_guard<std::mutex> lock(mutex);
1390 return;
1391 else
1392 {
1393#ifdef DEAL_II_WITH_TBB
1394 // If we build on the TBB, then we can't just wait for the
1395 // std::future object to get ready. Apparently the TBB happily
1396 // enqueues a task into an arena and then just sits on it without
1397 // ever executing it unless someone expresses an interest in the
1398 // task. The way to avoid this is to add the task to a
1399 // tbb::task_group, and then here wait for the single task
1400 // associated with that task group.
1401 task_group.wait();
1402#endif
1403
1404 // Wait for the task to finish and then move its
1405 // result. (We could have made the set_from() function
1406 // that we call here wait for the future to be ready --
1407 // which happens implicitly when it calls future.get() --
1408 // but that would have required putting an explicit
1409 // future.wait() into the implementation of
1410 // internal::return_value<void>::set_from(), which is a
1411 // bit awkward: that class doesn't actually need to set
1412 // anything, and so it looks odd to have the explicit call
1413 // to future.wait() in the set_from() function. Avoid the
1414 // issue by just explicitly calling future.wait() here.)
1415 future.wait();
1416
1417 // Acquire the returned object. If the task ended in an
1418 // exception, `set_from` will call `std::future::get`, which
1419 // will throw an exception. This leaves `returned_object` in
1420 // an undefined state, but moreover we would bypass setting
1421 // `task_has_finished=true` below. So catch the exception
1422 // for just long enough that we can set that flag, and then
1423 // re-throw it:
1424 try
1425 {
1426 returned_object.set_from(future);
1427 }
1428 catch (...)
1429 {
1430 task_has_finished = true;
1431 throw;
1432 }
1433
1434 // If we got here, the task has ended without an exception and
1435 // we can safely set the flag and return.
1436 task_has_finished = true;
1437 }
1438 }
1439
1440
1441
1444 {
1445 wait();
1446 return returned_object.get();
1447 }
1448
1449 private:
1454 std::mutex mutex;
1455
1460 std::future<RT> future;
1461
1480 std::atomic<bool> task_has_finished;
1481
1487
1488#ifdef DEAL_II_WITH_TBB
1492 tbb::task_group task_group;
1493
1494 friend class Task<RT>;
1495#endif
1496 };
1497
1502 std::shared_ptr<TaskData> task_data;
1503 };
1504
1505
1506
1526 template <typename RT>
1527 inline Task<RT>
1528 new_task(const std::function<RT()> &function)
1529 {
1530 return Task<RT>(function);
1531 }
1532
1533
1534
1612 template <typename FunctionObjectType>
1613 DEAL_II_CXX20_REQUIRES((std::invocable<FunctionObjectType>))
1614 inline auto new_task(FunctionObjectType function_object)
1615 -> Task<decltype(function_object())>
1616 {
1617 using return_type = decltype(function_object());
1619 return new_task(std::function<return_type()>(function_object));
1620 }
1621
1622
1623
1630 template <typename RT, typename... Args>
1631 inline Task<RT>
1632 new_task(RT (*fun_ptr)(Args...), std_cxx20::type_identity_t<Args>... args)
1633 {
1634 auto dummy = std::make_tuple(internal::maybe_make_ref<Args>::act(args)...);
1635 return new_task(
1636 [dummy, fun_ptr]() -> RT { return std_cxx17::apply(fun_ptr, dummy); });
1637 }
1638
1639
1640
1647 template <typename RT, typename C, typename... Args>
1648 inline Task<RT>
1649 new_task(RT (C::*fun_ptr)(Args...),
1650 std_cxx20::type_identity_t<C> &c,
1651 std_cxx20::type_identity_t<Args>... args)
1652 {
1653 // NOLINTNEXTLINE(modernize-avoid-bind) silence clang-tidy
1654 return new_task(std::function<RT()>(std::bind(
1655 fun_ptr, std::ref(c), internal::maybe_make_ref<Args>::act(args)...)));
1656 }
1657
1664 template <typename RT, typename C, typename... Args>
1665 inline Task<RT>
1666 new_task(RT (C::*fun_ptr)(Args...) const,
1667 std_cxx20::type_identity_t<const C> &c,
1668 std_cxx20::type_identity_t<Args>... args)
1669 {
1670 // NOLINTNEXTLINE(modernize-avoid-bind) silence clang-tidy
1671 return new_task(std::function<RT()>(std::bind(
1672 fun_ptr, std::cref(c), internal::maybe_make_ref<Args>::act(args)...)));
1673 }
1674
1675
1676 // ------------------------ TaskGroup -------------------------------------
1677
1690 template <typename RT = void>
1692 {
1693 public:
1697 TaskGroup &
1699 {
1700 tasks.push_back(t);
1701 return *this;
1702 }
1703
1704
1712 std::size_t
1713 size() const
1714 {
1715 return tasks.size();
1716 }
1717
1732 std::vector<RT>
1734 {
1735 std::vector<RT> results;
1736 results.reserve(size());
1737 for (auto &t : tasks)
1738 results.emplace_back(std::move(t.return_value()));
1739 return results;
1740 }
1741
1742
1749 void
1750 join_all() const
1751 {
1752 for (auto &t : tasks)
1753 t.join();
1754 }
1755
1756 private:
1760 std::list<Task<RT>> tasks;
1761 };
1762
1763} // namespace Threads
1764
1771#endif
static void initialize_multithreading()
static unsigned int n_threads()
TaskGroup & operator+=(const Task< RT > &t)
std::vector< RT > return_values()
std::size_t size() const
std::list< Task< RT > > tasks
TaskData(std::future< RT > &&future) noexcept
TaskData(const TaskData &)=delete
TaskData & operator=(const TaskData &)=delete
std::atomic< bool > task_has_finished
internal::return_value< RT > returned_object
TaskData & operator=(TaskData &&)=delete
TaskData(TaskData &&)=delete
internal::return_value< RT >::reference_type get()
std::shared_ptr< TaskData > task_data
bool joinable() const
internal::return_value< RT >::reference_type return_value()
Task()=default
Task(const std::function< RT()> &function_object)
std::list< Thread< RT > > threads
ThreadGroup & operator+=(const Thread< RT > &t)
Thread(const std::function< RT()> &function)
internal::return_value< RT >::reference_type return_value()
std::shared_ptr< internal::ThreadDescriptor< RT > > thread_descriptor
bool operator==(const Thread &t) const
Thread()=default
Thread(const Thread< RT > &t)
#define DEAL_II_DEPRECATED
Definition config.h:172
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:472
#define DEAL_II_DISABLE_EXTRA_DIAGNOSTICS
Definition config.h:486
#define DEAL_II_CXX20_REQUIRES(condition)
Definition config.h:160
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:473
#define DEAL_II_ENABLE_EXTRA_DIAGNOSTICS
Definition config.h:529
Point< 2 > second
Definition grid_out.cc:4616
static ::ExceptionBase & ExcNoTask()
#define Assert(cond, exc)
#define DeclExceptionMsg(Exception, defaulttext)
Definition exceptions.h:488
static ::ExceptionBase & ExcInternalError()
#define AssertThrow(cond, exc)
std::vector< std::pair< ForwardIterator, ForwardIterator > > split_range(const ForwardIterator &begin, const ForwardIterator &end, const unsigned int n_intervals)
Thread< RT > new_thread(const std::function< RT()> &function)
Task< RT > new_task(const std::function< RT()> &function)
std::vector< std::pair< unsigned int, unsigned int > > split_interval(const unsigned int begin, const unsigned int end, const unsigned int n_intervals)
void evaluate_and_set_promise(Function &function, std::promise< RT > &promise)
void call(const std::function< RT()> &function, internal::return_value< RT > &ret_val)
void handle_std_exception(const std::exception &exc)
VectorType::value_type * end(VectorType &V)
VectorType::value_type * begin(VectorType &V)
typename type_identity< T >::type type_identity_t
Definition type_traits.h:96
STL namespace.
std::shared_ptr< return_value< RT > > ret_val
void start(const std::function< RT()> &function)
static void thread_entry_point(const std::function< RT()> &function, std::shared_ptr< return_value< RT > > ret_val)
static std::reference_wrapper< T > act(T &t)
void set_from(std::future< RT & > &v)
void set_from(std::future< RT > &v)