Reference documentation for deal.II version 9.2.0
\(\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 - 2020 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 <condition_variable>
28 # include <functional>
29 # include <iterator>
30 # include <list>
31 # include <memory>
32 # include <mutex>
33 # include <tuple>
34 # include <utility>
35 # include <vector>
37 # ifdef DEAL_II_WITH_THREADS
38 # include <thread>
39 # ifdef DEAL_II_USE_MT_POSIX
40 # include <pthread.h>
41 # endif
42 # define TBB_SUPPRESS_DEPRECATED_MESSAGES 1
43 # include <tbb/task.h>
44 # undef TBB_SUPPRESS_DEPRECATED_MESSAGES
45 # include <tbb/tbb_stddef.h>
46 # endif
48 
49 
50 
52 
55 
56 
64 namespace Threads
65 {
91  class Mutex : public std::mutex
92  {
93  public:
114  using ScopedLock DEAL_II_DEPRECATED = std::lock_guard<std::mutex>;
115 
119  Mutex() = default;
120 
125  Mutex(const Mutex &)
126  : std::mutex()
127  {}
128 
129 
134  Mutex &
135  operator=(const Mutex &)
136  {
137  return *this;
138  }
139 
140 
147  DEAL_II_DEPRECATED
148  inline void
150  {
151  this->lock();
152  }
153 
160  DEAL_II_DEPRECATED
161  inline void
163  {
164  this->unlock();
165  }
166  };
167 
168 
178  {
179  public:
184  inline void
186  {
187  condition_variable.notify_one();
188  }
189 
194  inline void
196  {
197  condition_variable.notify_all();
198  }
199 
209  inline void
210  wait(Mutex &mutex)
211  {
212  std::unique_lock<std::mutex> lock(mutex, std::adopt_lock);
213  condition_variable.wait(lock);
214  }
215 
216  private:
220  std::condition_variable condition_variable;
221  };
222 
223 # ifdef DEAL_II_USE_MT_POSIX
224 
243  {
244  public:
248  PosixThreadBarrier(const unsigned int count,
249  const char * name = nullptr,
250  void * arg = nullptr);
251 
256 
263  int
264  wait();
265 
266  private:
271 # ifndef DEAL_II_USE_MT_POSIX_NO_BARRIERS
272  pthread_barrier_t barrier;
273 # else
274  unsigned int count;
275 # endif
276  };
277 
278 
287 # endif
288 } // namespace Threads
289 
290 
291 namespace Threads
292 {
332  unsigned int
334 
349  unsigned int
350  this_thread_id();
351 
366  template <typename ForwardIterator>
367  std::vector<std::pair<ForwardIterator, ForwardIterator>>
368  split_range(const ForwardIterator &begin,
369  const ForwardIterator &end,
370  const unsigned int n_intervals);
371 
380  std::vector<std::pair<unsigned int, unsigned int>>
381  split_interval(const unsigned int begin,
382  const unsigned int end,
383  const unsigned int n_intervals);
384 
396  namespace internal
397  {
413  [[noreturn]] void
414  handle_std_exception(const std::exception &exc);
415 
423  [[noreturn]] void
425 
433  void
434  register_thread();
435 
443  void
445  } // namespace internal
446 
451 } // namespace Threads
452 
453 /* ----------- implementation of functions in namespace Threads ---------- */
454 # ifndef DOXYGEN
455 namespace Threads
456 {
457  template <typename ForwardIterator>
458  std::vector<std::pair<ForwardIterator, ForwardIterator>>
459  split_range(const ForwardIterator &begin,
460  const ForwardIterator &end,
461  const unsigned int n_intervals)
462  {
463  using IteratorPair = std::pair<ForwardIterator, ForwardIterator>;
464 
465  // in non-multithreaded mode, we often have the case that this
466  // function is called with n_intervals==1, so have a shortcut here
467  // to handle that case efficiently
468 
469  if (n_intervals == 1)
470  return (std::vector<IteratorPair>(1, IteratorPair(begin, end)));
471 
472  // if more than one interval requested, do the full work
473  const unsigned int n_elements = std::distance(begin, end);
474  const unsigned int n_elements_per_interval = n_elements / n_intervals;
475  const unsigned int residual = n_elements % n_intervals;
476 
477  std::vector<IteratorPair> return_values(n_intervals);
478 
479  return_values[0].first = begin;
480  for (unsigned int i = 0; i < n_intervals; ++i)
481  {
482  if (i != n_intervals - 1)
483  {
484  return_values[i].second = return_values[i].first;
485  // note: the cast is performed to avoid a warning of gcc
486  // that in the library `dist>=0' is checked (dist has a
487  // template type, which here is unsigned if no cast is
488  // performed)
489  std::advance(return_values[i].second,
490  static_cast<signed int>(n_elements_per_interval));
491  // distribute residual in division equally among the first
492  // few subintervals
493  if (i < residual)
494  ++return_values[i].second;
495 
496  return_values[i + 1].first = return_values[i].second;
497  }
498  else
499  return_values[i].second = end;
500  }
501  return return_values;
502  }
503 } // namespace Threads
504 
505 # endif // DOXYGEN
506 
507 namespace Threads
508 {
509  namespace internal
510  {
519  template <typename RT>
521  {
522  private:
523  RT value;
524 
525  public:
526  using reference_type = RT &;
527 
528  inline return_value()
529  : value()
530  {}
531 
532  inline reference_type
533  get()
534  {
535  return value;
536  }
537 
538  inline void
539  set(RT &&v)
540  {
541  value = std::move(v);
542  }
543  };
544 
545 
555  template <typename RT>
556  struct return_value<RT &>
557  {
558  private:
559  RT *value;
560 
561  public:
562  using reference_type = RT &;
563 
564  inline return_value()
565  : value(nullptr)
566  {}
567 
568  inline reference_type
569  get() const
570  {
571  return *value;
572  }
573 
574  inline void
575  set(RT &v)
576  {
577  value = &v;
578  }
579  };
580 
581 
590  template <>
591  struct return_value<void>
592  {
593  using reference_type = void;
594 
595  static inline void
596  get()
597  {}
598  };
599  } // namespace internal
600 
601 
602 
603  namespace internal
604  {
605  template <typename RT>
606  inline void
607  call(const std::function<RT()> & function,
609  {
610  ret_val.set(function());
611  }
612 
613 
614  inline void
615  call(const std::function<void()> &function, internal::return_value<void> &)
616  {
617  function();
618  }
619  } // namespace internal
620 
621 
622 
623  namespace internal
624  {
625 # ifdef DEAL_II_WITH_THREADS
626 
637  template <typename RT>
639  {
643  std::thread thread;
644 
653  std::shared_ptr<return_value<RT>> ret_val;
654 
687 
692 
697  : thread_is_active(false)
698  {}
699 
701  {
702  if (!thread_is_active)
703  return;
704  thread.detach();
705  thread_is_active = false;
706  }
707 
712  void
713  start(const std::function<RT()> &function)
714  {
715  thread_is_active = true;
716  ret_val = std::make_shared<return_value<RT>>();
717  thread = std::thread(thread_entry_point, function, ret_val);
718  }
719 
720 
724  void
726  {
727  // see if the thread hasn't been joined yet. if it has, then
728  // join() is a no-op. use schmidt's double-checking strategy
729  // to use the mutex only when necessary
730  if (thread_is_active == false)
731  return;
732 
733  Mutex::ScopedLock lock(thread_is_active_mutex);
734  if (thread_is_active == true)
735  {
736  Assert(thread.joinable(), ExcInternalError());
737  thread.join();
738  thread_is_active = false;
739  }
740  }
741 
742  private:
746  static void
747  thread_entry_point(const std::function<RT()> & function,
748  std::shared_ptr<return_value<RT>> ret_val)
749  {
750  // call the function in question. since an exception that is
751  // thrown from one of the called functions will not propagate
752  // to the main thread, it will kill the program if not treated
753  // here before we return to the operating system's thread
754  // library
756  try
757  {
758  call(function, *ret_val);
759  }
760  catch (const std::exception &exc)
761  {
763  }
764  catch (...)
765  {
767  }
769  }
770  };
771 
772 # else
773 
781  template <typename RT>
782  struct ThreadDescriptor
783  {
788  std::shared_ptr<return_value<RT>> ret_val;
789 
794  void
795  start(const std::function<RT()> &function)
796  {
797  ret_val = std::make_shared<return_value<RT>>();
798  call(function, *ret_val);
799  }
800 
804  void
805  join()
806  {}
807  };
808 
809 # endif
810  } // namespace internal
811 
812 
835  template <typename RT = void>
836  class Thread
837  {
838  public:
842  Thread(const std::function<RT()> &function)
843  : thread_descriptor(new internal::ThreadDescriptor<RT>())
844  {
845  // in a second step, start the thread.
846  thread_descriptor->start(function);
847  }
848 
854  Thread() = default;
855 
859  Thread(const Thread<RT> &t)
860  : thread_descriptor(t.thread_descriptor)
861  {}
862 
868  void
869  join() const
870  {
871  if (thread_descriptor)
872  thread_descriptor->join();
873  }
874 
920  {
921  join();
922  return thread_descriptor->ret_val->get();
923  }
924 
929  bool
930  valid() const
931  {
932  return static_cast<bool>(thread_descriptor);
933  }
934 
935 
941  bool
942  operator==(const Thread &t) const
943  {
944  return thread_descriptor == t.thread_descriptor;
945  }
946 
947  private:
953  std::shared_ptr<internal::ThreadDescriptor<RT>> thread_descriptor;
954  };
955 
956 
957  namespace internal
958  {
966  template <typename T>
968  {
969  static T
970  act(T &t)
971  {
972  return t;
973  }
974  };
975 
976 
977 
985  template <typename T>
986  struct maybe_make_ref<T &>
987  {
988  static std::reference_wrapper<T>
989  act(T &t)
990  {
991  return std::ref(t);
992  }
993  };
994  } // namespace internal
995 
996 
997 
998  // ----------- thread starters for functions not taking any parameters
999 
1008  template <typename RT>
1009  inline Thread<RT>
1010  new_thread(const std::function<RT()> &function)
1011  {
1012  return Thread<RT>(function);
1013  }
1014 
1015 
1016 
1081  template <typename FunctionObjectType>
1082  inline auto
1083  new_thread(FunctionObjectType function_object)
1085  {
1086  using return_type = decltype(function_object());
1087  return Thread<return_type>(std::function<return_type()>(function_object));
1088  }
1089 
1090 
1091 
1098  template <typename RT, typename... Args>
1099  inline Thread<RT>
1100  new_thread(RT (*fun_ptr)(Args...), typename identity<Args>::type... args)
1101  {
1102  auto dummy = std::make_tuple(internal::maybe_make_ref<Args>::act(args)...);
1103  return new_thread(
1104  [dummy, fun_ptr]() -> RT { return std_cxx17::apply(fun_ptr, dummy); });
1105  }
1106 
1107 
1108 
1114  template <typename RT, typename C, typename... Args>
1115  inline Thread<RT>
1116  new_thread(RT (C::*fun_ptr)(Args...),
1117  typename identity<C>::type &c,
1118  typename identity<Args>::type... args)
1119  {
1120  // NOLINTNEXTLINE(modernize-avoid-bind) silence clang-tidy
1121  return new_thread(std::function<RT()>(std::bind(
1122  fun_ptr, std::ref(c), internal::maybe_make_ref<Args>::act(args)...)));
1123  }
1124 
1125 # ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG
1126 
1131  template <typename RT, typename C, typename... Args>
1132  inline Thread<RT>
1133  new_thread(RT (C::*fun_ptr)(Args...) const,
1134  typename identity<const C>::type &c,
1135  typename identity<Args>::type... args)
1136  {
1137  // NOLINTNEXTLINE(modernize-avoid-bind) silence clang-tidy
1138  return new_thread(std::function<RT()>(std::bind(
1139  fun_ptr, std::cref(c), internal::maybe_make_ref<Args>::act(args)...)));
1140  }
1141 # endif
1142 
1143  // ------------------------ ThreadGroup -------------------------------------
1144 
1153  template <typename RT = void>
1155  {
1156  public:
1160  ThreadGroup &
1162  {
1163  threads.push_back(t);
1164  return *this;
1165  }
1166 
1173  void
1174  join_all() const
1175  {
1176  for (typename std::list<Thread<RT>>::const_iterator t = threads.begin();
1177  t != threads.end();
1178  ++t)
1179  t->join();
1180  }
1181 
1182  private:
1186  std::list<Thread<RT>> threads;
1187  };
1188 
1189 
1190  template <typename>
1191  class Task;
1192 
1193 
1194  namespace internal
1195  {
1196 # ifdef DEAL_II_WITH_THREADS
1197 
1198  template <typename>
1200 
1204  template <typename RT>
1205  struct TaskEntryPoint : public tbb::task
1206  {
1208  : task_descriptor(task_descriptor)
1209  {}
1210 
1211  virtual tbb::task *
1212  execute() override
1213  {
1214  // call the function object and put the return value into the
1215  // proper place
1216  try
1217  {
1218  call(task_descriptor.function, task_descriptor.ret_val);
1219  }
1220  catch (const std::exception &exc)
1221  {
1223  }
1224  catch (...)
1225  {
1227  }
1228  return nullptr;
1229  }
1230 
1235  };
1236 
1258  template <typename RT>
1259  struct TaskDescriptor
1260  {
1261  private:
1265  std::function<RT()> function;
1266 
1275  tbb::task *task;
1276 
1281 
1286 
1287  public:
1291  TaskDescriptor(const std::function<RT()> &function);
1292 
1298  TaskDescriptor();
1299 
1304  TaskDescriptor(const TaskDescriptor &) = delete;
1305 
1309  ~TaskDescriptor();
1310 
1315  TaskDescriptor &
1316  operator=(const TaskDescriptor &) = delete;
1317 
1324  void
1325  queue_task();
1326 
1332  void
1333  join();
1334 
1335 
1336  template <typename>
1337  friend struct TaskEntryPoint;
1338  friend class ::Threads::Task<RT>;
1339  };
1340 
1341 
1342 
1343  template <typename RT>
1345  const std::function<RT()> &function)
1346  : function(function)
1347  , task(nullptr)
1348  , task_is_done(false)
1349  {}
1350 
1351 
1352  template <typename RT>
1353  inline void
1355  {
1356  // use the pattern described in the TBB book on pages 230/231
1357  // ("Start a large task in parallel with the main program")
1358  task = new (tbb::task::allocate_root()) tbb::empty_task;
1359  task->set_ref_count(2);
1360 
1361  tbb::task *worker =
1362  new (task->allocate_child()) TaskEntryPoint<RT>(*this);
1363 
1364  tbb::task::spawn(*worker);
1365  }
1366 
1367 
1368 
1369  template <typename RT>
1371  : task_is_done(false)
1372  {
1373  Assert(false, ExcInternalError());
1374  }
1375 
1376 
1377 
1378  template <typename RT>
1380  {
1381  // wait for the task to complete for sure
1382  join();
1383 
1384  // now destroy the empty task structure. the book recommends to
1385  // spawn it as well and let the scheduler destroy the object
1386  // when done, but this has the disadvantage that the scheduler
1387  // may not get to actually finishing the task before it goes out
1388  // of scope (at the end of the program, or if a thread is done
1389  // on which it was run) and then we would get a hard-to-decipher
1390  // warning about unfinished tasks when the scheduler "goes out
1391  // of the arena". rather, let's explicitly destroy the empty
1392  // task object. before that, make sure that the task has been
1393  // shut down, expressed by a zero reference count
1394  AssertNothrow(task != nullptr, ExcInternalError());
1395  AssertNothrow(task->ref_count() == 0, ExcInternalError());
1396  task->destroy(*task);
1397  }
1398 
1399 
1400  template <typename RT>
1401  inline void
1403  {
1404  // if the task is already done, just return. this makes sure we
1405  // call tbb::Task::wait_for_all() exactly once, as required by
1406  // TBB. we could also get the reference count of task for doing
1407  // this, but that is usually slower. note that this does not
1408  // work when the thread calling this function is not the same as
1409  // the one that initialized the task.
1410  //
1411  // TODO: can we assert that no other thread tries to end the
1412  // task?
1413  if (task_is_done == true)
1414  return;
1415 
1416  // let TBB wait for the task to complete.
1417  task_is_done = true;
1418  task->wait_for_all();
1419  }
1420 
1421 
1422 
1423 # else // no threading enabled
1424 
1429  template <typename RT>
1430  struct TaskDescriptor
1431  {
1436 
1441  TaskDescriptor(const std::function<RT()> &function)
1442  {
1443  call(function, ret_val);
1444  }
1445 
1450  static void
1451  join()
1452  {}
1453 
1458  static void
1459  queue_task()
1460  {}
1461  };
1462 
1463 # endif
1464 
1465  } // namespace internal
1466 
1467 
1468 
1479  template <typename RT = void>
1480  class Task
1481  {
1482  public:
1490  Task(const std::function<RT()> &function_object)
1491  {
1492  // create a task descriptor and tell it to queue itself up with
1493  // the scheduling system
1494  task_descriptor =
1495  std::make_shared<internal::TaskDescriptor<RT>>(function_object);
1496  task_descriptor->queue_task();
1497  }
1498 
1499 
1500 
1509  Task() = default;
1510 
1511 
1512 
1524  void
1525  join() const
1526  {
1527  AssertThrow(joinable(), ExcNoTask());
1528  task_descriptor->join();
1529  }
1530 
1543  bool
1544  joinable() const
1545  {
1546  return (task_descriptor !=
1547  std::shared_ptr<internal::TaskDescriptor<RT>>());
1548  }
1549 
1550 
1596  {
1597  join();
1598  return task_descriptor->ret_val.get();
1599  }
1600 
1601 
1607  bool
1608  operator==(const Task &t) const
1609  {
1610  AssertThrow(joinable(), ExcNoTask());
1611  return task_descriptor == t.task_descriptor;
1612  }
1613 
1622  DeclExceptionMsg(ExcNoTask,
1623  "The current object is not associated with a task that "
1624  "can be joined. It may have been detached, or you "
1625  "may have already joined it in the past.");
1627  private:
1632  std::shared_ptr<internal::TaskDescriptor<RT>> task_descriptor;
1633  };
1634 
1635 
1636 
1645  template <typename RT>
1646  inline Task<RT>
1647  new_task(const std::function<RT()> &function)
1648  {
1649  return Task<RT>(function);
1650  }
1651 
1652 
1653 
1718  template <typename FunctionObjectType>
1719  inline auto
1720  new_task(FunctionObjectType function_object)
1722  {
1723  using return_type = decltype(function_object());
1725  return Task<return_type>(std::function<return_type()>(function_object));
1726  }
1727 
1728 
1729 
1736  template <typename RT, typename... Args>
1737  inline Task<RT>
1738  new_task(RT (*fun_ptr)(Args...), typename identity<Args>::type... args)
1739  {
1740  auto dummy = std::make_tuple(internal::maybe_make_ref<Args>::act(args)...);
1741  return new_task(
1742  [dummy, fun_ptr]() -> RT { return std_cxx17::apply(fun_ptr, dummy); });
1743  }
1744 
1745 
1746 
1752  template <typename RT, typename C, typename... Args>
1753  inline Task<RT>
1754  new_task(RT (C::*fun_ptr)(Args...),
1755  typename identity<C>::type &c,
1756  typename identity<Args>::type... args)
1757  {
1758  // NOLINTNEXTLINE(modernize-avoid-bind) silence clang-tidy
1759  return new_task(std::function<RT()>(std::bind(
1760  fun_ptr, std::ref(c), internal::maybe_make_ref<Args>::act(args)...)));
1761  }
1762 
1763 # ifndef DEAL_II_CONST_MEMBER_DEDUCTION_BUG
1764 
1769  template <typename RT, typename C, typename... Args>
1770  inline Task<RT>
1771  new_task(RT (C::*fun_ptr)(Args...) const,
1772  typename identity<const C>::type &c,
1773  typename identity<Args>::type... args)
1774  {
1775  // NOLINTNEXTLINE(modernize-avoid-bind) silence clang-tidy
1776  return new_task(std::function<RT()>(std::bind(
1777  fun_ptr, std::cref(c), internal::maybe_make_ref<Args>::act(args)...)));
1778  }
1779 # endif
1780 
1781 
1782  // ------------------------ TaskGroup -------------------------------------
1783 
1797  template <typename RT = void>
1799  {
1800  public:
1804  TaskGroup &
1806  {
1807  tasks.push_back(t);
1808  return *this;
1809  }
1810 
1811 
1819  std::size_t
1820  size() const
1821  {
1822  return tasks.size();
1823  }
1824 
1825 
1832  void
1833  join_all() const
1834  {
1835  for (auto &t : tasks)
1836  t.join();
1837  }
1838 
1839  private:
1843  std::list<Task<RT>> tasks;
1844  };
1845 
1846 } // namespace Threads
1847 
1853 //---------------------------------------------------------------------------
1855 // end of #ifndef dealii_thread_management_h
1856 #endif
1857 //---------------------------------------------------------------------------
std::shared_ptr< internal::ThreadDescriptor< RT > > thread_descriptor
#define AssertNothrow(cond, exc)
Definition: exceptions.h:1483
unsigned int this_thread_id()
bool valid() const
Task< RT > new_task(const std::function< RT()> &function)
TaskEntryPoint(TaskDescriptor< RT > &task_descriptor)
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
void call(const std::function< void()> &function, internal::return_value< void > &)
std::size_t size() const
std::shared_ptr< return_value< RT > > ret_val
STL namespace.
#define AssertThrow(cond, exc)
Definition: exceptions.h:1531
Point< 2 > second
Definition: grid_out.cc:4353
bool operator==(const Task &t) const
SymmetricTensor< 2, dim, Number > C(const Tensor< 2, dim, Number > &F)
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)
#define DEAL_II_DISABLE_EXTRA_DIAGNOSTICS
Definition: config.h:372
Mutex & operator=(const Mutex &)
void call(const std::function< RT()> &function, internal::return_value< RT > &ret_val)
static const char T
std::condition_variable condition_variable
#define Assert(cond, exc)
Definition: exceptions.h:1419
static void initialize_multithreading()
std::shared_ptr< internal::TaskDescriptor< RT > > task_descriptor
#define DeclExceptionMsg(Exception, defaulttext)
Definition: exceptions.h:496
Mutex()=default
#define DEAL_II_NAMESPACE_CLOSE
Definition: config.h:359
auto apply(F &&fn, Tuple &&t) -> decltype(apply_impl(std::forward< F >(fn), std::forward< Tuple >(t), std_cxx14::make_index_sequence< std::tuple_size< typename std::remove_reference< Tuple >::type >::value >()))
Definition: tuple.h:40
VectorType::value_type * end(VectorType &V)
unsigned int n_existing_threads()
const types::global_dof_index * dummy()
Definition: dof_handler.cc:59
bool joinable() const
Thread(const std::function< RT()> &function)
static std::reference_wrapper< T > act(T &t)
std::lock_guard< std::mutex > ScopedLock
internal::return_value< RT >::reference_type return_value()
void advance(std::tuple< I1, I2 > &t, const unsigned int n)
bool operator==(const Thread &t) const
#define DEAL_II_ENABLE_EXTRA_DIAGNOSTICS
Definition: config.h:409
#define DEAL_II_NAMESPACE_OPEN
Definition: config.h:358
VectorType::value_type * begin(VectorType &V)
TaskGroup & operator+=(const Task< RT > &t)
void start(const std::function< RT()> &function)
ThreadGroup & operator+=(const Thread< 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)
void handle_std_exception(const std::exception &exc)
virtual tbb::task * execute() override
static const bool value
#define DEAL_II_DEPRECATED
Definition: config.h:98
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()