Reference documentation for deal.II version GIT 5dcc62ab57 2022-07-04 21:05: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 
47 
48 
56 namespace 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 &
97  operator=(const Mutex &)
98  {
99  return *this;
100  }
101  };
102 } // namespace Threads
103 
104 
105 namespace 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
188 namespace 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 
240 namespace Threads
241 {
242  namespace internal
243  {
262  template <typename RT>
264  {
265  private:
266  RT value;
267 
268  public:
269  using reference_type = RT &;
270 
271  inline return_value()
272  : value()
273  {}
274 
275  inline reference_type
276  get()
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 
323  inline return_value()
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
371  get()
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 
607  Thread(const Thread<RT> &t)
609  {}
610 
616  void
617  join() const
618  {
619  if (thread_descriptor)
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:
932  ThreadGroup &
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  {
1034  if (MultithreadInfo::n_threads() > 1)
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:
1313  class TaskData
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.
1401  if (task_has_finished)
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);
1408  if (task_has_finished)
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)
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()
TaskData(std::future< RT > &&future) noexcept
TaskData(const TaskData &)=delete
std::shared_ptr< TaskData > task_data
#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=(const TaskData &)=delete
std::list< Task< RT > > tasks
#define DeclExceptionMsg(Exception, defaulttext)
Definition: exceptions.h:487
TaskData(TaskData &&)=delete
TaskGroup & operator+=(const Task< RT > &t)
TaskData & operator=(TaskData &&)=delete
internal::return_value< RT >::reference_type get()
#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)