Reference documentation for deal.II version GIT 194dd8bb02 2022-12-03 08:20: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 
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_WITH_TBB
39 # include <tbb/task_group.h>
41 #endif
42 
44 
57 namespace Threads
58 {
73  template <typename ForwardIterator>
74  std::vector<std::pair<ForwardIterator, ForwardIterator>>
75  split_range(const ForwardIterator &begin,
76  const ForwardIterator &end,
77  const unsigned int n_intervals);
78 
87  std::vector<std::pair<unsigned int, unsigned int>>
88  split_interval(const unsigned int begin,
89  const unsigned int end,
90  const unsigned int n_intervals);
91 
101  namespace internal
102  {
118  [[noreturn]] void
119  handle_std_exception(const std::exception &exc);
120 
128  [[noreturn]] void
130  } // namespace internal
131 
136 } // namespace Threads
137 
138 /* ----------- implementation of functions in namespace Threads ---------- */
139 #ifndef DOXYGEN
140 namespace Threads
141 {
142  template <typename ForwardIterator>
143  std::vector<std::pair<ForwardIterator, ForwardIterator>>
144  split_range(const ForwardIterator &begin,
145  const ForwardIterator &end,
146  const unsigned int n_intervals)
147  {
148  using IteratorPair = std::pair<ForwardIterator, ForwardIterator>;
149 
150  // in non-multithreaded mode, we often have the case that this
151  // function is called with n_intervals==1, so have a shortcut here
152  // to handle that case efficiently
153 
154  if (n_intervals == 1)
155  return (std::vector<IteratorPair>(1, IteratorPair(begin, end)));
156 
157  // if more than one interval requested, do the full work
158  const unsigned int n_elements = std::distance(begin, end);
159  const unsigned int n_elements_per_interval = n_elements / n_intervals;
160  const unsigned int residual = n_elements % n_intervals;
161 
162  std::vector<IteratorPair> return_values(n_intervals);
163 
164  return_values[0].first = begin;
165  for (unsigned int i = 0; i < n_intervals; ++i)
166  {
167  if (i != n_intervals - 1)
168  {
169  return_values[i].second = return_values[i].first;
170  // note: the cast is performed to avoid a warning of gcc
171  // that in the library `dist>=0' is checked (dist has a
172  // template type, which here is unsigned if no cast is
173  // performed)
174  std::advance(return_values[i].second,
175  static_cast<signed int>(n_elements_per_interval));
176  // distribute residual in division equally among the first
177  // few subintervals
178  if (i < residual)
179  ++return_values[i].second;
180 
181  return_values[i + 1].first = return_values[i].second;
182  }
183  else
184  return_values[i].second = end;
185  }
186  return return_values;
187  }
188 } // namespace Threads
189 
190 #endif // DOXYGEN
191 
192 namespace Threads
193 {
194  namespace internal
195  {
214  template <typename RT>
216  {
217  private:
218  RT value;
219 
220  public:
221  using reference_type = RT &;
222 
223  inline return_value()
224  : value()
225  {}
226 
227  inline reference_type
228  get()
229  {
230  return value;
231  }
232 
233  inline void
234  set(RT &&v)
235  {
236  value = std::move(v);
237  }
238 
239  inline void
240  set_from(std::future<RT> &v)
241  {
242  value = std::move(v.get());
243  }
244  };
245 
246 
266  template <typename RT>
267  struct return_value<RT &>
268  {
269  private:
270  RT *value;
271 
272  public:
273  using reference_type = RT &;
274 
275  inline return_value()
276  : value(nullptr)
277  {}
278 
279  inline reference_type
280  get() const
281  {
282  return *value;
283  }
284 
285  inline void
286  set(RT &v)
287  {
288  value = &v;
289  }
290 
291  inline void
292  set_from(std::future<RT &> &v)
293  {
294  value = &v.get();
295  }
296  };
297 
298 
317  template <>
318  struct return_value<void>
319  {
320  using reference_type = void;
321 
322  static inline void
323  get()
324  {}
325 
326 
327  inline void
328  set_from(std::future<void> &)
329  {}
330  };
331  } // namespace internal
332 
333 
334 
335  namespace internal
336  {
337  template <typename RT>
338  inline void
339  call(const std::function<RT()> & function,
341  {
342  ret_val.set(function());
343  }
344 
345 
346  inline void
347  call(const std::function<void()> &function, internal::return_value<void> &)
348  {
349  function();
350  }
351  } // namespace internal
352 
353 
354 
355  namespace internal
356  {
367  template <typename RT>
369  {
373  std::thread thread;
374 
383  std::shared_ptr<return_value<RT>> ret_val;
384 
420  std::atomic<bool> thread_is_active;
421 
426 
431  : thread_is_active(false)
432  {}
433 
435  {
436  if (!thread_is_active)
437  return;
438  thread.detach();
439  thread_is_active = false;
440  }
441 
446  void
447  start(const std::function<RT()> &function)
448  {
449  thread_is_active = true;
450  ret_val = std::make_shared<return_value<RT>>();
451  thread = std::thread(thread_entry_point, function, ret_val);
452  }
453 
454 
458  void
460  {
461  // see if the thread hasn't been joined yet. if it has, then
462  // join() is a no-op. use schmidt's double-checking strategy
463  // to use the mutex only when necessary
464  if (thread_is_active == false)
465  return;
466 
467  std::lock_guard<std::mutex> lock(thread_is_active_mutex);
468  if (thread_is_active == true)
469  {
470  Assert(thread.joinable(), ExcInternalError());
471  thread.join();
472  thread_is_active = false;
473  }
474  }
475 
476  private:
480  static void
481  thread_entry_point(const std::function<RT()> & function,
482  std::shared_ptr<return_value<RT>> ret_val)
483  {
484  // call the function in question. since an exception that is
485  // thrown from one of the called functions will not propagate
486  // to the main thread, it will kill the program if not treated
487  // here before we return to the operating system's thread
488  // library
489  try
490  {
491  call(function, *ret_val);
492  }
493  catch (const std::exception &exc)
494  {
496  }
497  catch (...)
498  {
500  }
501  }
502  };
503  } // namespace internal
504 
505 
532  template <typename RT = void>
533  class Thread
534  {
535  public:
540  Thread(const std::function<RT()> &function)
541  : thread_descriptor(new internal::ThreadDescriptor<RT>())
542  {
543  // in a second step, start the thread.
544  thread_descriptor->start(function);
545  }
546 
553  Thread() = default;
554 
559  Thread(const Thread<RT> &t)
561  {}
562 
568  void
569  join() const
570  {
571  if (thread_descriptor)
572  thread_descriptor->join();
573  }
574 
620  {
621  join();
622  return thread_descriptor->ret_val->get();
623  }
624 
629  bool
630  valid() const
631  {
632  return static_cast<bool>(thread_descriptor);
633  }
634 
635 
641  bool
642  operator==(const Thread &t) const
643  {
645  }
646 
647  private:
653  std::shared_ptr<internal::ThreadDescriptor<RT>> thread_descriptor;
654  };
655 
656 
657  namespace internal
658  {
666  template <typename T>
668  {
669  static T
670  act(T &t)
671  {
672  return t;
673  }
674  };
675 
676 
677 
685  template <typename T>
686  struct maybe_make_ref<T &>
687  {
688  static std::reference_wrapper<T>
689  act(T &t)
690  {
691  return std::ref(t);
692  }
693  };
694  } // namespace internal
695 
696 
697 
698  // ----------- thread starters for functions not taking any parameters
699 
710  template <typename RT>
711  DEAL_II_DEPRECATED inline Thread<RT>
712  new_thread(const std::function<RT()> &function)
713  {
714  // Here and below we need to disable deprecation warnings for calling the
715  // constructor in this function - as this function itself is deprecated
716  // these warnings are not helpful. This problem only appears in some
717  // configurations (e.g., Debian 11 with GCC-10).
719  return Thread<RT>(function);
721  }
722 
723 
724 
791  template <typename FunctionObjectType>
792  DEAL_II_DEPRECATED inline auto
793  new_thread(FunctionObjectType function_object)
794  -> Thread<decltype(function_object())>
795  {
796  // See the comment in the first new_thread() implementation
798  using return_type = decltype(function_object());
799  return Thread<return_type>(std::function<return_type()>(function_object));
801  }
802 
803 
804 
813  template <typename RT, typename... Args>
814  DEAL_II_DEPRECATED inline Thread<RT>
815  new_thread(RT (*fun_ptr)(Args...), typename identity<Args>::type... args)
816  {
817  // See the comment in the first new_thread() implementation
819  auto dummy = std::make_tuple(internal::maybe_make_ref<Args>::act(args)...);
820  return new_thread(
821  [dummy, fun_ptr]() -> RT { return std_cxx17::apply(fun_ptr, dummy); });
823  }
824 
825 
826 
834  template <typename RT, typename C, typename... Args>
835  DEAL_II_DEPRECATED inline Thread<RT>
836  new_thread(RT (C::*fun_ptr)(Args...),
837  typename identity<C>::type &c,
838  typename identity<Args>::type... args)
839  {
840  // NOLINTNEXTLINE(modernize-avoid-bind) silence clang-tidy
841  return new_thread(std::function<RT()>(std::bind(
842  fun_ptr, std::ref(c), internal::maybe_make_ref<Args>::act(args)...)));
843  }
844 
852  template <typename RT, typename C, typename... Args>
853  DEAL_II_DEPRECATED inline Thread<RT>
854  new_thread(RT (C::*fun_ptr)(Args...) const,
855  typename identity<const C>::type &c,
856  typename identity<Args>::type... args)
857  {
858  // See the comment in the first new_thread() implementation
860  // NOLINTNEXTLINE(modernize-avoid-bind) silence clang-tidy
861  return new_thread(std::function<RT()>(std::bind(
862  fun_ptr, std::cref(c), internal::maybe_make_ref<Args>::act(args)...)));
864  }
865 
866  // ------------------------ ThreadGroup -------------------------------------
867 
877  template <typename RT = void>
879  {
880  public:
884  ThreadGroup &
886  {
887  threads.push_back(t);
888  return *this;
889  }
890 
897  void
898  join_all() const
899  {
900  for (auto &t : threads)
901  t.join();
902  }
903 
904  private:
908  std::list<Thread<RT>> threads;
909  };
910 
911 
912  namespace internal
913  {
917  template <typename RT, typename Function>
918  void
919  evaluate_and_set_promise(Function &function, std::promise<RT> &promise)
920  {
921  promise.set_value(function());
922  }
923 
924 
932  template <typename Function>
933  void
934  evaluate_and_set_promise(Function &function, std::promise<void> &promise)
935  {
936  function();
937  promise.set_value();
938  }
939  } // namespace internal
940 
941 
942 
969  template <typename RT = void>
970  class Task
971  {
972  public:
984  Task(const std::function<RT()> &function_object)
985  {
986  if (MultithreadInfo::n_threads() > 1)
987  {
988 #ifdef DEAL_II_WITH_TBB
989  // Create a promise object and from it extract a future that
990  // we can use to refer to the outcome of the task. For reasons
991  // explained below, we can't just create a std::promise object,
992  // but have to make do with a pointer to such an object.
993  std::unique_ptr<std::promise<RT>> promise =
994  std::make_unique<std::promise<RT>>();
995  task_data =
996  std::make_shared<TaskData>(std::move(promise->get_future()));
997 
998  // Then start the task, using a task_group object (for just this one
999  // task) that is associated with the TaskData object. Note that we
1000  // have to *copy* the function object being executed so that it is
1001  // guaranteed to live on the called thread as well -- the copying is
1002  // facilitated by capturing the 'function_object' variable by value.
1003  //
1004  // We also have to *move* the promise object into the new task's
1005  // memory space because promises can not be copied and we can't refer
1006  // to it by reference because it's a local variable of the current
1007  // (surrounding) function that may go out of scope before the promise
1008  // is ultimately set. This leads to a conundrum: if we had just
1009  // declared 'promise' as an object of type std::promise, then we could
1010  // capture it in the lambda function via
1011  // [..., promise=std::move(promise)]() {...}
1012  // and set the promise in the body of the lambda. But setting a
1013  // promise is a non-const operation on the promise, and so we would
1014  // actually have to declare the lambda function as 'mutable' because
1015  // by default, lambda captures are 'const'. That is, we would have
1016  // to write
1017  // [..., promise=std::move(promise)]() mutable {...}
1018  // But this leads to other problems: It turns out that the
1019  // tbb::task_group::run() function cannot take mutable lambdas as
1020  // argument :-(
1021  //
1022  // We work around this issue by not declaring the 'promise' variable
1023  // as an object of type std::promise, but as a pointer to such an
1024  // object. This pointer we can move, and the *pointer* itself can
1025  // be 'const' (meaning we can leave the lambda as non-mutable)
1026  // even though we modify the object *pointed to*. One would think
1027  // that a std::unique_ptr would be the right choice for this, but
1028  // that's not true: the resulting lambda function can then be
1029  // non-mutable, but the lambda function object is not copyable
1030  // and at least some TBB variants require that as well. So
1031  // instead we move the std::unique_ptr used above into a
1032  // std::shared_ptr to be stored within the lambda function object.
1033  task_data->task_group.run(
1034  [function_object,
1035  promise =
1036  std::shared_ptr<std::promise<RT>>(std::move(promise))]() {
1037  try
1038  {
1039  internal::evaluate_and_set_promise(function_object, *promise);
1040  }
1041  catch (...)
1042  {
1043  try
1044  {
1045  // store anything thrown in the promise
1046  promise->set_exception(std::current_exception());
1047  }
1048  catch (...)
1049  {
1050  // set_exception() may throw too. But ignore this on
1051  // the task.
1052  }
1053  }
1054  });
1055 
1056 #else
1057  // If no threading library is supported, just fall back onto C++11
1058  // facilities. The problem with this is that the standard does
1059  // not actually say what std::async should do. The first
1060  // argument to that function can be std::launch::async or
1061  // std::launch::deferred, or both. The *intent* of the standard's
1062  // authors was probably that if one sets it to
1063  // std::launch::async | std::launch::deferred,
1064  // that the task is run in a thread pool. But at least as of
1065  // 2021, GCC doesn't do that: It just runs it on a new thread.
1066  // If one chooses std::launch::deferred, it runs the task on
1067  // the same thread but only when one calls join() on the task's
1068  // std::future object. In the former case, this leads to
1069  // oversubscription, in the latter case to undersubscription of
1070  // resources. We choose oversubscription here.
1071  //
1072  // The issue illustrates why relying on external libraries
1073  // with task schedulers is the way to go.
1074  task_data = std::make_shared<TaskData>(
1075  std::async(std::launch::async | std::launch::deferred,
1076  function_object));
1077 #endif
1078  }
1079  else
1080  {
1081  // Only one thread allowed. So let the task run to completion
1082  // and just emplace a 'ready' future.
1083  //
1084  // The design of std::promise/std::future is unclear, but it
1085  // seems that the intent is to obtain the std::future before
1086  // we set the std::promise. So create the TaskData object at
1087  // the top and then run the task and set the returned
1088  // value. Since everything here happens sequentially, it
1089  // really doesn't matter in which order all of this is
1090  // happening.
1091  std::promise<RT> promise;
1092  task_data = std::make_shared<TaskData>(promise.get_future());
1093  try
1094  {
1095  internal::evaluate_and_set_promise(function_object, promise);
1096  }
1097  catch (...)
1098  {
1099  try
1100  {
1101  // store anything thrown in the promise
1102  promise.set_exception(std::current_exception());
1103  }
1104  catch (...)
1105  {
1106  // set_exception() may throw too. But ignore this on
1107  // the task.
1108  }
1109  }
1110  }
1111  }
1112 
1121  Task() = default;
1122 
1154  void
1155  join() const
1156  {
1157  // Make sure we actually have a task that we can wait for.
1159 
1160  task_data->wait();
1161  }
1162 
1175  bool
1176  joinable() const
1177  {
1178  return (task_data != nullptr);
1179  }
1180 
1181 
1233  {
1234  // Make sure we actually have a task that we can wait for.
1236 
1237  // Then return the promised object. If necessary, wait for the promise to
1238  // be set.
1239  return task_data->get();
1240  }
1241 
1242 
1252  "The current object is not associated with a task that "
1253  "can be joined. It may have been detached, or you "
1254  "may have already joined it in the past.");
1256  private:
1265  class TaskData
1266  {
1267  public:
1272  TaskData(std::future<RT> &&future) noexcept
1273  : future(std::move(future))
1274  , task_has_finished(false)
1275  {}
1276 
1281  TaskData(const TaskData &) = delete;
1282 
1287  TaskData(TaskData &&) = delete;
1288 
1293  TaskData &
1294  operator=(const TaskData &) = delete;
1295 
1300  TaskData &
1301  operator=(TaskData &&) = delete;
1302 
1310  ~TaskData() noexcept
1311  {
1312  // Explicitly wait for the results to be ready. This class stores
1313  // a std::future object, and we could just let the compiler generate
1314  // the destructor which would then call the destructor of std::future
1315  // which *may* block until the future is ready. As explained in
1316  // https://en.cppreference.com/w/cpp/thread/future/~future
1317  // this is only a *may*, not a *must*. (The standard does not
1318  // appear to say anything about it at all.) As a consequence,
1319  // let's be explicit about waiting.
1320  //
1321  // One of the corner cases we have to worry about is that if a task
1322  // ends by throwing an exception, then wait() will re-throw that
1323  // exception on the thread that calls it, the first time around
1324  // someone calls wait() (or the return_value() function of the
1325  // surrounding class). So if we get to this constructor and an exception
1326  // is thrown by wait(), then that means that the last Task object
1327  // referring to a task is going out of scope with nobody having
1328  // ever checked the return value of the task itself. In that case,
1329  // one could argue that they would also not have cared about whether
1330  // an exception is thrown, and that we should simply ignore the
1331  // exception. This is what we do here. It is also the simplest solution,
1332  // because we don't know what one should do with the exception to begin
1333  // with: destructors aren't allowed to throw exceptions, so we can't
1334  // just rethrow it here if one had been triggered.
1335  try
1336  {
1337  wait();
1338  }
1339  catch (...)
1340  {}
1341  }
1342 
1348  void
1350  {
1351  // If we have previously already moved the result, then we don't
1352  // need a lock and can just return.
1353  if (task_has_finished)
1354  return;
1355 
1356  // Else, we need to go under a lock and try again. A different thread
1357  // may have waited and finished the task since then, so we have to try
1358  // a second time. (This is Schmidt's double-checking pattern.)
1359  std::lock_guard<std::mutex> lock(mutex);
1360  if (task_has_finished)
1361  return;
1362  else
1363  {
1364 #ifdef DEAL_II_WITH_TBB
1365  // If we build on the TBB, then we can't just wait for the
1366  // std::future object to get ready. Apparently the TBB happily
1367  // enqueues a task into an arena and then just sits on it without
1368  // ever executing it unless someone expresses an interest in the
1369  // task. The way to avoid this is to add the task to a
1370  // tbb::task_group, and then here wait for the single task
1371  // associated with that task group.
1372  task_group.wait();
1373 #endif
1374 
1375  // Wait for the task to finish and then move its
1376  // result. (We could have made the set_from() function
1377  // that we call here wait for the future to be ready --
1378  // which happens implicitly when it calls future.get() --
1379  // but that would have required putting an explicit
1380  // future.wait() into the implementation of
1381  // internal::return_value<void>::set_from(), which is a
1382  // bit awkward: that class doesn't actually need to set
1383  // anything, and so it looks odd to have the explicit call
1384  // to future.wait() in the set_from() function. Avoid the
1385  // issue by just explicitly calling future.wait() here.)
1386  future.wait();
1387  returned_object.set_from(future);
1388 
1389  // Now we can safely set the flag and return.
1390  task_has_finished = true;
1391  }
1392  }
1393 
1394 
1395 
1398  {
1399  wait();
1400  return returned_object.get();
1401  }
1402 
1403  private:
1408  std::mutex mutex;
1409 
1414  std::future<RT> future;
1415 
1434  std::atomic<bool> task_has_finished;
1435 
1441 
1442 #ifdef DEAL_II_WITH_TBB
1446  tbb::task_group task_group;
1447 
1448  friend class Task<RT>;
1449 #endif
1450  };
1451 
1456  std::shared_ptr<TaskData> task_data;
1457  };
1458 
1459 
1460 
1480  template <typename RT>
1481  inline Task<RT>
1482  new_task(const std::function<RT()> &function)
1483  {
1484  return Task<RT>(function);
1485  }
1486 
1487 
1488 
1564  template <typename FunctionObjectType>
1565  inline auto
1566  new_task(FunctionObjectType function_object)
1567  -> Task<decltype(function_object())>
1568  {
1569  using return_type = decltype(function_object());
1571  return new_task(std::function<return_type()>(function_object));
1572  }
1573 
1574 
1575 
1582  template <typename RT, typename... Args>
1583  inline Task<RT>
1584  new_task(RT (*fun_ptr)(Args...), typename identity<Args>::type... args)
1585  {
1586  auto dummy = std::make_tuple(internal::maybe_make_ref<Args>::act(args)...);
1587  return new_task(
1588  [dummy, fun_ptr]() -> RT { return std_cxx17::apply(fun_ptr, dummy); });
1589  }
1590 
1591 
1592 
1599  template <typename RT, typename C, typename... Args>
1600  inline Task<RT>
1601  new_task(RT (C::*fun_ptr)(Args...),
1602  typename identity<C>::type &c,
1603  typename identity<Args>::type... args)
1604  {
1605  // NOLINTNEXTLINE(modernize-avoid-bind) silence clang-tidy
1606  return new_task(std::function<RT()>(std::bind(
1607  fun_ptr, std::ref(c), internal::maybe_make_ref<Args>::act(args)...)));
1608  }
1609 
1616  template <typename RT, typename C, typename... Args>
1617  inline Task<RT>
1618  new_task(RT (C::*fun_ptr)(Args...) const,
1619  typename identity<const C>::type &c,
1620  typename identity<Args>::type... args)
1621  {
1622  // NOLINTNEXTLINE(modernize-avoid-bind) silence clang-tidy
1623  return new_task(std::function<RT()>(std::bind(
1624  fun_ptr, std::cref(c), internal::maybe_make_ref<Args>::act(args)...)));
1625  }
1626 
1627 
1628  // ------------------------ TaskGroup -------------------------------------
1629 
1642  template <typename RT = void>
1644  {
1645  public:
1649  TaskGroup &
1651  {
1652  tasks.push_back(t);
1653  return *this;
1654  }
1655 
1656 
1664  std::size_t
1665  size() const
1666  {
1667  return tasks.size();
1668  }
1669 
1684  std::vector<RT>
1686  {
1687  std::vector<RT> results;
1688  results.reserve(size());
1689  for (auto &t : tasks)
1690  results.emplace_back(std::move(t.return_value()));
1691  return results;
1692  }
1693 
1694 
1701  void
1702  join_all() const
1703  {
1704  for (auto &t : tasks)
1705  t.join();
1706  }
1707 
1708  private:
1712  std::list<Task<RT>> tasks;
1713  };
1714 
1715 } // namespace Threads
1716 
1723 #endif
static void initialize_multithreading()
static unsigned int n_threads()
std::size_t size() const
std::list< Task< RT > > tasks
std::vector< RT > return_values()
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:458
#define DEAL_II_DISABLE_EXTRA_DIAGNOSTICS
Definition: config.h:472
#define DEAL_II_NAMESPACE_CLOSE
Definition: config.h:459
#define DEAL_II_ENABLE_EXTRA_DIAGNOSTICS
Definition: config.h:512
Point< 2 > second
Definition: grid_out.cc:4606
static ::ExceptionBase & ExcNoTask()
static ::ExceptionBase & ExcInternalError()
#define Assert(cond, exc)
Definition: exceptions.h:1501
#define DeclExceptionMsg(Exception, defaulttext)
Definition: exceptions.h:487
#define AssertThrow(cond, exc)
Definition: exceptions.h:1611
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)
Definition: mutex.h:32
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)