Reference documentation for deal.II version GIT d7aca55de5 2022-08-10 12:50:02+00:00
\(\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\}}\)
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 
22 # include <deal.II/base/exceptions.h>
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 
57 namespace Threads
58 {
77  class Mutex : public std::mutex
78  {
79  public:
83  Mutex() = default;
84 
89  Mutex(const Mutex &)
90  : std::mutex()
91  {}
92 
97  Mutex &
98  operator=(const Mutex &)
99  {
100  return *this;
101  }
102  };
103 } // namespace Threads
104 
105 
106 namespace Threads
107 {
122  template <typename ForwardIterator>
123  std::vector<std::pair<ForwardIterator, ForwardIterator>>
124  split_range(const ForwardIterator &begin,
125  const ForwardIterator &end,
126  const unsigned int n_intervals);
127 
136  std::vector<std::pair<unsigned int, unsigned int>>
137  split_interval(const unsigned int begin,
138  const unsigned int end,
139  const unsigned int n_intervals);
140 
150  namespace internal
151  {
167  [[noreturn]] void
168  handle_std_exception(const std::exception &exc);
169 
177  [[noreturn]] void
179  } // namespace internal
180 
185 } // namespace Threads
186 
187 /* ----------- implementation of functions in namespace Threads ---------- */
188 # ifndef DOXYGEN
189 namespace Threads
190 {
191  template <typename ForwardIterator>
192  std::vector<std::pair<ForwardIterator, ForwardIterator>>
193  split_range(const ForwardIterator &begin,
194  const ForwardIterator &end,
195  const unsigned int n_intervals)
196  {
197  using IteratorPair = std::pair<ForwardIterator, ForwardIterator>;
198 
199  // in non-multithreaded mode, we often have the case that this
200  // function is called with n_intervals==1, so have a shortcut here
201  // to handle that case efficiently
202 
203  if (n_intervals == 1)
204  return (std::vector<IteratorPair>(1, IteratorPair(begin, end)));
205 
206  // if more than one interval requested, do the full work
207  const unsigned int n_elements = std::distance(begin, end);
208  const unsigned int n_elements_per_interval = n_elements / n_intervals;
209  const unsigned int residual = n_elements % n_intervals;
210 
211  std::vector<IteratorPair> return_values(n_intervals);
212 
213  return_values[0].first = begin;
214  for (unsigned int i = 0; i < n_intervals; ++i)
215  {
216  if (i != n_intervals - 1)
217  {
218  return_values[i].second = return_values[i].first;
219  // note: the cast is performed to avoid a warning of gcc
220  // that in the library `dist>=0' is checked (dist has a
221  // template type, which here is unsigned if no cast is
222  // performed)
223  std::advance(return_values[i].second,
224  static_cast<signed int>(n_elements_per_interval));
225  // distribute residual in division equally among the first
226  // few subintervals
227  if (i < residual)
228  ++return_values[i].second;
229 
230  return_values[i + 1].first = return_values[i].second;
231  }
232  else
233  return_values[i].second = end;
234  }
235  return return_values;
236  }
237 } // namespace Threads
238 
239 # endif // DOXYGEN
240 
241 namespace Threads
242 {
243  namespace internal
244  {
263  template <typename RT>
265  {
266  private:
267  RT value;
268 
269  public:
270  using reference_type = RT &;
271 
272  inline return_value()
273  : value()
274  {}
275 
276  inline reference_type
277  get()
278  {
279  return value;
280  }
281 
282  inline void
283  set(RT &&v)
284  {
285  value = std::move(v);
286  }
287 
288  inline void
289  set_from(std::future<RT> &v)
290  {
291  value = std::move(v.get());
292  }
293  };
294 
295 
315  template <typename RT>
316  struct return_value<RT &>
317  {
318  private:
319  RT *value;
320 
321  public:
322  using reference_type = RT &;
323 
324  inline return_value()
325  : value(nullptr)
326  {}
327 
328  inline reference_type
329  get() const
330  {
331  return *value;
332  }
333 
334  inline void
335  set(RT &v)
336  {
337  value = &v;
338  }
339 
340  inline void
341  set_from(std::future<RT &> &v)
342  {
343  value = &v.get();
344  }
345  };
346 
347 
366  template <>
367  struct return_value<void>
368  {
369  using reference_type = void;
370 
371  static inline void
372  get()
373  {}
374 
375 
376  inline void
377  set_from(std::future<void> &)
378  {}
379  };
380  } // namespace internal
381 
382 
383 
384  namespace internal
385  {
386  template <typename RT>
387  inline void
388  call(const std::function<RT()> & function,
390  {
391  ret_val.set(function());
392  }
393 
394 
395  inline void
396  call(const std::function<void()> &function, internal::return_value<void> &)
397  {
398  function();
399  }
400  } // namespace internal
401 
402 
403 
404  namespace internal
405  {
416  template <typename RT>
418  {
422  std::thread thread;
423 
432  std::shared_ptr<return_value<RT>> ret_val;
433 
469  std::atomic<bool> thread_is_active;
470 
475 
480  : thread_is_active(false)
481  {}
482 
484  {
485  if (!thread_is_active)
486  return;
487  thread.detach();
488  thread_is_active = false;
489  }
490 
495  void
496  start(const std::function<RT()> &function)
497  {
498  thread_is_active = true;
499  ret_val = std::make_shared<return_value<RT>>();
500  thread = std::thread(thread_entry_point, function, ret_val);
501  }
502 
503 
507  void
509  {
510  // see if the thread hasn't been joined yet. if it has, then
511  // join() is a no-op. use schmidt's double-checking strategy
512  // to use the mutex only when necessary
513  if (thread_is_active == false)
514  return;
515 
516  std::lock_guard<std::mutex> lock(thread_is_active_mutex);
517  if (thread_is_active == true)
518  {
519  Assert(thread.joinable(), ExcInternalError());
520  thread.join();
521  thread_is_active = false;
522  }
523  }
524 
525  private:
529  static void
530  thread_entry_point(const std::function<RT()> & function,
531  std::shared_ptr<return_value<RT>> ret_val)
532  {
533  // call the function in question. since an exception that is
534  // thrown from one of the called functions will not propagate
535  // to the main thread, it will kill the program if not treated
536  // here before we return to the operating system's thread
537  // library
538  try
539  {
540  call(function, *ret_val);
541  }
542  catch (const std::exception &exc)
543  {
545  }
546  catch (...)
547  {
549  }
550  }
551  };
552  } // namespace internal
553 
554 
581  template <typename RT = void>
582  class Thread
583  {
584  public:
589  Thread(const std::function<RT()> &function)
590  : thread_descriptor(new internal::ThreadDescriptor<RT>())
591  {
592  // in a second step, start the thread.
593  thread_descriptor->start(function);
594  }
595 
602  Thread() = default;
603 
608  Thread(const Thread<RT> &t)
610  {}
611 
617  void
618  join() const
619  {
620  if (thread_descriptor)
621  thread_descriptor->join();
622  }
623 
669  {
670  join();
671  return thread_descriptor->ret_val->get();
672  }
673 
678  bool
679  valid() const
680  {
681  return static_cast<bool>(thread_descriptor);
682  }
683 
684 
690  bool
691  operator==(const Thread &t) const
692  {
694  }
695 
696  private:
702  std::shared_ptr<internal::ThreadDescriptor<RT>> thread_descriptor;
703  };
704 
705 
706  namespace internal
707  {
715  template <typename T>
717  {
718  static T
719  act(T &t)
720  {
721  return t;
722  }
723  };
724 
725 
726 
734  template <typename T>
735  struct maybe_make_ref<T &>
736  {
737  static std::reference_wrapper<T>
738  act(T &t)
739  {
740  return std::ref(t);
741  }
742  };
743  } // namespace internal
744 
745 
746 
747  // ----------- thread starters for functions not taking any parameters
748 
759  template <typename RT>
760  DEAL_II_DEPRECATED inline Thread<RT>
761  new_thread(const std::function<RT()> &function)
762  {
763  // Here and below we need to disable deprecation warnings for calling the
764  // constructor in this function - as this function itself is deprecated
765  // these warnings are not helpful. This problem only appears in some
766  // configurations (e.g., Debian 11 with GCC-10).
768  return Thread<RT>(function);
770  }
771 
772 
773 
840  template <typename FunctionObjectType>
841  DEAL_II_DEPRECATED inline auto
842  new_thread(FunctionObjectType function_object)
843  -> Thread<decltype(function_object())>
844  {
845  // See the comment in the first new_thread() implementation
847  using return_type = decltype(function_object());
848  return Thread<return_type>(std::function<return_type()>(function_object));
850  }
851 
852 
853 
862  template <typename RT, typename... Args>
863  DEAL_II_DEPRECATED inline Thread<RT>
864  new_thread(RT (*fun_ptr)(Args...), typename identity<Args>::type... args)
865  {
866  // See the comment in the first new_thread() implementation
868  auto dummy = std::make_tuple(internal::maybe_make_ref<Args>::act(args)...);
869  return new_thread(
870  [dummy, fun_ptr]() -> RT { return std_cxx17::apply(fun_ptr, dummy); });
872  }
873 
874 
875 
883  template <typename RT, typename C, typename... Args>
884  DEAL_II_DEPRECATED inline Thread<RT>
885  new_thread(RT (C::*fun_ptr)(Args...),
886  typename identity<C>::type &c,
887  typename identity<Args>::type... args)
888  {
889  // NOLINTNEXTLINE(modernize-avoid-bind) silence clang-tidy
890  return new_thread(std::function<RT()>(std::bind(
891  fun_ptr, std::ref(c), internal::maybe_make_ref<Args>::act(args)...)));
892  }
893 
901  template <typename RT, typename C, typename... Args>
902  DEAL_II_DEPRECATED inline Thread<RT>
903  new_thread(RT (C::*fun_ptr)(Args...) const,
904  typename identity<const C>::type &c,
905  typename identity<Args>::type... args)
906  {
907  // See the comment in the first new_thread() implementation
909  // NOLINTNEXTLINE(modernize-avoid-bind) silence clang-tidy
910  return new_thread(std::function<RT()>(std::bind(
911  fun_ptr, std::cref(c), internal::maybe_make_ref<Args>::act(args)...)));
913  }
914 
915  // ------------------------ ThreadGroup -------------------------------------
916 
926  template <typename RT = void>
928  {
929  public:
933  ThreadGroup &
935  {
936  threads.push_back(t);
937  return *this;
938  }
939 
946  void
947  join_all() const
948  {
949  for (auto &t : threads)
950  t.join();
951  }
952 
953  private:
957  std::list<Thread<RT>> threads;
958  };
959 
960 
961  namespace internal
962  {
966  template <typename RT, typename Function>
967  void
968  evaluate_and_set_promise(Function &function, std::promise<RT> &promise)
969  {
970  promise.set_value(function());
971  }
972 
973 
981  template <typename Function>
982  void
983  evaluate_and_set_promise(Function &function, std::promise<void> &promise)
984  {
985  function();
986  promise.set_value();
987  }
988  } // namespace internal
989 
990 
991 
1018  template <typename RT = void>
1019  class Task
1020  {
1021  public:
1033  Task(const std::function<RT()> &function_object)
1034  {
1035  if (MultithreadInfo::n_threads() > 1)
1036  {
1037 # ifdef DEAL_II_WITH_TBB
1038  // Create a promise object and from it extract a future that
1039  // we can use to refer to the outcome of the task. For reasons
1040  // explained below, we can't just create a std::promise object,
1041  // but have to make do with a pointer to such an object.
1042  std::unique_ptr<std::promise<RT>> promise =
1043  std::make_unique<std::promise<RT>>();
1044  task_data =
1045  std::make_shared<TaskData>(std::move(promise->get_future()));
1046 
1047  // Then start the task, using a task_group object (for just this one
1048  // task) that is associated with the TaskData object. Note that we
1049  // have to *copy* the function object being executed so that it is
1050  // guaranteed to live on the called thread as well -- the copying is
1051  // facilitated by capturing the 'function_object' variable by value.
1052  //
1053  // We also have to *move* the promise object into the new task's
1054  // memory space because promises can not be copied and we can't refer
1055  // to it by reference because it's a local variable of the current
1056  // (surrounding) function that may go out of scope before the promise
1057  // is ultimately set. This leads to a conundrum: if we had just
1058  // declared 'promise' as an object of type std::promise, then we could
1059  // capture it in the lambda function via
1060  // [..., promise=std::move(promise)]() {...}
1061  // and set the promise in the body of the lambda. But setting a
1062  // promise is a non-const operation on the promise, and so we would
1063  // actually have to declare the lambda function as 'mutable' because
1064  // by default, lambda captures are 'const'. That is, we would have
1065  // to write
1066  // [..., promise=std::move(promise)]() mutable {...}
1067  // But this leads to other problems: It turns out that the
1068  // tbb::task_group::run() function cannot take mutable lambdas as
1069  // argument :-(
1070  //
1071  // We work around this issue by not declaring the 'promise' variable
1072  // as an object of type std::promise, but as a pointer to such an
1073  // object. This pointer we can move, and the *pointer* itself can
1074  // be 'const' (meaning we can leave the lambda as non-mutable)
1075  // even though we modify the object *pointed to*. One would think
1076  // that a std::unique_ptr would be the right choice for this, but
1077  // that's not true: the resulting lambda function can then be
1078  // non-mutable, but the lambda function object is not copyable
1079  // and at least some TBB variants require that as well. So
1080  // instead we move the std::unique_ptr used above into a
1081  // std::shared_ptr to be stored within the lambda function object.
1082  task_data->task_group.run(
1083  [function_object,
1084  promise =
1085  std::shared_ptr<std::promise<RT>>(std::move(promise))]() {
1086  try
1087  {
1088  internal::evaluate_and_set_promise(function_object, *promise);
1089  }
1090  catch (...)
1091  {
1092  try
1093  {
1094  // store anything thrown in the promise
1095  promise->set_exception(std::current_exception());
1096  }
1097  catch (...)
1098  {
1099  // set_exception() may throw too. But ignore this on
1100  // the task.
1101  }
1102  }
1103  });
1104 
1105 # else
1106  // If no threading library is supported, just fall back onto C++11
1107  // facilities. The problem with this is that the standard does
1108  // not actually say what std::async should do. The first
1109  // argument to that function can be std::launch::async or
1110  // std::launch::deferred, or both. The *intent* of the standard's
1111  // authors was probably that if one sets it to
1112  // std::launch::async | std::launch::deferred,
1113  // that the task is run in a thread pool. But at least as of
1114  // 2021, GCC doesn't do that: It just runs it on a new thread.
1115  // If one chooses std::launch::deferred, it runs the task on
1116  // the same thread but only when one calls join() on the task's
1117  // std::future object. In the former case, this leads to
1118  // oversubscription, in the latter case to undersubscription of
1119  // resources. We choose oversubscription here.
1120  //
1121  // The issue illustrates why relying on external libraries
1122  // with task schedulers is the way to go.
1123  task_data = std::make_shared<TaskData>(
1124  std::async(std::launch::async | std::launch::deferred,
1125  function_object));
1126 # endif
1127  }
1128  else
1129  {
1130  // Only one thread allowed. So let the task run to completion
1131  // and just emplace a 'ready' future.
1132  //
1133  // The design of std::promise/std::future is unclear, but it
1134  // seems that the intent is to obtain the std::future before
1135  // we set the std::promise. So create the TaskData object at
1136  // the top and then run the task and set the returned
1137  // value. Since everything here happens sequentially, it
1138  // really doesn't matter in which order all of this is
1139  // happening.
1140  std::promise<RT> promise;
1141  task_data = std::make_shared<TaskData>(promise.get_future());
1142  try
1143  {
1144  internal::evaluate_and_set_promise(function_object, promise);
1145  }
1146  catch (...)
1147  {
1148  try
1149  {
1150  // store anything thrown in the promise
1151  promise.set_exception(std::current_exception());
1152  }
1153  catch (...)
1154  {
1155  // set_exception() may throw too. But ignore this on
1156  // the task.
1157  }
1158  }
1159  }
1160  }
1161 
1170  Task() = default;
1171 
1203  void
1204  join() const
1205  {
1206  // Make sure we actually have a task that we can wait for.
1208 
1209  task_data->wait();
1210  }
1211 
1224  bool
1225  joinable() const
1226  {
1227  return (task_data != nullptr);
1228  }
1229 
1230 
1282  {
1283  // Make sure we actually have a task that we can wait for.
1285 
1286  // Then return the promised object. If necessary, wait for the promise to
1287  // be set.
1288  return task_data->get();
1289  }
1290 
1291 
1301  "The current object is not associated with a task that "
1302  "can be joined. It may have been detached, or you "
1303  "may have already joined it in the past.");
1305  private:
1314  class TaskData
1315  {
1316  public:
1321  TaskData(std::future<RT> &&future) noexcept
1322  : future(std::move(future))
1323  , task_has_finished(false)
1324  {}
1325 
1330  TaskData(const TaskData &) = delete;
1331 
1336  TaskData(TaskData &&) = delete;
1337 
1342  TaskData &
1343  operator=(const TaskData &) = delete;
1344 
1349  TaskData &
1350  operator=(TaskData &&) = delete;
1351 
1359  ~TaskData() noexcept
1360  {
1361  // Explicitly wait for the results to be ready. This class stores
1362  // a std::future object, and we could just let the compiler generate
1363  // the destructor which would then call the destructor of std::future
1364  // which *may* block until the future is ready. As explained in
1365  // https://en.cppreference.com/w/cpp/thread/future/~future
1366  // this is only a *may*, not a *must*. (The standard does not
1367  // appear to say anything about it at all.) As a consequence,
1368  // let's be explicit about waiting.
1369  //
1370  // One of the corner cases we have to worry about is that if a task
1371  // ends by throwing an exception, then wait() will re-throw that
1372  // exception on the thread that calls it, the first time around
1373  // someone calls wait() (or the return_value() function of the
1374  // surrounding class). So if we get to this constructor and an exception
1375  // is thrown by wait(), then that means that the last Task object
1376  // referring to a task is going out of scope with nobody having
1377  // ever checked the return value of the task itself. In that case,
1378  // one could argue that they would also not have cared about whether
1379  // an exception is thrown, and that we should simply ignore the
1380  // exception. This is what we do here. It is also the simplest solution,
1381  // because we don't know what one should do with the exception to begin
1382  // with: destructors aren't allowed to throw exceptions, so we can't
1383  // just rethrow it here if one had been triggered.
1384  try
1385  {
1386  wait();
1387  }
1388  catch (...)
1389  {}
1390  }
1391 
1397  void
1399  {
1400  // If we have previously already moved the result, then we don't
1401  // need a lock and can just return.
1402  if (task_has_finished)
1403  return;
1404 
1405  // Else, we need to go under a lock and try again. A different thread
1406  // may have waited and finished the task since then, so we have to try
1407  // a second time. (This is Schmidt's double-checking pattern.)
1408  std::lock_guard<std::mutex> lock(mutex);
1409  if (task_has_finished)
1410  return;
1411  else
1412  {
1413 # ifdef DEAL_II_WITH_TBB
1414  // If we build on the TBB, then we can't just wait for the
1415  // std::future object to get ready. Apparently the TBB happily
1416  // enqueues a task into an arena and then just sits on it without
1417  // ever executing it unless someone expresses an interest in the
1418  // task. The way to avoid this is to add the task to a
1419  // tbb::task_group, and then here wait for the single task
1420  // associated with that task group.
1421  task_group.wait();
1422 # endif
1423 
1424  // Wait for the task to finish and then move its
1425  // result. (We could have made the set_from() function
1426  // that we call here wait for the future to be ready --
1427  // which happens implicitly when it calls future.get() --
1428  // but that would have required putting an explicit
1429  // future.wait() into the implementation of
1430  // internal::return_value<void>::set_from(), which is a
1431  // bit awkward: that class doesn't actually need to set
1432  // anything, and so it looks odd to have the explicit call
1433  // to future.wait() in the set_from() function. Avoid the
1434  // issue by just explicitly calling future.wait() here.)
1435  future.wait();
1436  returned_object.set_from(future);
1437 
1438  // Now we can safely set the flag and return.
1439  task_has_finished = true;
1440  }
1441  }
1442 
1443 
1444 
1447  {
1448  wait();
1449  return returned_object.get();
1450  }
1451 
1452  private:
1457  std::mutex mutex;
1458 
1463  std::future<RT> future;
1464 
1483  std::atomic<bool> task_has_finished;
1484 
1490 
1491 # ifdef DEAL_II_WITH_TBB
1495  tbb::task_group task_group;
1496 
1497  friend class Task<RT>;
1498 # endif
1499  };
1500 
1505  std::shared_ptr<TaskData> task_data;
1506  };
1507 
1508 
1509 
1529  template <typename RT>
1530  inline Task<RT>
1531  new_task(const std::function<RT()> &function)
1532  {
1533  return Task<RT>(function);
1534  }
1535 
1536 
1537 
1613  template <typename FunctionObjectType>
1614  inline auto
1615  new_task(FunctionObjectType function_object)
1616  -> Task<decltype(function_object())>
1617  {
1618  using return_type = decltype(function_object());
1620  return new_task(std::function<return_type()>(function_object));
1621  }
1622 
1623 
1624 
1631  template <typename RT, typename... Args>
1632  inline Task<RT>
1633  new_task(RT (*fun_ptr)(Args...), typename identity<Args>::type... args)
1634  {
1635  auto dummy = std::make_tuple(internal::maybe_make_ref<Args>::act(args)...);
1636  return new_task(
1637  [dummy, fun_ptr]() -> RT { return std_cxx17::apply(fun_ptr, dummy); });
1638  }
1639 
1640 
1641 
1648  template <typename RT, typename C, typename... Args>
1649  inline Task<RT>
1650  new_task(RT (C::*fun_ptr)(Args...),
1651  typename identity<C>::type &c,
1652  typename identity<Args>::type... args)
1653  {
1654  // NOLINTNEXTLINE(modernize-avoid-bind) silence clang-tidy
1655  return new_task(std::function<RT()>(std::bind(
1656  fun_ptr, std::ref(c), internal::maybe_make_ref<Args>::act(args)...)));
1657  }
1658 
1665  template <typename RT, typename C, typename... Args>
1666  inline Task<RT>
1667  new_task(RT (C::*fun_ptr)(Args...) const,
1668  typename identity<const C>::type &c,
1669  typename identity<Args>::type... args)
1670  {
1671  // NOLINTNEXTLINE(modernize-avoid-bind) silence clang-tidy
1672  return new_task(std::function<RT()>(std::bind(
1673  fun_ptr, std::cref(c), internal::maybe_make_ref<Args>::act(args)...)));
1674  }
1675 
1676 
1677  // ------------------------ TaskGroup -------------------------------------
1678 
1691  template <typename RT = void>
1693  {
1694  public:
1698  TaskGroup &
1700  {
1701  tasks.push_back(t);
1702  return *this;
1703  }
1704 
1705 
1713  std::size_t
1714  size() const
1715  {
1716  return tasks.size();
1717  }
1718 
1719 
1726  void
1727  join_all() const
1728  {
1729  for (auto &t : tasks)
1730  t.join();
1731  }
1732 
1733  private:
1737  std::list<Task<RT>> tasks;
1738  };
1739 
1740 } // namespace Threads
1741 
1747 //---------------------------------------------------------------------------
1749 // end of #ifndef dealii_thread_management_h
1750 #endif
1751 //---------------------------------------------------------------------------
static void initialize_multithreading()
static unsigned int n_threads()
Mutex()=default
Mutex & operator=(const Mutex &)
Mutex(const Mutex &)
std::size_t size() const
std::list< Task< RT > > tasks
TaskGroup & operator+=(const Task< RT > &t)
TaskData(std::future< RT > &&future) noexcept
TaskData(const TaskData &)=delete
std::atomic< bool > task_has_finished
internal::return_value< RT > returned_object
TaskData & operator=(const TaskData &)=delete
TaskData(TaskData &&)=delete
TaskData & operator=(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()
void join() const
Task()=default
Task(const std::function< RT()> &function_object)
std::list< Thread< RT > > threads
ThreadGroup & operator+=(const Thread< RT > &t)
internal::return_value< RT >::reference_type return_value()
bool valid() const
Thread(const std::function< RT()> &function)
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:4605
static ::ExceptionBase & ExcNoTask()
static ::ExceptionBase & ExcInternalError()
#define Assert(cond, exc)
Definition: exceptions.h:1473
#define DeclExceptionMsg(Exception, defaulttext)
Definition: exceptions.h:487
#define AssertThrow(cond, exc)
Definition: exceptions.h:1583
Thread< RT > new_thread(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)
std::vector< std::pair< ForwardIterator, ForwardIterator > > split_range(const ForwardIterator &begin, const ForwardIterator &end, const unsigned int n_intervals)
Task< RT > new_task(const std::function< RT()> &function)
static const char T
SymmetricTensor< 2, dim, Number > C(const Tensor< 2, dim, Number > &F)
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 * begin(VectorType &V)
VectorType::value_type * end(VectorType &V)
auto apply(F &&fn, Tuple &&t) -> decltype(apply_impl(std::forward< F >(fn), std::forward< Tuple >(t), std::make_index_sequence< std::tuple_size< typename std::remove_reference< Tuple >::type >::value >()))
Definition: tuple.h:36
std::shared_ptr< return_value< RT > > ret_val
static void thread_entry_point(const std::function< RT()> &function, std::shared_ptr< return_value< RT >> ret_val)
void start(const std::function< RT()> &function)
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)
void advance(std::tuple< I1, I2 > &t, const unsigned int n)