Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TTaskGroup.cxx
Go to the documentation of this file.
1 // @(#)root/thread:$Id$
2 // Author: Danilo Piparo August 2017
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2017, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 #include "RConfigure.h"
13 
14 #include "ROOT/TTaskGroup.hxx"
15 
16 #ifdef R__USE_IMT
17 #include "TROOT.h"
18 #include "tbb/task_group.h"
19 #include "tbb/task_arena.h"
20 #endif
21 
22 #include <type_traits>
23 
24 /**
25 \class ROOT::Experimental::TTaskGroup
26 \ingroup Parallelism
27 \brief A class to manage the asynchronous execution of work items.
28 
29 A TTaskGroup represents concurrent execution of a group of tasks.
30 Tasks may be dynamically added to the group as it is executing.
31 Nesting TTaskGroup instances may result in a runtime overhead.
32 */
33 
34 namespace ROOT {
35 
36 namespace Internal {
37 
38 #ifdef R__USE_IMT
39 tbb::task_group *CastToTG(void* p) {
40  return (tbb::task_group *) p;
41 }
42 
43 #endif
44 
45 } // namespace Internal
46 
47 namespace Experimental {
48 
49 using namespace ROOT::Internal;
50 
51 // in the constructor and destructor the casts are present in order to be able
52 // to be independent from the runtime used.
53 // This leaves the door open for other TTaskGroup implementations.
54 
55 TTaskGroup::TTaskGroup()
56 {
57 #ifdef R__USE_IMT
58  if (!ROOT::IsImplicitMTEnabled()) {
59  throw std::runtime_error("Implicit parallelism not enabled. Cannot instantiate a TTaskGroup.");
60  }
61  fTaskContainer = ((void *)new tbb::task_group());
62 #endif
63 }
64 
65 TTaskGroup::TTaskGroup(TTaskGroup &&other)
66 {
67  *this = std::move(other);
68 }
69 
70 TTaskGroup &TTaskGroup::operator=(TTaskGroup &&other)
71 {
72  fTaskContainer = other.fTaskContainer;
73  other.fTaskContainer = nullptr;
74  fCanRun.store(other.fCanRun);
75  return *this;
76 }
77 
78 TTaskGroup::~TTaskGroup()
79 {
80 #ifdef R__USE_IMT
81  if (!fTaskContainer)
82  return;
83  Wait();
84  delete CastToTG(fTaskContainer);
85 #endif
86 }
87 
88 /////////////////////////////////////////////////////////////////////////////
89 /// Cancel all submitted tasks immediately.
90 void TTaskGroup::Cancel()
91 {
92 #ifdef R__USE_IMT
93  fCanRun = false;
94  CastToTG(fTaskContainer)->cancel();
95  fCanRun = true;
96 #endif
97 }
98 
99 /////////////////////////////////////////////////////////////////////////////
100 /// Add to the group an item of work which will be ran asynchronously.
101 /// Adding many small items of work to the TTaskGroup is not efficient,
102 /// unless they run for long enough. If the work to be done is little, look
103 /// try to express nested parallelism or resort to other constructs such as
104 /// the TThreadExecutor.
105 /// Trying to add a work item to the group while it is in waiting state
106 /// makes the method block.
107 void TTaskGroup::Run(const std::function<void(void)> &closure)
108 {
109 #ifdef R__USE_IMT
110  while (!fCanRun)
111  /* empty */;
112 
113  CastToTG(fTaskContainer)->run(closure);
114 #else
115  closure();
116 #endif
117 }
118 
119 /////////////////////////////////////////////////////////////////////////////
120 /// Wait until all submitted items of work are completed. This method
121 /// is blocking.
122 void TTaskGroup::Wait()
123 {
124 #ifdef R__USE_IMT
125  fCanRun = false;
126  CastToTG(fTaskContainer)->wait();
127  fCanRun = true;
128 #endif
129 }
130 } // namespace Experimental
131 } // namespace ROOT