Reference documentation for deal.II version 9.1.0-pre
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.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>
23 # include <deal.II/base/multithread_info.h>
24 # include <deal.II/base/template_constraints.h>
25 
26 # ifdef DEAL_II_WITH_THREADS
27 # include <condition_variable>
28 # include <mutex>
29 # include <thread>
30 # endif
31 
32 # include <functional>
33 # include <iterator>
34 # include <list>
35 # include <memory>
36 # include <tuple>
37 # include <utility>
38 # include <vector>
39 
40 
41 # ifdef DEAL_II_WITH_THREADS
42 # ifdef DEAL_II_USE_MT_POSIX
43 # include <pthread.h>
44 # endif
45 # include <tbb/task.h>
46 # include <tbb/tbb_stddef.h>
47 # endif
48 
49 
50 
51 DEAL_II_NAMESPACE_OPEN
52 
55 
56 
64 namespace Threads
65 {
80  {
81  public:
96  class ScopedLock
97  {
98  public:
104  {}
105 
112  {}
113  };
114 
119  inline void
120  acquire() const
121  {}
122 
127  inline void
128  release() const
129  {}
130  };
131 
132 
133 
152  {
153  public:
159  inline void
160  signal() const
161  {}
162 
168  inline void
169  broadcast() const
170  {}
171 
179  inline void
181  {}
182  };
183 
184 
185 
202  {
203  public:
209  DummyBarrier(const unsigned int count,
210  const char * name = nullptr,
211  void * arg = nullptr);
212 
217  inline int
218  wait() const
219  {
220  return 0;
221  }
222 
226  inline void
227  dump() const
228  {}
229 
239  ExcBarrierSizeNotUseful,
240  int,
241  << "In single-thread mode, barrier sizes other than 1 are not "
242  << "useful. You gave " << arg1 << ".");
243 
245  };
246 
247 
248 # ifdef DEAL_II_WITH_THREADS
249 
266  class Mutex
267  {
268  public:
284  {
285  public:
290  : mutex(m)
291  {
292  mutex.acquire();
293  }
294 
300  {
301  mutex.release();
302  }
303 
304  private:
309  };
310 
314  Mutex() = default;
315 
320  Mutex(const Mutex &)
321  : mutex()
322  {}
323 
324 
329  Mutex &
330  operator=(const Mutex &)
331  {
332  return *this;
333  }
334 
335 
339  inline void
341  {
342  mutex.lock();
343  }
344 
348  inline void
350  {
351  mutex.unlock();
352  }
353 
354  private:
358  std::mutex mutex;
359 
364  friend class ConditionVariable;
365  };
366 
367 
375  {
376  public:
381  inline void
383  {
384  condition_variable.notify_one();
385  }
386 
391  inline void
393  {
394  condition_variable.notify_all();
395  }
396 
406  inline void
407  wait(Mutex &mutex)
408  {
409  std::unique_lock<std::mutex> lock(mutex.mutex, std::adopt_lock);
410  condition_variable.wait(lock);
411  }
412 
413  private:
417  std::condition_variable condition_variable;
418  };
419 
420 
437  {
438  public:
442  PosixThreadBarrier(const unsigned int count,
443  const char * name = nullptr,
444  void * arg = nullptr);
445 
450 
457  int
458  wait();
459 
460  private:
465 # ifndef DEAL_II_USE_MT_POSIX_NO_BARRIERS
466  pthread_barrier_t barrier;
467 # else
468  unsigned int count;
469 # endif
470  };
471 
472 
478 
479 # else
480 
485  using Mutex = DummyThreadMutex;
486 
493 
499  using Barrier = DummyBarrier;
500 # endif
501 
502 } // namespace Threads
503 
504 
505 namespace Threads
506 {
536  unsigned int
538 
550  unsigned int
551  this_thread_id();
552 
567  template <typename ForwardIterator>
568  std::vector<std::pair<ForwardIterator, ForwardIterator>>
569  split_range(const ForwardIterator &begin,
570  const ForwardIterator &end,
571  const unsigned int n_intervals);
572 
581  std::vector<std::pair<unsigned int, unsigned int>>
582  split_interval(const unsigned int begin,
583  const unsigned int end,
584  const unsigned int n_intervals);
585 
597  namespace internal
598  {
614  [[noreturn]] void
615  handle_std_exception(const std::exception &exc);
616 
624  [[noreturn]] void
625  handle_unknown_exception();
626 
634  void
635  register_thread();
636 
644  void
645  deregister_thread();
646  } // namespace internal
647 
652 } // namespace Threads
653 
654 /* ----------- implementation of functions in namespace Threads ---------- */
655 # ifndef DOXYGEN
656 namespace Threads
657 {
658  template <typename ForwardIterator>
659  std::vector<std::pair<ForwardIterator, ForwardIterator>>
660  split_range(const ForwardIterator &begin,
661  const ForwardIterator &end,
662  const unsigned int n_intervals)
663  {
664  using IteratorPair = std::pair<ForwardIterator, ForwardIterator>;
665 
666  // in non-multithreaded mode, we often have the case that this
667  // function is called with n_intervals==1, so have a shortcut here
668  // to handle that case efficiently
669 
670  if (n_intervals == 1)
671  return (std::vector<IteratorPair>(1, IteratorPair(begin, end)));
672 
673  // if more than one interval requested, do the full work
674  const unsigned int n_elements = std::distance(begin, end);
675  const unsigned int n_elements_per_interval = n_elements / n_intervals;
676  const unsigned int residual = n_elements % n_intervals;
677 
678  std::vector<IteratorPair> return_values(n_intervals);
679 
680  return_values[0].first = begin;
681  for (unsigned int i = 0; i < n_intervals; ++i)
682  {
683  if (i != n_intervals - 1)
684  {
685  return_values[i].second = return_values[i].first;
686  // note: the cast is performed to avoid a warning of gcc
687  // that in the library `dist>=0' is checked (dist has a
688  // template type, which here is unsigned if no cast is
689  // performed)
690  std::advance(return_values[i].second,
691  static_cast<signed int>(n_elements_per_interval));
692  // distribute residual in division equally among the first
693  // few subintervals
694  if (i < residual)
695  ++return_values[i].second;
696 
697  return_values[i + 1].first = return_values[i].second;
698  }
699  else
700  return_values[i].second = end;
701  }
702  return return_values;
703  }
704 } // namespace Threads
705 
706 # endif // DOXYGEN
707 
708 namespace Threads
709 {
710  namespace internal
711  {
720  template <typename RT>
721  struct return_value
722  {
723  private:
724  RT value;
725 
726  public:
727  using reference_type = RT &;
728 
729  inline return_value()
730  : value()
731  {}
732 
733  inline reference_type
734  get()
735  {
736  return value;
737  }
738 
739  inline void
740  set(RT &&v)
741  {
742  value = std::move(v);
743  }
744  };
745 
746 
756  template <typename RT>
757  struct return_value<RT &>
758  {
759  private:
760  RT *value;
761 
762  public:
763  using reference_type = RT &;
764 
765  inline return_value()
766  : value(nullptr)
767  {}
768 
769  inline reference_type
770  get() const
771  {
772  return *value;
773  }
774 
775  inline void
776  set(RT &v)
777  {
778  value = &v;
779  }
780  };
781 
782 
791  template <>
792  struct return_value<void>
793  {
794  using reference_type = void;
795 
796  static inline void
797  get()
798  {}
799  };
800  } // namespace internal
801 
802 
803 
804  namespace internal
805  {
806  template <typename RT>
807  inline void
808  call(const std::function<RT()> & function,
809  internal::return_value<RT> &ret_val)
810  {
811  ret_val.set(function());
812  }
813 
814 
815  inline void
816  call(const std::function<void()> &function, internal::return_value<void> &)
817  {
818  function();
819  }
820  } // namespace internal
821 
822 
823 
824  namespace internal
825  {
826 # ifdef DEAL_II_WITH_THREADS
827 
838  template <typename RT>
840  {
844  std::thread thread;
845 
854  std::shared_ptr<return_value<RT>> ret_val;
855 
888 
893 
898  : thread_is_active(false)
899  {}
900 
902  {
903  if (!thread_is_active)
904  return;
905  thread.detach();
906  thread_is_active = false;
907  }
908 
913  void
914  start(const std::function<RT()> &function)
915  {
916  thread_is_active = true;
917  ret_val = std::make_shared<return_value<RT>>();
918  thread = std::thread(thread_entry_point, function, ret_val);
919  }
920 
921 
925  void
927  {
928  // see if the thread hasn't been joined yet. if it has, then
929  // join() is a no-op. use schmidt's double-checking strategy
930  // to use the mutex only when necessary
931  if (thread_is_active == false)
932  return;
933 
934  Mutex::ScopedLock lock(thread_is_active_mutex);
935  if (thread_is_active == true)
936  {
937  Assert(thread.joinable(), ExcInternalError());
938  thread.join();
939  thread_is_active = false;
940  }
941  }
942 
943  private:
947  static void
948  thread_entry_point(const std::function<RT()> & function,
949  std::shared_ptr<return_value<RT>> ret_val)
950  {
951  // call the function in question. since an exception that is
952  // thrown from one of the called functions will not propagate
953  // to the main thread, it will kill the program if not treated
954  // here before we return to the operating system's thread
955  // library
956  internal::register_thread();
957  try
958  {
959  call(function, *ret_val);
960  }
961  catch (const std::exception &exc)
962  {
963  internal::handle_std_exception(exc);
964  }
965  catch (...)
966  {
967  internal::handle_unknown_exception();
968  }
969  internal::deregister_thread();
970  }
971  };
972 
973 # else
974 
982  template <typename RT>
983  struct ThreadDescriptor
984  {
989  std::shared_ptr<return_value<RT>> ret_val;
990 
995  void
996  start(const std::function<RT()> &function)
997  {
998  ret_val = std::make_shared<return_value<RT>>();
999  call(function, *ret_val);
1000  }
1001 
1005  void
1006  join()
1007  {}
1008  };
1009 
1010 # endif
1011  } // namespace internal
1012 
1013 
1036  template <typename RT = void>
1037  class Thread
1038  {
1039  public:
1043  Thread(const std::function<RT()> &function)
1044  : thread_descriptor(new internal::ThreadDescriptor<RT>())
1045  {
1046  // in a second step, start the thread.
1047  thread_descriptor->start(function);
1048  }
1049 
1055  Thread() = default;
1056 
1060  Thread(const Thread<RT> &t)
1061  : thread_descriptor(t.thread_descriptor)
1062  {}
1063 
1069  void
1070  join() const
1071  {
1072  if (thread_descriptor)
1073  thread_descriptor->join();
1074  }
1075 
1119  typename internal::return_value<RT>::reference_type
1121  {
1122  join();
1123  return thread_descriptor->ret_val->get();
1124  }
1125 
1130  bool
1131  valid() const
1132  {
1133  return static_cast<bool>(thread_descriptor);
1134  }
1135 
1136 
1142  bool
1143  operator==(const Thread &t) const
1144  {
1145  return thread_descriptor == t.thread_descriptor;
1146  }
1147 
1148  private:
1154  std::shared_ptr<internal::ThreadDescriptor<RT>> thread_descriptor;
1155  };
1156 
1157 
1158  namespace internal
1159  {
1167  template <typename T>
1169  {
1170  static T
1171  act(T &t)
1172  {
1173  return t;
1174  }
1175  };
1176 
1177 
1178 
1186  template <typename T>
1187  struct maybe_make_ref<T &>
1188  {
1189  static std::reference_wrapper<T>
1190  act(T &t)
1191  {
1192  return std::ref(t);
1193  }
1194  };
1195  } // namespace internal
1196 
1197 
1198 
1199  // ----------- thread starters for functions not taking any parameters
1200 
1209  template <typename RT>
1210  inline Thread<RT>
1211  new_thread(const std::function<RT()> &function)
1212  {
1213  return Thread<RT>(function);
1214  }
1215 
1216 
1217 
1282  template <typename FunctionObjectType>
1283  inline auto
1284  new_thread(FunctionObjectType function_object)
1286  {
1287  using return_type = decltype(function_object());
1288  return Thread<return_type>(std::function<return_type()>(function_object));
1289  }
1290 
1291 
1292 
1299  template <typename RT, typename... Args>
1300  inline Thread<RT>
1301  new_thread(RT (*fun_ptr)(Args...), typename identity<Args>::type... args)
1302  {
1303  return new_thread(std::function<RT()>(
1304  std::bind(fun_ptr, internal::maybe_make_ref<Args>::act(args)...)));
1305  }
1306 
1307 
1308 
1314  template <typename RT, typename C, typename... Args>
1315  inline Thread<RT>
1316  new_thread(RT (C::*fun_ptr)(Args...),
1317  typename identity<C>::type &c,
1318  typename identity<Args>::type... args)
1319  {
1320  return new_thread(std::function<RT()>(std::bind(
1321  fun_ptr, std::ref(c), internal::maybe_make_ref<Args>::act(args)...)));
1322  }
1323 
1324 # ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG
1325 
1330  template <typename RT, typename C, typename... Args>
1331  inline Thread<RT>
1332  new_thread(RT (C::*fun_ptr)(Args...) const,
1333  typename identity<const C>::type &c,
1334  typename identity<Args>::type... args)
1335  {
1336  return new_thread(std::function<RT()>(std::bind(
1337  fun_ptr, std::cref(c), internal::maybe_make_ref<Args>::act(args)...)));
1338  }
1339 # endif
1340 
1341  // ------------------------ ThreadGroup -------------------------------------
1342 
1351  template <typename RT = void>
1353  {
1354  public:
1358  ThreadGroup &
1360  {
1361  threads.push_back(t);
1362  return *this;
1363  }
1364 
1371  void
1372  join_all() const
1373  {
1374  for (typename std::list<Thread<RT>>::const_iterator t = threads.begin();
1375  t != threads.end();
1376  ++t)
1377  t->join();
1378  }
1379 
1380  private:
1384  std::list<Thread<RT>> threads;
1385  };
1386 
1387 
1388  template <typename>
1389  class Task;
1390 
1391 
1392  namespace internal
1393  {
1394 # ifdef DEAL_II_WITH_THREADS
1395 
1396  template <typename>
1397  struct TaskDescriptor;
1398 
1402  template <typename RT>
1403  struct TaskEntryPoint : public tbb::task
1404  {
1405  TaskEntryPoint(TaskDescriptor<RT> &task_descriptor)
1406  : task_descriptor(task_descriptor)
1407  {}
1408 
1409  virtual tbb::task *
1410  execute() override
1411  {
1412  // call the function object and put the return value into the
1413  // proper place
1414  try
1415  {
1416  call(task_descriptor.function, task_descriptor.ret_val);
1417  }
1418  catch (const std::exception &exc)
1419  {
1420  internal::handle_std_exception(exc);
1421  }
1422  catch (...)
1423  {
1424  internal::handle_unknown_exception();
1425  }
1426  return nullptr;
1427  }
1428 
1432  TaskDescriptor<RT> &task_descriptor;
1433  };
1434 
1456  template <typename RT>
1457  struct TaskDescriptor
1458  {
1459  private:
1463  std::function<RT()> function;
1464 
1473  tbb::task *task;
1474 
1478  return_value<RT> ret_val;
1479 
1483  bool task_is_done;
1484 
1485  public:
1489  TaskDescriptor(const std::function<RT()> &function);
1490 
1496  TaskDescriptor();
1497 
1502  TaskDescriptor(const TaskDescriptor &);
1503 
1507  ~TaskDescriptor();
1508 
1513  TaskDescriptor &
1514  operator=(const TaskDescriptor &);
1515 
1522  void
1523  queue_task();
1524 
1530  void
1531  join();
1532 
1533 
1534  template <typename>
1535  friend struct TaskEntryPoint;
1536  friend class ::Threads::Task<RT>;
1537  };
1538 
1539 
1540 
1541  template <typename RT>
1542  inline TaskDescriptor<RT>::TaskDescriptor(
1543  const std::function<RT()> &function)
1544  : function(function)
1545  , task(nullptr)
1546  , task_is_done(false)
1547  {}
1548 
1549 
1550  template <typename RT>
1551  inline void
1552  TaskDescriptor<RT>::queue_task()
1553  {
1554  // use the pattern described in the TBB book on pages 230/231
1555  // ("Start a large task in parallel with the main program")
1556  task = new (tbb::task::allocate_root()) tbb::empty_task;
1557  task->set_ref_count(2);
1558 
1559  tbb::task *worker =
1560  new (task->allocate_child()) TaskEntryPoint<RT>(*this);
1561 
1562  // in earlier versions of the TBB, task::spawn was a regular
1563  // member function; however, in later versions, it was converted
1564  // into a static function. we could always call it as a regular member
1565  // function of *task, but that appears to confuse the NVidia nvcc
1566  // compiler. consequently, the following work-around:
1567 # if TBB_VERSION_MAJOR >= 4
1568  tbb::task::spawn(*worker);
1569 # else
1570  task->spawn(*worker);
1571 # endif
1572  }
1573 
1574 
1575 
1576  template <typename RT>
1577  TaskDescriptor<RT>::TaskDescriptor()
1578  : task_is_done(false)
1579  {
1580  Assert(false, ExcInternalError());
1581  }
1582 
1583 
1584 
1585  template <typename RT>
1586  TaskDescriptor<RT>::TaskDescriptor(const TaskDescriptor &)
1587  : task_is_done(false)
1588  {
1589  // we shouldn't be getting here -- task descriptors
1590  // can't be copied
1591  Assert(false, ExcInternalError());
1592  }
1593 
1594 
1595 
1596  template <typename RT>
1597  inline TaskDescriptor<RT>::~TaskDescriptor()
1598  {
1599  // wait for the task to complete for sure
1600  join();
1601 
1602  // now destroy the empty task structure. the book recommends to
1603  // spawn it as well and let the scheduler destroy the object
1604  // when done, but this has the disadvantage that the scheduler
1605  // may not get to actually finishing the task before it goes out
1606  // of scope (at the end of the program, or if a thread is done
1607  // on which it was run) and then we would get a hard-to-decipher
1608  // warning about unfinished tasks when the scheduler "goes out
1609  // of the arena". rather, let's explicitly destroy the empty
1610  // task object. before that, make sure that the task has been
1611  // shut down, expressed by a zero reference count
1612  AssertNothrow(task != nullptr, ExcInternalError());
1613  AssertNothrow(task->ref_count() == 0, ExcInternalError());
1614  task->destroy(*task);
1615  }
1616 
1617 
1618  template <typename RT>
1619  TaskDescriptor<RT> &
1620  TaskDescriptor<RT>::operator=(const TaskDescriptor &)
1621  {
1622  // we shouldn't be getting here -- task descriptors
1623  // can't be copied
1624  Assert(false, ExcInternalError());
1625  return *this;
1626  }
1627 
1628 
1629  template <typename RT>
1630  inline void
1631  TaskDescriptor<RT>::join()
1632  {
1633  // if the task is already done, just return. this makes sure we
1634  // call tbb::Task::wait_for_all() exactly once, as required by
1635  // TBB. we could also get the reference count of task for doing
1636  // this, but that is usually slower. note that this does not
1637  // work when the thread calling this function is not the same as
1638  // the one that initialized the task.
1639  //
1640  // TODO: can we assert that no other thread tries to end the
1641  // task?
1642  if (task_is_done == true)
1643  return;
1644 
1645  // let TBB wait for the task to complete.
1646  task_is_done = true;
1647  task->wait_for_all();
1648  }
1649 
1650 
1651 
1652 # else // no threading enabled
1653 
1658  template <typename RT>
1659  struct TaskDescriptor
1660  {
1664  return_value<RT> ret_val;
1665 
1670  TaskDescriptor(const std::function<RT()> &function)
1671  {
1672  call(function, ret_val);
1673  }
1674 
1679  static void
1680  join()
1681  {}
1682 
1687  static void
1688  queue_task()
1689  {}
1690  };
1691 
1692 # endif
1693 
1694  } // namespace internal
1695 
1696 
1697 
1708  template <typename RT = void>
1709  class Task
1710  {
1711  public:
1719  Task(const std::function<RT()> &function_object)
1720  {
1721  // create a task descriptor and tell it to queue itself up with
1722  // the scheduling system
1723  task_descriptor =
1724  std::make_shared<internal::TaskDescriptor<RT>>(function_object);
1725  task_descriptor->queue_task();
1726  }
1727 
1728 
1735  Task(const Task<RT> &t)
1736  : task_descriptor(t.task_descriptor)
1737  {}
1738 
1739 
1748  Task() = default;
1749 
1761  void
1762  join() const
1763  {
1764  AssertThrow(joinable(), ExcNoTask());
1765  task_descriptor->join();
1766  }
1767 
1780  bool
1781  joinable() const
1782  {
1783  return (task_descriptor !=
1784  std::shared_ptr<internal::TaskDescriptor<RT>>());
1785  }
1786 
1787 
1831  typename internal::return_value<RT>::reference_type
1833  {
1834  join();
1835  return task_descriptor->ret_val.get();
1836  }
1837 
1838 
1844  bool
1845  operator==(const Task &t) const
1846  {
1847  AssertThrow(joinable(), ExcNoTask());
1848  return task_descriptor == t.task_descriptor;
1849  }
1850 
1859  DeclExceptionMsg(ExcNoTask,
1860  "The current object is not associated with a task that "
1861  "can be joined. It may have been detached, or you "
1862  "may have already joined it in the past.");
1864  private:
1869  std::shared_ptr<internal::TaskDescriptor<RT>> task_descriptor;
1870  };
1871 
1872 
1873 
1882  template <typename RT>
1883  inline Task<RT>
1884  new_task(const std::function<RT()> &function)
1885  {
1886  return Task<RT>(function);
1887  }
1888 
1889 
1890 
1955  template <typename FunctionObjectType>
1956  inline auto
1957  new_task(FunctionObjectType function_object)
1959  {
1960  using return_type = decltype(function_object());
1962  return Task<return_type>(std::function<return_type()>(function_object));
1963  }
1964 
1965 
1966 
1973  template <typename RT, typename... Args>
1974  inline Task<RT>
1975  new_task(RT (*fun_ptr)(Args...), typename identity<Args>::type... args)
1976  {
1977  return new_task(std::function<RT()>(
1978  std::bind(fun_ptr, internal::maybe_make_ref<Args>::act(args)...)));
1979  }
1980 
1981 
1982 
1988  template <typename RT, typename C, typename... Args>
1989  inline Task<RT>
1990  new_task(RT (C::*fun_ptr)(Args...),
1991  typename identity<C>::type &c,
1992  typename identity<Args>::type... args)
1993  {
1994  return new_task(std::function<RT()>(std::bind(
1995  fun_ptr, std::ref(c), internal::maybe_make_ref<Args>::act(args)...)));
1996  }
1997 
1998 # ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG
1999 
2004  template <typename RT, typename C, typename... Args>
2005  inline Task<RT>
2006  new_task(RT (C::*fun_ptr)(Args...) const,
2007  typename identity<const C>::type &c,
2008  typename identity<Args>::type... args)
2009  {
2010  return new_task(std::function<RT()>(std::bind(
2011  fun_ptr, std::cref(c), internal::maybe_make_ref<Args>::act(args)...)));
2012  }
2013 # endif
2014 
2015 
2016  // ------------------------ TaskGroup -------------------------------------
2017 
2031  template <typename RT = void>
2033  {
2034  public:
2038  TaskGroup &
2040  {
2041  tasks.push_back(t);
2042  return *this;
2043  }
2044 
2051  void
2052  join_all() const
2053  {
2054  for (typename std::list<Task<RT>>::const_iterator t = tasks.begin();
2055  t != tasks.end();
2056  ++t)
2057  t->join();
2058  }
2059 
2060  private:
2064  std::list<Task<RT>> tasks;
2065  };
2066 
2067 } // namespace Threads
2068 
2074 //---------------------------------------------------------------------------
2075 DEAL_II_NAMESPACE_CLOSE
2076 // end of #ifndef dealii_thread_management_h
2077 #endif
2078 //---------------------------------------------------------------------------
std::shared_ptr< internal::ThreadDescriptor< RT > > thread_descriptor
#define AssertNothrow(cond, exc)
Definition: exceptions.h:1278
bool valid() const
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()
std::shared_ptr< return_value< RT > > ret_val
#define AssertThrow(cond, exc)
Definition: exceptions.h:1329
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)
Mutex & operator=(const Mutex &)
void join() const
void wait(DummyThreadMutex &) const
#define DeclException1(Exception1, type1, outsequence)
Definition: exceptions.h:408
std::condition_variable condition_variable
#define Assert(cond, exc)
Definition: exceptions.h:1227
static void initialize_multithreading()
std::shared_ptr< internal::TaskDescriptor< RT > > task_descriptor
#define DeclExceptionMsg(Exception, defaulttext)
Definition: exceptions.h:397
void join() const
unsigned int n_existing_threads()
Thread(const std::function< RT()> &function)
internal::return_value< RT >::reference_type return_value()
bool operator==(const Task &t) const
TaskGroup & operator+=(const Task< RT > &t)
void start(const std::function< RT()> &function)
ThreadGroup & operator+=(const Thread< RT > &t)
Task(const Task< RT > &t)
Thread< RT > new_thread(const std::function< RT()> &function)
static void thread_entry_point(const std::function< RT()> &function, std::shared_ptr< return_value< RT >> ret_val)
bool joinable() const
bool operator==(const Thread &t) const
std::vector< std::pair< ForwardIterator, ForwardIterator > > split_range(const ForwardIterator &begin, const ForwardIterator &end, const unsigned int n_intervals)
std::list< Thread< RT > > threads
static::ExceptionBase & ExcInternalError()