Reference documentation for deal.II version 9.4.1
\(\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 - 2022 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
26
27# include <atomic>
28# include <functional>
29# include <future>
30# include <list>
31# include <memory>
32# include <mutex>
33# include <thread>
34# include <utility>
35# include <vector>
36
37# ifdef DEAL_II_WITH_TBB
39# include <tbb/task_group.h>
41# endif
42
44
47
48
56namespace Threads
57{
76 class Mutex : public std::mutex
77 {
78 public:
82 Mutex() = default;
83
88 Mutex(const Mutex &)
89 : std::mutex()
90 {}
91
96 Mutex &
98 {
99 return *this;
100 }
101 };
102} // namespace Threads
103
104
105namespace Threads
106{
121 template <typename ForwardIterator>
122 std::vector<std::pair<ForwardIterator, ForwardIterator>>
123 split_range(const ForwardIterator &begin,
124 const ForwardIterator &end,
125 const unsigned int n_intervals);
126
135 std::vector<std::pair<unsigned int, unsigned int>>
136 split_interval(const unsigned int begin,
137 const unsigned int end,
138 const unsigned int n_intervals);
139
149 namespace internal
150 {
166 [[noreturn]] void
167 handle_std_exception(const std::exception &exc);
168
176 [[noreturn]] void
178 } // namespace internal
179
184} // namespace Threads
185
186/* ----------- implementation of functions in namespace Threads ---------- */
187# ifndef DOXYGEN
188namespace Threads
189{
190 template <typename ForwardIterator>
191 std::vector<std::pair<ForwardIterator, ForwardIterator>>
192 split_range(const ForwardIterator &begin,
193 const ForwardIterator &end,
194 const unsigned int n_intervals)
195 {
196 using IteratorPair = std::pair<ForwardIterator, ForwardIterator>;
197
198 // in non-multithreaded mode, we often have the case that this
199 // function is called with n_intervals==1, so have a shortcut here
200 // to handle that case efficiently
201
202 if (n_intervals == 1)
203 return (std::vector<IteratorPair>(1, IteratorPair(begin, end)));
204
205 // if more than one interval requested, do the full work
206 const unsigned int n_elements = std::distance(begin, end);
207 const unsigned int n_elements_per_interval = n_elements / n_intervals;
208 const unsigned int residual = n_elements % n_intervals;
209
210 std::vector<IteratorPair> return_values(n_intervals);
211
212 return_values[0].first = begin;
213 for (unsigned int i = 0; i < n_intervals; ++i)
214 {
215 if (i != n_intervals - 1)
216 {
217 return_values[i].second = return_values[i].first;
218 // note: the cast is performed to avoid a warning of gcc
219 // that in the library `dist>=0' is checked (dist has a
220 // template type, which here is unsigned if no cast is
221 // performed)
222 std::advance(return_values[i].second,
223 static_cast<signed int>(n_elements_per_interval));
224 // distribute residual in division equally among the first
225 // few subintervals
226 if (i < residual)
227 ++return_values[i].second;
228
229 return_values[i + 1].first = return_values[i].second;
230 }
231 else
232 return_values[i].second = end;
233 }
234 return return_values;
235 }
236} // namespace Threads
237
238# endif // DOXYGEN
239
240namespace Threads
241{
242 namespace internal
243 {
262 template <typename RT>
264 {
265 private:
267
268 public:
269 using reference_type = RT &;
270
272 : value()
273 {}
274
275 inline reference_type
277 {
278 return value;
279 }
280
281 inline void
282 set(RT &&v)
283 {
284 value = std::move(v);
285 }
286
287 inline void
288 set_from(std::future<RT> &v)
289 {
290 value = std::move(v.get());
291 }
292 };
293
294
314 template <typename RT>
315 struct return_value<RT &>
316 {
317 private:
318 RT *value;
319
320 public:
321 using reference_type = RT &;
322
324 : value(nullptr)
325 {}
326
327 inline reference_type
328 get() const
329 {
330 return *value;
331 }
332
333 inline void
334 set(RT &v)
335 {
336 value = &v;
337 }
338
339 inline void
340 set_from(std::future<RT &> &v)
341 {
342 value = &v.get();
343 }
344 };
345
346
365 template <>
366 struct return_value<void>
367 {
368 using reference_type = void;
369
370 static inline void
372 {}
373
374
375 inline void
376 set_from(std::future<void> &)
377 {}
378 };
379 } // namespace internal
380
381
382
383 namespace internal
384 {
385 template <typename RT>
386 inline void
387 call(const std::function<RT()> & function,
389 {
390 ret_val.set(function());
391 }
392
393
394 inline void
395 call(const std::function<void()> &function, internal::return_value<void> &)
396 {
397 function();
398 }
399 } // namespace internal
400
401
402
403 namespace internal
404 {
415 template <typename RT>
417 {
421 std::thread thread;
422
431 std::shared_ptr<return_value<RT>> ret_val;
432
468 std::atomic<bool> thread_is_active;
469
474
479 : thread_is_active(false)
480 {}
481
483 {
484 if (!thread_is_active)
485 return;
486 thread.detach();
487 thread_is_active = false;
488 }
489
494 void
495 start(const std::function<RT()> &function)
496 {
497 thread_is_active = true;
498 ret_val = std::make_shared<return_value<RT>>();
499 thread = std::thread(thread_entry_point, function, ret_val);
500 }
501
502
506 void
508 {
509 // see if the thread hasn't been joined yet. if it has, then
510 // join() is a no-op. use schmidt's double-checking strategy
511 // to use the mutex only when necessary
512 if (thread_is_active == false)
513 return;
514
515 std::lock_guard<std::mutex> lock(thread_is_active_mutex);
516 if (thread_is_active == true)
517 {
518 Assert(thread.joinable(), ExcInternalError());
519 thread.join();
520 thread_is_active = false;
521 }
522 }
523
524 private:
528 static void
529 thread_entry_point(const std::function<RT()> & function,
530 std::shared_ptr<return_value<RT>> ret_val)
531 {
532 // call the function in question. since an exception that is
533 // thrown from one of the called functions will not propagate
534 // to the main thread, it will kill the program if not treated
535 // here before we return to the operating system's thread
536 // library
537 try
538 {
539 call(function, *ret_val);
540 }
541 catch (const std::exception &exc)
542 {
544 }
545 catch (...)
546 {
548 }
549 }
550 };
551 } // namespace internal
552
553
580 template <typename RT = void>
581 class Thread
582 {
583 public:
588 Thread(const std::function<RT()> &function)
589 : thread_descriptor(new internal::ThreadDescriptor<RT>())
590 {
591 // in a second step, start the thread.
592 thread_descriptor->start(function);
593 }
594
601 Thread() = default;
602
609 {}
610
616 void
617 join() const
618 {
620 thread_descriptor->join();
621 }
622
668 {
669 join();
670 return thread_descriptor->ret_val->get();
671 }
672
677 bool
678 valid() const
679 {
680 return static_cast<bool>(thread_descriptor);
681 }
682
683
689 bool
690 operator==(const Thread &t) const
691 {
693 }
694
695 private:
701 std::shared_ptr<internal::ThreadDescriptor<RT>> thread_descriptor;
702 };
703
704
705 namespace internal
706 {
714 template <typename T>
716 {
717 static T
718 act(T &t)
719 {
720 return t;
721 }
722 };
723
724
725
733 template <typename T>
734 struct maybe_make_ref<T &>
735 {
736 static std::reference_wrapper<T>
737 act(T &t)
738 {
739 return std::ref(t);
740 }
741 };
742 } // namespace internal
743
744
745
746 // ----------- thread starters for functions not taking any parameters
747
758 template <typename RT>
759 DEAL_II_DEPRECATED inline Thread<RT>
760 new_thread(const std::function<RT()> &function)
761 {
762 // Here and below we need to disable deprecation warnings for calling the
763 // constructor in this function - as this function itself is deprecated
764 // these warnings are not helpful. This problem only appears in some
765 // configurations (e.g., Debian 11 with GCC-10).
767 return Thread<RT>(function);
769 }
770
771
772
839 template <typename FunctionObjectType>
840 DEAL_II_DEPRECATED inline auto
841 new_thread(FunctionObjectType function_object)
842 -> Thread<decltype(function_object())>
843 {
844 // See the comment in the first new_thread() implementation
846 using return_type = decltype(function_object());
847 return Thread<return_type>(std::function<return_type()>(function_object));
849 }
850
851
852
861 template <typename RT, typename... Args>
862 DEAL_II_DEPRECATED inline Thread<RT>
863 new_thread(RT (*fun_ptr)(Args...), typename identity<Args>::type... args)
864 {
865 // See the comment in the first new_thread() implementation
867 auto dummy = std::make_tuple(internal::maybe_make_ref<Args>::act(args)...);
868 return new_thread(
869 [dummy, fun_ptr]() -> RT { return std_cxx17::apply(fun_ptr, dummy); });
871 }
872
873
874
882 template <typename RT, typename C, typename... Args>
883 DEAL_II_DEPRECATED inline Thread<RT>
884 new_thread(RT (C::*fun_ptr)(Args...),
885 typename identity<C>::type &c,
886 typename identity<Args>::type... args)
887 {
888 // NOLINTNEXTLINE(modernize-avoid-bind) silence clang-tidy
889 return new_thread(std::function<RT()>(std::bind(
890 fun_ptr, std::ref(c), internal::maybe_make_ref<Args>::act(args)...)));
891 }
892
900 template <typename RT, typename C, typename... Args>
901 DEAL_II_DEPRECATED inline Thread<RT>
902 new_thread(RT (C::*fun_ptr)(Args...) const,
903 typename identity<const C>::type &c,
904 typename identity<Args>::type... args)
905 {
906 // See the comment in the first new_thread() implementation
908 // NOLINTNEXTLINE(modernize-avoid-bind) silence clang-tidy
909 return new_thread(std::function<RT()>(std::bind(
910 fun_ptr, std::cref(c), internal::maybe_make_ref<Args>::act(args)...)));
912 }
913
914 // ------------------------ ThreadGroup -------------------------------------
915
925 template <typename RT = void>
927 {
928 public:
934 {
935 threads.push_back(t);
936 return *this;
937 }
938
945 void
946 join_all() const
947 {
948 for (auto &t : threads)
949 t.join();
950 }
951
952 private:
956 std::list<Thread<RT>> threads;
957 };
958
959
960 namespace internal
961 {
965 template <typename RT, typename Function>
966 void
967 evaluate_and_set_promise(Function &function, std::promise<RT> &promise)
968 {
969 promise.set_value(function());
970 }
971
972
980 template <typename Function>
981 void
982 evaluate_and_set_promise(Function &function, std::promise<void> &promise)
983 {
984 function();
985 promise.set_value();
986 }
987 } // namespace internal
988
989
990
1017 template <typename RT = void>
1018 class Task
1019 {
1020 public:
1032 Task(const std::function<RT()> &function_object)
1033 {
1035 {
1036# ifdef DEAL_II_WITH_TBB
1037 // Create a promise object and from it extract a future that
1038 // we can use to refer to the outcome of the task. For reasons
1039 // explained below, we can't just create a std::promise object,
1040 // but have to make do with a pointer to such an object.
1041 std::unique_ptr<std::promise<RT>> promise =
1042 std::make_unique<std::promise<RT>>();
1043 task_data =
1044 std::make_shared<TaskData>(std::move(promise->get_future()));
1045
1046 // Then start the task, using a task_group object (for just this one
1047 // task) that is associated with the TaskData object. Note that we
1048 // have to *copy* the function object being executed so that it is
1049 // guaranteed to live on the called thread as well -- the copying is
1050 // facilitated by capturing the 'function_object' variable by value.
1051 //
1052 // We also have to *move* the promise object into the new task's
1053 // memory space because promises can not be copied and we can't refer
1054 // to it by reference because it's a local variable of the current
1055 // (surrounding) function that may go out of scope before the promise
1056 // is ultimately set. This leads to a conundrum: if we had just
1057 // declared 'promise' as an object of type std::promise, then we could
1058 // capture it in the lambda function via
1059 // [..., promise=std::move(promise)]() {...}
1060 // and set the promise in the body of the lambda. But setting a
1061 // promise is a non-const operation on the promise, and so we would
1062 // actually have to declare the lambda function as 'mutable' because
1063 // by default, lambda captures are 'const'. That is, we would have
1064 // to write
1065 // [..., promise=std::move(promise)]() mutable {...}
1066 // But this leads to other problems: It turns out that the
1067 // tbb::task_group::run() function cannot take mutable lambdas as
1068 // argument :-(
1069 //
1070 // We work around this issue by not declaring the 'promise' variable
1071 // as an object of type std::promise, but as a pointer to such an
1072 // object. This pointer we can move, and the *pointer* itself can
1073 // be 'const' (meaning we can leave the lambda as non-mutable)
1074 // even though we modify the object *pointed to*. One would think
1075 // that a std::unique_ptr would be the right choice for this, but
1076 // that's not true: the resulting lambda function can then be
1077 // non-mutable, but the lambda function object is not copyable
1078 // and at least some TBB variants require that as well. So
1079 // instead we move the std::unique_ptr used above into a
1080 // std::shared_ptr to be stored within the lambda function object.
1081 task_data->task_group.run(
1082 [function_object,
1083 promise =
1084 std::shared_ptr<std::promise<RT>>(std::move(promise))]() {
1085 try
1086 {
1087 internal::evaluate_and_set_promise(function_object, *promise);
1088 }
1089 catch (...)
1090 {
1091 try
1092 {
1093 // store anything thrown in the promise
1094 promise->set_exception(std::current_exception());
1095 }
1096 catch (...)
1097 {
1098 // set_exception() may throw too. But ignore this on
1099 // the task.
1100 }
1101 }
1102 });
1103
1104# else
1105 // If no threading library is supported, just fall back onto C++11
1106 // facilities. The problem with this is that the standard does
1107 // not actually say what std::async should do. The first
1108 // argument to that function can be std::launch::async or
1109 // std::launch::deferred, or both. The *intent* of the standard's
1110 // authors was probably that if one sets it to
1111 // std::launch::async | std::launch::deferred,
1112 // that the task is run in a thread pool. But at least as of
1113 // 2021, GCC doesn't do that: It just runs it on a new thread.
1114 // If one chooses std::launch::deferred, it runs the task on
1115 // the same thread but only when one calls join() on the task's
1116 // std::future object. In the former case, this leads to
1117 // oversubscription, in the latter case to undersubscription of
1118 // resources. We choose oversubscription here.
1119 //
1120 // The issue illustrates why relying on external libraries
1121 // with task schedulers is the way to go.
1122 task_data = std::make_shared<TaskData>(
1123 std::async(std::launch::async | std::launch::deferred,
1124 function_object));
1125# endif
1126 }
1127 else
1128 {
1129 // Only one thread allowed. So let the task run to completion
1130 // and just emplace a 'ready' future.
1131 //
1132 // The design of std::promise/std::future is unclear, but it
1133 // seems that the intent is to obtain the std::future before
1134 // we set the std::promise. So create the TaskData object at
1135 // the top and then run the task and set the returned
1136 // value. Since everything here happens sequentially, it
1137 // really doesn't matter in which order all of this is
1138 // happening.
1139 std::promise<RT> promise;
1140 task_data = std::make_shared<TaskData>(promise.get_future());
1141 try
1142 {
1143 internal::evaluate_and_set_promise(function_object, promise);
1144 }
1145 catch (...)
1146 {
1147 try
1148 {
1149 // store anything thrown in the promise
1150 promise.set_exception(std::current_exception());
1151 }
1152 catch (...)
1153 {
1154 // set_exception() may throw too. But ignore this on
1155 // the task.
1156 }
1157 }
1158 }
1159 }
1160
1169 Task() = default;
1170
1202 void
1203 join() const
1204 {
1205 // Make sure we actually have a task that we can wait for.
1207
1208 task_data->wait();
1209 }
1210
1223 bool
1224 joinable() const
1225 {
1226 return (task_data != nullptr);
1227 }
1228
1229
1281 {
1282 // Make sure we actually have a task that we can wait for.
1284
1285 // Then return the promised object. If necessary, wait for the promise to
1286 // be set.
1287 return task_data->get();
1288 }
1289
1290
1300 "The current object is not associated with a task that "
1301 "can be joined. It may have been detached, or you "
1302 "may have already joined it in the past.");
1304 private:
1314 {
1315 public:
1320 TaskData(std::future<RT> &&future) noexcept
1321 : future(std::move(future))
1322 , task_has_finished(false)
1323 {}
1324
1329 TaskData(const TaskData &) = delete;
1330
1335 TaskData(TaskData &&) = delete;
1336
1341 TaskData &
1342 operator=(const TaskData &) = delete;
1343
1348 TaskData &
1349 operator=(TaskData &&) = delete;
1350
1358 ~TaskData() noexcept
1359 {
1360 // Explicitly wait for the results to be ready. This class stores
1361 // a std::future object, and we could just let the compiler generate
1362 // the destructor which would then call the destructor of std::future
1363 // which *may* block until the future is ready. As explained in
1364 // https://en.cppreference.com/w/cpp/thread/future/~future
1365 // this is only a *may*, not a *must*. (The standard does not
1366 // appear to say anything about it at all.) As a consequence,
1367 // let's be explicit about waiting.
1368 //
1369 // One of the corner cases we have to worry about is that if a task
1370 // ends by throwing an exception, then wait() will re-throw that
1371 // exception on the thread that calls it, the first time around
1372 // someone calls wait() (or the return_value() function of the
1373 // surrounding class). So if we get to this constructor and an exception
1374 // is thrown by wait(), then that means that the last Task object
1375 // referring to a task is going out of scope with nobody having
1376 // ever checked the return value of the task itself. In that case,
1377 // one could argue that they would also not have cared about whether
1378 // an exception is thrown, and that we should simply ignore the
1379 // exception. This is what we do here. It is also the simplest solution,
1380 // because we don't know what one should do with the exception to begin
1381 // with: destructors aren't allowed to throw exceptions, so we can't
1382 // just rethrow it here if one had been triggered.
1383 try
1384 {
1385 wait();
1386 }
1387 catch (...)
1388 {}
1389 }
1390
1396 void
1398 {
1399 // If we have previously already moved the result, then we don't
1400 // need a lock and can just return.
1402 return;
1403
1404 // Else, we need to go under a lock and try again. A different thread
1405 // may have waited and finished the task since then, so we have to try
1406 // a second time. (This is Schmidt's double-checking pattern.)
1407 std::lock_guard<std::mutex> lock(mutex);
1409 return;
1410 else
1411 {
1412# ifdef DEAL_II_WITH_TBB
1413 // If we build on the TBB, then we can't just wait for the
1414 // std::future object to get ready. Apparently the TBB happily
1415 // enqueues a task into an arena and then just sits on it without
1416 // ever executing it unless someone expresses an interest in the
1417 // task. The way to avoid this is to add the task to a
1418 // tbb::task_group, and then here wait for the single task
1419 // associated with that task group.
1420 task_group.wait();
1421# endif
1422
1423 // Wait for the task to finish and then move its
1424 // result. (We could have made the set_from() function
1425 // that we call here wait for the future to be ready --
1426 // which happens implicitly when it calls future.get() --
1427 // but that would have required putting an explicit
1428 // future.wait() into the implementation of
1429 // internal::return_value<void>::set_from(), which is a
1430 // bit awkward: that class doesn't actually need to set
1431 // anything, and so it looks odd to have the explicit call
1432 // to future.wait() in the set_from() function. Avoid the
1433 // issue by just explicitly calling future.wait() here.)
1434 future.wait();
1435 returned_object.set_from(future);
1436
1437 // Now we can safely set the flag and return.
1438 task_has_finished = true;
1439 }
1440 }
1441
1442
1443
1446 {
1447 wait();
1448 return returned_object.get();
1449 }
1450
1451 private:
1456 std::mutex mutex;
1457
1462 std::future<RT> future;
1463
1482 std::atomic<bool> task_has_finished;
1483
1489
1490# ifdef DEAL_II_WITH_TBB
1494 tbb::task_group task_group;
1495
1496 friend class Task<RT>;
1497# endif
1498 };
1499
1504 std::shared_ptr<TaskData> task_data;
1505 };
1506
1507
1508
1528 template <typename RT>
1529 inline Task<RT>
1530 new_task(const std::function<RT()> &function)
1531 {
1532 return Task<RT>(function);
1533 }
1534
1535
1536
1612 template <typename FunctionObjectType>
1613 inline auto
1614 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...), typename identity<Args>::type... 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 typename identity<C>::type &c,
1651 typename identity<Args>::type... 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 typename identity<const C>::type &c,
1668 typename identity<Args>::type... 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
1718
1725 void
1726 join_all() const
1727 {
1728 for (auto &t : tasks)
1729 t.join();
1730 }
1731
1732 private:
1736 std::list<Task<RT>> tasks;
1737 };
1738
1739} // namespace Threads
1740
1746//---------------------------------------------------------------------------
1748// end of #ifndef dealii_thread_management_h
1749#endif
1750//---------------------------------------------------------------------------
static void initialize_multithreading()
static unsigned int n_threads()
Mutex()=default
Mutex & operator=(const Mutex &)
Mutex(const Mutex &)
bool joinable() const
internal::return_value< RT >::reference_type return_value()
void join() const
Task()=default
Task(const std::function< RT()> &function_object)
std::list< Thread< RT > > threads
ThreadGroup & operator+=(const Thread< RT > &t)
bool valid() const
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:164
#define DEAL_II_NAMESPACE_OPEN
Definition: config.h:442
#define DEAL_II_DISABLE_EXTRA_DIAGNOSTICS
Definition: config.h:456
#define DEAL_II_NAMESPACE_CLOSE
Definition: config.h:443
#define DEAL_II_ENABLE_EXTRA_DIAGNOSTICS
Definition: config.h:495
Point< 2 > second
Definition: grid_out.cc:4604
static ::ExceptionBase & ExcNoTask()
TaskGroup & operator+=(const Task< RT > &t)
TaskData(std::future< RT > &&future) noexcept
TaskData(const TaskData &)=delete
std::shared_ptr< TaskData > task_data
TaskData & operator=(const TaskData &)=delete
#define Assert(cond, exc)
Definition: exceptions.h:1473
std::atomic< bool > task_has_finished
std::size_t size() const
internal::return_value< RT > returned_object
TaskData & operator=(TaskData &&)=delete
std::list< Task< RT > > tasks
#define DeclExceptionMsg(Exception, defaulttext)
Definition: exceptions.h:487
TaskData(TaskData &&)=delete
static ::ExceptionBase & ExcInternalError()
internal::return_value< RT >::reference_type get()
#define AssertThrow(cond, exc)
Definition: exceptions.h:1583
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)
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< void > &)
void set_from(std::future< RT > &v)