Reference documentation for deal.II version 9.0.0
thread_management.h
1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 2000 - 2018 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 at
12 // the top level of the deal.II distribution.
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 #include <deal.II/base/exceptions.h>
22 #include <deal.II/base/template_constraints.h>
23 #include <deal.II/base/multithread_info.h>
24 
25 #ifdef DEAL_II_WITH_THREADS
26 # include <thread>
27 # include <mutex>
28 # include <condition_variable>
29 #endif
30 
31 #include <iterator>
32 #include <vector>
33 #include <list>
34 #include <utility>
35 #include <functional>
36 #include <memory>
37 #include <tuple>
38 
39 
40 #ifdef DEAL_II_WITH_THREADS
41 # ifdef DEAL_II_USE_MT_POSIX
42 # include <pthread.h>
43 # endif
44 # include <tbb/task.h>
45 # include <tbb/tbb_stddef.h>
46 #endif
47 
48 
49 
50 DEAL_II_NAMESPACE_OPEN
51 
54 
55 
63 namespace Threads
64 {
79  {
80  public:
95  class ScopedLock
96  {
97  public:
103 
110  };
111 
116  inline void acquire () const {}
117 
122  inline void release () const {}
123  };
124 
125 
126 
145  {
146  public:
152  inline void signal () const {}
153 
159  inline void broadcast () const {}
160 
168  inline void wait (DummyThreadMutex &) const {}
169  };
170 
171 
172 
189  {
190  public:
196  DummyBarrier (const unsigned int count,
197  const char *name = nullptr,
198  void *arg = nullptr);
199 
204  inline int wait () const
205  {
206  return 0;
207  }
208 
212  inline void dump () const {}
213 
223  int,
224  << "In single-thread mode, barrier sizes other than 1 are not "
225  << "useful. You gave " << arg1 << ".");
226 
228  };
229 
230 
231 #ifdef DEAL_II_WITH_THREADS
232 
249  class Mutex
250  {
251  public:
267  {
268  public:
273  {
274  mutex.acquire();
275  }
276 
282  {
283  mutex.release ();
284  }
285 
286  private:
291  };
292 
296  Mutex () = default;
297 
302  Mutex (const Mutex &)
303  :
304  mutex()
305  {}
306 
307 
313  {
314  return *this;
315  }
316 
317 
321  inline void acquire ()
322  {
323  mutex.lock();
324  }
325 
329  inline void release ()
330  {
331  mutex.unlock();
332  }
333 
334  private:
338  std::mutex mutex;
339 
344  friend class ConditionVariable;
345  };
346 
347 
355  {
356  public:
361  inline void signal ()
362  {
363  condition_variable.notify_one();
364  }
365 
370  inline void broadcast ()
371  {
372  condition_variable.notify_all();
373  }
374 
384  inline void wait (Mutex &mutex)
385  {
386  std::unique_lock<std::mutex> lock(mutex.mutex, std::adopt_lock);
387  condition_variable.wait (lock);
388  }
389 
390  private:
394  std::condition_variable condition_variable;
395  };
396 
397 
414  {
415  public:
419  PosixThreadBarrier (const unsigned int count,
420  const char *name = nullptr,
421  void *arg = nullptr);
422 
427 
434  int wait ();
435 
436  private:
441 #ifndef DEAL_II_USE_MT_POSIX_NO_BARRIERS
442  pthread_barrier_t barrier;
443 #else
444  unsigned int count;
445 #endif
446  };
447 
448 
454 
455 #else
456 
461  typedef DummyThreadMutex Mutex;
462 
469 
475  typedef DummyBarrier Barrier;
476 #endif
477 
478 }
479 
480 
481 namespace Threads
482 {
483 
513  unsigned int n_existing_threads ();
514 
526  unsigned int this_thread_id ();
527 
542  template <typename ForwardIterator>
543  std::vector<std::pair<ForwardIterator,ForwardIterator> >
544  split_range (const ForwardIterator &begin,
545  const ForwardIterator &end,
546  const unsigned int n_intervals);
547 
556  std::vector<std::pair<unsigned int,unsigned int> >
557  split_interval (const unsigned int begin,
558  const unsigned int end,
559  const unsigned int n_intervals);
560 
572  namespace internal
573  {
589  [[noreturn]]
590  void handle_std_exception (const std::exception &exc);
591 
599  [[noreturn]]
600  void handle_unknown_exception ();
601 
609  void register_thread ();
610 
618  void deregister_thread ();
619  }
620 
625 } // end declarations of namespace Threads
626 
627 /* ----------- implementation of functions in namespace Threads ---------- */
628 #ifndef DOXYGEN
629 namespace Threads
630 {
631  template <typename ForwardIterator>
632  std::vector<std::pair<ForwardIterator,ForwardIterator> >
633  split_range (const ForwardIterator &begin,
634  const ForwardIterator &end,
635  const unsigned int n_intervals)
636  {
637  typedef std::pair<ForwardIterator,ForwardIterator> IteratorPair;
638 
639  // in non-multithreaded mode, we often have the case that this
640  // function is called with n_intervals==1, so have a shortcut here
641  // to handle that case efficiently
642 
643  if (n_intervals==1)
644  return (std::vector<IteratorPair>
645  (1, IteratorPair(begin, end)));
646 
647  // if more than one interval requested, do the full work
648  const unsigned int n_elements = std::distance (begin, end);
649  const unsigned int n_elements_per_interval = n_elements / n_intervals;
650  const unsigned int residual = n_elements % n_intervals;
651 
652  std::vector<IteratorPair> return_values (n_intervals);
653 
654  return_values[0].first = begin;
655  for (unsigned int i=0; i<n_intervals; ++i)
656  {
657  if (i != n_intervals-1)
658  {
659  return_values[i].second = return_values[i].first;
660  // note: the cast is performed to avoid a warning of gcc
661  // that in the library `dist>=0' is checked (dist has a
662  // template type, which here is unsigned if no cast is
663  // performed)
664  std::advance (return_values[i].second,
665  static_cast<signed int>(n_elements_per_interval));
666  // distribute residual in division equally among the first
667  // few subintervals
668  if (i < residual)
669  ++return_values[i].second;
670 
671  return_values[i+1].first = return_values[i].second;
672  }
673  else
674  return_values[i].second = end;
675  }
676  return return_values;
677  }
678 }
679 
680 #endif // DOXYGEN
681 
682 namespace Threads
683 {
684  namespace internal
685  {
694  template <typename RT> struct return_value
695  {
696  private:
697  RT value;
698  public:
699  typedef RT &reference_type;
700 
701  inline return_value () : value() {}
702 
703  inline reference_type get ()
704  {
705  return value;
706  }
707 
708  inline void set (RT &&v)
709  {
710  value = std::move(v);
711  }
712  };
713 
714 
724  template <typename RT> struct return_value<RT &>
725  {
726  private:
727  RT *value;
728  public:
729  typedef RT &reference_type;
730 
731  inline return_value ()
732  : value(nullptr)
733  {}
734 
735  inline reference_type get () const
736  {
737  return *value;
738  }
739 
740  inline void set (RT &v)
741  {
742  value = &v;
743  }
744  };
745 
746 
755  template <> struct return_value<void>
756  {
757  typedef void reference_type;
758 
759  static inline void get () {}
760  };
761  }
762 
763 
764 
765  namespace internal
766  {
767  template <typename RT>
768  inline void call (const std::function<RT ()> &function,
769  internal::return_value<RT> &ret_val)
770  {
771  ret_val.set (function());
772  }
773 
774 
775  inline void call (const std::function<void ()> &function,
776  internal::return_value<void> &)
777  {
778  function();
779  }
780  }
781 
782 
783 
784  namespace internal
785  {
786 #ifdef DEAL_II_WITH_THREADS
787 
798  template <typename RT>
800  {
804  std::thread thread;
805 
814  std::shared_ptr<return_value<RT> > ret_val;
815 
848 
853 
858  :
859  thread_is_active (false)
860  {}
861 
862  ~ThreadDescriptor ()
863  {
864  if (!thread_is_active)
865  return;
866  thread.detach();
867  thread_is_active = false;
868  }
869 
874  void start (const std::function<RT ()> &function)
875  {
876  thread_is_active = true;
877  ret_val = std::make_shared<return_value<RT>>();
878  thread = std::thread (thread_entry_point, function, ret_val);
879  }
880 
881 
885  void join ()
886  {
887  // see if the thread hasn't been joined yet. if it has, then
888  // join() is a no-op. use schmidt's double-checking strategy
889  // to use the mutex only when necessary
890  if (thread_is_active == false)
891  return;
892 
894  if (thread_is_active == true)
895  {
896  Assert (thread.joinable(), ExcInternalError());
897  thread.join ();
898  thread_is_active = false;
899  }
900  }
901 
902  private:
903 
907  static
908  void thread_entry_point (const std::function<RT ()> &function,
909  std::shared_ptr<return_value<RT> > ret_val)
910  {
911  // call the function in question. since an exception that is
912  // thrown from one of the called functions will not propagate
913  // to the main thread, it will kill the program if not treated
914  // here before we return to the operating system's thread
915  // library
916  internal::register_thread ();
917  try
918  {
919  call (function, *ret_val);
920  }
921  catch (const std::exception &exc)
922  {
923  internal::handle_std_exception (exc);
924  }
925  catch (...)
926  {
927  internal::handle_unknown_exception ();
928  }
929  internal::deregister_thread ();
930  }
931  };
932 
933 #else
934 
942  template <typename RT>
943  struct ThreadDescriptor
944  {
949  std::shared_ptr<return_value<RT> > ret_val;
950 
955  void start (const std::function<RT ()> &function)
956  {
957  ret_val = std::make_shared<return_value<RT>>();
958  call (function, *ret_val);
959  }
960 
964  void join ()
965  {}
966  };
967 
968 #endif
969  }
970 
971 
994  template <typename RT = void>
995  class Thread
996  {
997  public:
1001  Thread (const std::function<RT ()> &function)
1002  :
1003  thread_descriptor (new internal::ThreadDescriptor<RT>())
1004  {
1005  // in a second step, start the thread.
1006  thread_descriptor->start (function);
1007  }
1008 
1014  Thread () = default;
1015 
1019  Thread (const Thread<RT> &t)
1020  :
1022  {}
1023 
1029  void join () const
1030  {
1031  if (thread_descriptor)
1032  thread_descriptor->join ();
1033  }
1034 
1074  typename internal::return_value<RT>::reference_type
1076  {
1077  join ();
1078  return thread_descriptor->ret_val->get();
1079  }
1080 
1085  bool valid () const
1086  {
1087  return static_cast<bool>(thread_descriptor);
1088  }
1089 
1090 
1096  bool operator == (const Thread &t) const
1097  {
1099  }
1100 
1101  private:
1107  std::shared_ptr<internal::ThreadDescriptor<RT> > thread_descriptor;
1108  };
1109 
1110 
1111  namespace internal
1112  {
1120  template <typename T>
1122  {
1123  static T act (T &t)
1124  {
1125  return t;
1126  }
1127  };
1128 
1129 
1130 
1138  template <typename T>
1139  struct maybe_make_ref<T &>
1140  {
1141  static std::reference_wrapper<T> act (T &t)
1142  {
1143  return std::ref(t);
1144  }
1145  };
1146  }
1147 
1148 
1149 
1150 // ----------- thread starters for functions not taking any parameters
1151 
1160  template <typename RT>
1161  inline
1162  Thread<RT>
1163  new_thread (const std::function<RT ()> &function)
1164  {
1165  return Thread<RT>(function);
1166  }
1167 
1168 
1169 
1233  template <typename FunctionObjectType>
1234  inline
1235  auto
1236  new_thread (FunctionObjectType function_object)
1238  {
1239  typedef decltype(function_object()) return_type;
1240  return Thread<return_type>(std::function<return_type ()>(function_object));
1241  }
1242 
1243 
1244 
1251  template <typename RT, typename... Args>
1252  inline
1253  Thread<RT>
1254  new_thread (RT (*fun_ptr)(Args...),
1255  typename identity<Args>::type... args)
1256  {
1257  return
1258  new_thread (std::function<RT ()>
1259  (std::bind(fun_ptr,
1261  }
1262 
1263 
1264 
1270  template <typename RT, typename C, typename... Args>
1271  inline
1272  Thread<RT>
1273  new_thread (RT (C::*fun_ptr)(Args...),
1274  typename identity<C>::type &c,
1275  typename identity<Args>::type... args)
1276  {
1277  return
1278  new_thread (std::function<RT ()>
1279  (std::bind(fun_ptr, std::ref(c),
1281  }
1282 
1283 #ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG
1284 
1289  template <typename RT, typename C, typename... Args>
1290  inline
1291  Thread<RT>
1292  new_thread (RT (C::*fun_ptr)(Args...) const,
1293  typename identity<const C>::type &c,
1294  typename identity<Args>::type... args)
1295  {
1296  return
1297  new_thread (std::function<RT ()>
1298  (std::bind(fun_ptr, std::cref(c),
1300  }
1301 #endif
1302 
1303 // ------------------------ ThreadGroup -------------------------------------
1304 
1313  template <typename RT = void>
1315  {
1316  public:
1321  {
1322  threads.push_back (t);
1323  return *this;
1324  }
1325 
1332  void join_all () const
1333  {
1334  for (typename std::list<Thread<RT> >::const_iterator
1335  t=threads.begin(); t!=threads.end(); ++t)
1336  t->join ();
1337  }
1338 
1339  private:
1343  std::list<Thread<RT> > threads;
1344  };
1345 
1346 
1347  template <typename> class Task;
1348 
1349 
1350  namespace internal
1351  {
1352 #ifdef DEAL_II_WITH_THREADS
1353 
1354  template <typename> struct TaskDescriptor;
1355 
1359  template <typename RT>
1360  struct TaskEntryPoint : public tbb::task
1361  {
1362  TaskEntryPoint (TaskDescriptor<RT> &task_descriptor)
1363  :
1365  {}
1366 
1367  virtual tbb::task *execute ()
1368  {
1369  // call the function object and put the return value into the
1370  // proper place
1371  try
1372  {
1373  call (task_descriptor.function, task_descriptor.ret_val);
1374  }
1375  catch (const std::exception &exc)
1376  {
1377  internal::handle_std_exception (exc);
1378  }
1379  catch (...)
1380  {
1381  internal::handle_unknown_exception ();
1382  }
1383  return nullptr;
1384  }
1385 
1389  TaskDescriptor<RT> &task_descriptor;
1390  };
1391 
1413  template <typename RT>
1414  struct TaskDescriptor
1415  {
1416  private:
1420  std::function<RT ()> function;
1421 
1430  tbb::task *task;
1431 
1435  return_value<RT> ret_val;
1436 
1440  bool task_is_done;
1441 
1442  public:
1443 
1447  TaskDescriptor (const std::function<RT ()> &function);
1448 
1454  TaskDescriptor ();
1455 
1460  TaskDescriptor (const TaskDescriptor &);
1461 
1465  ~TaskDescriptor ();
1466 
1471  TaskDescriptor &
1472  operator = (const TaskDescriptor &);
1473 
1480  void queue_task ();
1481 
1487  void join ();
1488 
1489 
1490  template <typename> friend struct TaskEntryPoint;
1491  friend class ::Threads::Task<RT>;
1492  };
1493 
1494 
1495 
1496  template <typename RT>
1497  inline
1498  TaskDescriptor<RT>::TaskDescriptor (const std::function<RT ()> &function)
1499  :
1500  function (function),
1501  task(nullptr),
1502  task_is_done (false)
1503  {}
1504 
1505 
1506  template <typename RT>
1507  inline
1508  void
1509  TaskDescriptor<RT>::queue_task ()
1510  {
1511  // use the pattern described in the TBB book on pages 230/231
1512  // ("Start a large task in parallel with the main program")
1513  task = new (tbb::task::allocate_root()) tbb::empty_task;
1514  task->set_ref_count (2);
1515 
1516  tbb::task *worker = new (task->allocate_child()) TaskEntryPoint<RT>(*this);
1517 
1518  // in earlier versions of the TBB, task::spawn was a regular
1519  // member function; however, in later versions, it was converted
1520  // into a static function. we could always call it as a regular member
1521  // function of *task, but that appears to confuse the NVidia nvcc
1522  // compiler. consequently, the following work-around:
1523 #if TBB_VERSION_MAJOR >= 4
1524  tbb::task::spawn (*worker);
1525 #else
1526  task->spawn (*worker);
1527 #endif
1528  }
1529 
1530 
1531 
1532  template <typename RT>
1533  TaskDescriptor<RT>::TaskDescriptor ()
1534  :
1535  task_is_done (false)
1536  {
1537  Assert (false, ExcInternalError());
1538  }
1539 
1540 
1541 
1542  template <typename RT>
1543  TaskDescriptor<RT>::TaskDescriptor (const TaskDescriptor &)
1544  :
1545  task_is_done (false)
1546  {
1547  // we shouldn't be getting here -- task descriptors
1548  // can't be copied
1549  Assert (false, ExcInternalError());
1550  }
1551 
1552 
1553 
1554  template <typename RT>
1555  inline
1556  TaskDescriptor<RT>::~TaskDescriptor ()
1557  {
1558  // wait for the task to complete for sure
1559  join ();
1560 
1561  // now destroy the empty task structure. the book recommends to
1562  // spawn it as well and let the scheduler destroy the object
1563  // when done, but this has the disadvantage that the scheduler
1564  // may not get to actually finishing the task before it goes out
1565  // of scope (at the end of the program, or if a thread is done
1566  // on which it was run) and then we would get a hard-to-decipher
1567  // warning about unfinished tasks when the scheduler "goes out
1568  // of the arena". rather, let's explicitly destroy the empty
1569  // task object. before that, make sure that the task has been
1570  // shut down, expressed by a zero reference count
1571  AssertNothrow (task != nullptr, ExcInternalError());
1572  AssertNothrow (task->ref_count()==0, ExcInternalError());
1573  task->destroy (*task);
1574  }
1575 
1576 
1577  template <typename RT>
1578  TaskDescriptor<RT> &
1579  TaskDescriptor<RT>::operator = (const TaskDescriptor &)
1580  {
1581  // we shouldn't be getting here -- task descriptors
1582  // can't be copied
1583  Assert (false, ExcInternalError());
1584  return *this;
1585  }
1586 
1587 
1588  template <typename RT>
1589  inline
1590  void
1591  TaskDescriptor<RT>::join ()
1592  {
1593  // if the task is already done, just return. this makes sure we
1594  // call tbb::Task::wait_for_all() exactly once, as required by
1595  // TBB. we could also get the reference count of task for doing
1596  // this, but that is usually slower. note that this does not
1597  // work when the thread calling this function is not the same as
1598  // the one that initialized the task.
1599  //
1600  // TODO: can we assert that no other thread tries to end the
1601  // task?
1602  if (task_is_done == true)
1603  return;
1604 
1605  // let TBB wait for the task to complete.
1606  task_is_done = true;
1607  task->wait_for_all();
1608  }
1609 
1610 
1611 
1612 #else // no threading enabled
1613 
1618  template <typename RT>
1619  struct TaskDescriptor
1620  {
1624  return_value<RT> ret_val;
1625 
1630  TaskDescriptor (const std::function<RT ()> &function)
1631  {
1632  call (function, ret_val);
1633  }
1634 
1639  static void join () {}
1640 
1645  static void queue_task () {}
1646  };
1647 
1648 #endif
1649 
1650  }
1651 
1652 
1653 
1664  template <typename RT = void>
1665  class Task
1666  {
1667  public:
1675  Task (const std::function<RT ()> &function_object)
1676  {
1677  // create a task descriptor and tell it to queue itself up with
1678  // the scheduling system
1679  task_descriptor = std::make_shared<internal::TaskDescriptor<RT>>(function_object);
1680  task_descriptor->queue_task ();
1681  }
1682 
1683 
1690  Task (const Task<RT> &t)
1691  :
1693  {}
1694 
1695 
1704  Task () = default;
1705 
1717  void join () const
1718  {
1720  task_descriptor->join ();
1721  }
1722 
1735  bool joinable () const
1736  {
1737  return (task_descriptor !=
1738  std::shared_ptr<internal::TaskDescriptor<RT> >());
1739  }
1740 
1741 
1785  typename internal::return_value<RT>::reference_type
1787  {
1788  join ();
1789  return task_descriptor->ret_val.get();
1790  }
1791 
1792 
1798  bool operator == (const Task &t) const
1799  {
1801  return task_descriptor == t.task_descriptor;
1802  }
1803 
1813  "The current object is not associated with a task that "
1814  "can be joined. It may have been detached, or you "
1815  "may have already joined it in the past.");
1817  private:
1822  std::shared_ptr<internal::TaskDescriptor<RT> > task_descriptor;
1823  };
1824 
1825 
1826 
1835  template <typename RT>
1836  inline
1837  Task<RT>
1838  new_task (const std::function<RT ()> &function)
1839  {
1840  return Task<RT>(function);
1841  }
1842 
1843 
1844 
1908  template <typename FunctionObjectType>
1909  inline
1910  auto
1911  new_task (FunctionObjectType function_object)
1913  {
1914  typedef decltype(function_object()) return_type;
1916  return Task<return_type>(std::function<return_type ()>(function_object));
1917  }
1918 
1919 
1920 
1927  template <typename RT, typename... Args>
1928  inline
1929  Task<RT>
1930  new_task (RT (*fun_ptr)(Args...),
1931  typename identity<Args>::type... args)
1932  {
1933  return
1934  new_task (std::function<RT ()>
1935  (std::bind(fun_ptr,
1937  }
1938 
1939 
1940 
1946  template <typename RT, typename C, typename... Args>
1947  inline
1948  Task<RT>
1949  new_task (RT (C::*fun_ptr)(Args...),
1950  typename identity<C>::type &c,
1951  typename identity<Args>::type... args)
1952  {
1953  return
1954  new_task (std::function<RT ()>
1955  (std::bind(fun_ptr, std::ref(c),
1957  }
1958 
1959 #ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG
1960 
1965  template <typename RT, typename C, typename... Args>
1966  inline
1967  Task<RT>
1968  new_task (RT (C::*fun_ptr)(Args...) const,
1969  typename identity<const C>::type &c,
1970  typename identity<Args>::type... args)
1971  {
1972  return
1973  new_task (std::function<RT ()>
1974  (std::bind(fun_ptr, std::cref(c),
1976  }
1977 #endif
1978 
1979 
1980 // ------------------------ TaskGroup -------------------------------------
1981 
1995  template <typename RT = void>
1997  {
1998  public:
2003  {
2004  tasks.push_back (t);
2005  return *this;
2006  }
2007 
2014  void join_all () const
2015  {
2016  for (typename std::list<Task<RT> >::const_iterator
2017  t=tasks.begin(); t!=tasks.end(); ++t)
2018  t->join ();
2019  }
2020 
2021  private:
2025  std::list<Task<RT> > tasks;
2026  };
2027 
2028 } // end of implementation of namespace Threads
2029 
2035 //---------------------------------------------------------------------------
2036 DEAL_II_NAMESPACE_CLOSE
2037 // end of #ifndef dealii_thread_management_h
2038 #endif
2039 //---------------------------------------------------------------------------
Task()=default
std::shared_ptr< internal::ThreadDescriptor< RT > > thread_descriptor
#define AssertNothrow(cond, exc)
Definition: exceptions.h:1183
std::vector< std::pair< ForwardIterator, ForwardIterator > > split_range(const ForwardIterator &begin, const ForwardIterator &end, const unsigned int n_intervals)
unsigned int this_thread_id()
Task< RT > new_task(const std::function< RT()> &function)
Thread(const Thread< RT > &t)
TaskDescriptor< RT > & task_descriptor
Mutex(const Mutex &)
std::list< Task< RT > > tasks
internal::return_value< RT >::reference_type return_value()
void join() const
std::shared_ptr< return_value< RT > > ret_val
#define AssertThrow(cond, exc)
Definition: exceptions.h:1221
bool operator==(const Task &t) const
std::vector< std::pair< unsigned int, unsigned int > > split_interval(const unsigned int begin, const unsigned int end, const unsigned int n_intervals)
Task(const std::function< RT()> &function_object)
Thread()=default
Mutex & operator=(const Mutex &)
DummyBarrier(const unsigned int count, const char *name=nullptr, void *arg=nullptr)
static ::ExceptionBase & ExcNoTask()
#define DeclException1(Exception1, type1, outsequence)
Definition: exceptions.h:346
std::condition_variable condition_variable
#define Assert(cond, exc)
Definition: exceptions.h:1142
static void initialize_multithreading()
std::shared_ptr< internal::TaskDescriptor< RT > > task_descriptor
#define DeclExceptionMsg(Exception, defaulttext)
Definition: exceptions.h:335
Mutex()=default
unsigned int n_existing_threads()
static void thread_entry_point(const std::function< RT()> &function, std::shared_ptr< return_value< RT > > ret_val)
bool joinable() const
Thread(const std::function< RT()> &function)
void wait(DummyThreadMutex &) const
internal::return_value< RT >::reference_type return_value()
bool operator==(const Thread &t) const
TaskGroup & operator+=(const Task< RT > &t)
void start(const std::function< RT()> &function)
ThreadGroup & operator+=(const Thread< RT > &t)
PosixThreadBarrier Barrier
Task(const Task< RT > &t)
Thread< RT > new_thread(const std::function< RT()> &function)
PosixThreadBarrier(const unsigned int count, const char *name=nullptr, void *arg=nullptr)
std::list< Thread< RT > > threads
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcBarrierSizeNotUseful(int arg1)