BRE12
parallel_invoke.h
1 /*
2  Copyright 2005-2016 Intel Corporation. All Rights Reserved.
3 
4  This file is part of Threading Building Blocks. Threading Building Blocks is free software;
5  you can redistribute it and/or modify it under the terms of the GNU General Public License
6  version 2 as published by the Free Software Foundation. Threading Building Blocks is
7  distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
8  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
9  See the GNU General Public License for more details. You should have received a copy of
10  the GNU General Public License along with Threading Building Blocks; if not, write to the
11  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
12 
13  As a special exception, you may use this file as part of a free software library without
14  restriction. Specifically, if other files instantiate templates or use macros or inline
15  functions from this file, or you compile this file and link it with other files to produce
16  an executable, this file does not by itself cause the resulting executable to be covered
17  by the GNU General Public License. This exception does not however invalidate any other
18  reasons why the executable file might be covered by the GNU General Public License.
19 */
20 
21 #ifndef __TBB_parallel_invoke_H
22 #define __TBB_parallel_invoke_H
23 
24 #include "task.h"
25 
26 #if __TBB_VARIADIC_PARALLEL_INVOKE
27  #include <utility>
28 #endif
29 
30 namespace tbb {
31 
32 #if !__TBB_TASK_GROUP_CONTEXT
33 
34  struct task_group_context {};
35 #endif /* __TBB_TASK_GROUP_CONTEXT */
36 
38 namespace internal {
39  // Simple task object, executing user method
40  template<typename function>
41  class function_invoker : public task{
42  public:
43  function_invoker(const function& _function) : my_function(_function) {}
44  private:
45  const function &my_function;
46  /*override*/
47  task* execute()
48  {
49  my_function();
50  return NULL;
51  }
52  };
53 
54  // The class spawns two or three child tasks
55  template <size_t N, typename function1, typename function2, typename function3>
56  class spawner : public task {
57  private:
58  const function1& my_func1;
59  const function2& my_func2;
60  const function3& my_func3;
61  bool is_recycled;
62 
63  task* execute (){
64  if(is_recycled){
65  return NULL;
66  }else{
67  __TBB_ASSERT(N==2 || N==3, "Number of arguments passed to spawner is wrong");
68  set_ref_count(N);
69  recycle_as_safe_continuation();
70  internal::function_invoker<function2>* invoker2 = new (allocate_child()) internal::function_invoker<function2>(my_func2);
71  __TBB_ASSERT(invoker2, "Child task allocation failed");
72  spawn(*invoker2);
73  size_t n = N; // To prevent compiler warnings
74  if (n>2) {
75  internal::function_invoker<function3>* invoker3 = new (allocate_child()) internal::function_invoker<function3>(my_func3);
76  __TBB_ASSERT(invoker3, "Child task allocation failed");
77  spawn(*invoker3);
78  }
79  my_func1();
80  is_recycled = true;
81  return NULL;
82  }
83  } // execute
84 
85  public:
86  spawner(const function1& _func1, const function2& _func2, const function3& _func3) : my_func1(_func1), my_func2(_func2), my_func3(_func3), is_recycled(false) {}
87  };
88 
89  // Creates and spawns child tasks
90  class parallel_invoke_helper : public empty_task {
91  public:
92  // Dummy functor class
93  class parallel_invoke_noop {
94  public:
95  void operator() () const {}
96  };
97  // Creates a helper object with user-defined number of children expected
98  parallel_invoke_helper(int number_of_children)
99  {
100  set_ref_count(number_of_children + 1);
101  }
102 
103 #if __TBB_VARIADIC_PARALLEL_INVOKE
104  void add_children() {}
105  void add_children(tbb::task_group_context&) {}
106 
107  template <typename function>
108  void add_children(function&& _func)
109  {
110  internal::function_invoker<function>* invoker = new (allocate_child()) internal::function_invoker<function>(std::forward<function>(_func));
111  __TBB_ASSERT(invoker, "Child task allocation failed");
112  spawn(*invoker);
113  }
114 
115  template<typename function>
116  void add_children(function&& _func, tbb::task_group_context&)
117  {
118  add_children(std::forward<function>(_func));
119  }
120 
121  // Adds child(ren) task(s) and spawns them
122  template <typename function1, typename function2, typename... function>
123  void add_children(function1&& _func1, function2&& _func2, function&&... _func)
124  {
125  // The third argument is dummy, it is ignored actually.
126  parallel_invoke_noop noop;
127  typedef internal::spawner<2, function1, function2, parallel_invoke_noop> spawner_type;
128  spawner_type & sub_root = *new(allocate_child()) spawner_type(std::forward<function1>(_func1), std::forward<function2>(_func2), noop);
129  spawn(sub_root);
130  add_children(std::forward<function>(_func)...);
131  }
132 #else
133  // Adds child task and spawns it
134  template <typename function>
135  void add_children (const function &_func)
136  {
137  internal::function_invoker<function>* invoker = new (allocate_child()) internal::function_invoker<function>(_func);
138  __TBB_ASSERT(invoker, "Child task allocation failed");
139  spawn(*invoker);
140  }
141 
142  // Adds a task with multiple child tasks and spawns it
143  // two arguments
144  template <typename function1, typename function2>
145  void add_children (const function1& _func1, const function2& _func2)
146  {
147  // The third argument is dummy, it is ignored actually.
148  parallel_invoke_noop noop;
149  internal::spawner<2, function1, function2, parallel_invoke_noop>& sub_root = *new(allocate_child())internal::spawner<2, function1, function2, parallel_invoke_noop>(_func1, _func2, noop);
150  spawn(sub_root);
151  }
152  // three arguments
153  template <typename function1, typename function2, typename function3>
154  void add_children (const function1& _func1, const function2& _func2, const function3& _func3)
155  {
156  internal::spawner<3, function1, function2, function3>& sub_root = *new(allocate_child())internal::spawner<3, function1, function2, function3>(_func1, _func2, _func3);
157  spawn(sub_root);
158  }
159 #endif // __TBB_VARIADIC_PARALLEL_INVOKE
160 
161  // Waits for all child tasks
162  template <typename F0>
163  void run_and_finish(const F0& f0)
164  {
165  internal::function_invoker<F0>* invoker = new (allocate_child()) internal::function_invoker<F0>(f0);
166  __TBB_ASSERT(invoker, "Child task allocation failed");
167  spawn_and_wait_for_all(*invoker);
168  }
169  };
170  // The class destroys root if exception occurred as well as in normal case
171  class parallel_invoke_cleaner: internal::no_copy {
172  public:
173 #if __TBB_TASK_GROUP_CONTEXT
174  parallel_invoke_cleaner(int number_of_children, tbb::task_group_context& context)
175  : root(*new(task::allocate_root(context)) internal::parallel_invoke_helper(number_of_children))
176 #else
177  parallel_invoke_cleaner(int number_of_children, tbb::task_group_context&)
178  : root(*new(task::allocate_root()) internal::parallel_invoke_helper(number_of_children))
179 #endif /* !__TBB_TASK_GROUP_CONTEXT */
180  {}
181 
182  ~parallel_invoke_cleaner(){
183  root.destroy(root);
184  }
185  internal::parallel_invoke_helper& root;
186  };
187 
188 #if __TBB_VARIADIC_PARALLEL_INVOKE
189 // Determine whether the last parameter in a pack is task_group_context
190  template<typename... T> struct impl_selector; // to workaround a GCC bug
191 
192  template<typename T1, typename... T> struct impl_selector<T1, T...> {
193  typedef typename impl_selector<T...>::type type;
194  };
195 
196  template<typename T> struct impl_selector<T> {
197  typedef false_type type;
198  };
199  template<> struct impl_selector<task_group_context&> {
200  typedef true_type type;
201  };
202 
203  // Select task_group_context parameter from the back of a pack
204  inline task_group_context& get_context( task_group_context& tgc ) { return tgc; }
205 
206  template<typename T1, typename... T>
207  task_group_context& get_context( T1&& /*ignored*/, T&&... t )
208  { return get_context( std::forward<T>(t)... ); }
209 
210  // task_group_context is known to be at the back of the parameter pack
211  template<typename F0, typename F1, typename... F>
212  void parallel_invoke_impl(true_type, F0&& f0, F1&& f1, F&&... f) {
213  __TBB_STATIC_ASSERT(sizeof...(F)>0, "Variadic parallel_invoke implementation broken?");
214  // # of child tasks: f0, f1, and a task for each two elements of the pack except the last
215  const size_t number_of_children = 2 + sizeof...(F)/2;
216  parallel_invoke_cleaner cleaner(number_of_children, get_context(std::forward<F>(f)...));
217  parallel_invoke_helper& root = cleaner.root;
218 
219  root.add_children(std::forward<F>(f)...);
220  root.add_children(std::forward<F1>(f1));
221  root.run_and_finish(std::forward<F0>(f0));
222  }
223 
224  // task_group_context is not in the pack, needs to be added
225  template<typename F0, typename F1, typename... F>
226  void parallel_invoke_impl(false_type, F0&& f0, F1&& f1, F&&... f) {
227  tbb::task_group_context context;
228  // Add context to the arguments, and redirect to the other overload
229  parallel_invoke_impl(true_type(), std::forward<F0>(f0), std::forward<F1>(f1), std::forward<F>(f)..., context);
230  }
231 #endif
232 } // namespace internal
234 
238 
241 #if __TBB_VARIADIC_PARALLEL_INVOKE
242 
243 // parallel_invoke for two or more arguments via variadic templates
244 // presence of task_group_context is defined automatically
245 template<typename F0, typename F1, typename... F>
246 void parallel_invoke(F0&& f0, F1&& f1, F&&... f) {
247  typedef typename internal::impl_selector<internal::false_type, F...>::type selector_type;
248  internal::parallel_invoke_impl(selector_type(), std::forward<F0>(f0), std::forward<F1>(f1), std::forward<F>(f)...);
249 }
250 
251 #else
252 
253 // parallel_invoke with user-defined context
254 // two arguments
255 template<typename F0, typename F1 >
256 void parallel_invoke(const F0& f0, const F1& f1, tbb::task_group_context& context) {
257  internal::parallel_invoke_cleaner cleaner(2, context);
258  internal::parallel_invoke_helper& root = cleaner.root;
259 
260  root.add_children(f1);
261 
262  root.run_and_finish(f0);
263 }
264 
265 // three arguments
266 template<typename F0, typename F1, typename F2 >
267 void parallel_invoke(const F0& f0, const F1& f1, const F2& f2, tbb::task_group_context& context) {
268  internal::parallel_invoke_cleaner cleaner(3, context);
269  internal::parallel_invoke_helper& root = cleaner.root;
270 
271  root.add_children(f2);
272  root.add_children(f1);
273 
274  root.run_and_finish(f0);
275 }
276 
277 // four arguments
278 template<typename F0, typename F1, typename F2, typename F3>
279 void parallel_invoke(const F0& f0, const F1& f1, const F2& f2, const F3& f3,
280  tbb::task_group_context& context)
281 {
282  internal::parallel_invoke_cleaner cleaner(4, context);
283  internal::parallel_invoke_helper& root = cleaner.root;
284 
285  root.add_children(f3);
286  root.add_children(f2);
287  root.add_children(f1);
288 
289  root.run_and_finish(f0);
290 }
291 
292 // five arguments
293 template<typename F0, typename F1, typename F2, typename F3, typename F4 >
294 void parallel_invoke(const F0& f0, const F1& f1, const F2& f2, const F3& f3, const F4& f4,
295  tbb::task_group_context& context)
296 {
297  internal::parallel_invoke_cleaner cleaner(3, context);
298  internal::parallel_invoke_helper& root = cleaner.root;
299 
300  root.add_children(f4, f3);
301  root.add_children(f2, f1);
302 
303  root.run_and_finish(f0);
304 }
305 
306 // six arguments
307 template<typename F0, typename F1, typename F2, typename F3, typename F4, typename F5>
308 void parallel_invoke(const F0& f0, const F1& f1, const F2& f2, const F3& f3, const F4& f4, const F5& f5,
309  tbb::task_group_context& context)
310 {
311  internal::parallel_invoke_cleaner cleaner(3, context);
312  internal::parallel_invoke_helper& root = cleaner.root;
313 
314  root.add_children(f5, f4, f3);
315  root.add_children(f2, f1);
316 
317  root.run_and_finish(f0);
318 }
319 
320 // seven arguments
321 template<typename F0, typename F1, typename F2, typename F3, typename F4, typename F5, typename F6>
322 void parallel_invoke(const F0& f0, const F1& f1, const F2& f2, const F3& f3, const F4& f4,
323  const F5& f5, const F6& f6,
324  tbb::task_group_context& context)
325 {
326  internal::parallel_invoke_cleaner cleaner(3, context);
327  internal::parallel_invoke_helper& root = cleaner.root;
328 
329  root.add_children(f6, f5, f4);
330  root.add_children(f3, f2, f1);
331 
332  root.run_and_finish(f0);
333 }
334 
335 // eight arguments
336 template<typename F0, typename F1, typename F2, typename F3, typename F4,
337  typename F5, typename F6, typename F7>
338 void parallel_invoke(const F0& f0, const F1& f1, const F2& f2, const F3& f3, const F4& f4,
339  const F5& f5, const F6& f6, const F7& f7,
340  tbb::task_group_context& context)
341 {
342  internal::parallel_invoke_cleaner cleaner(4, context);
343  internal::parallel_invoke_helper& root = cleaner.root;
344 
345  root.add_children(f7, f6, f5);
346  root.add_children(f4, f3);
347  root.add_children(f2, f1);
348 
349  root.run_and_finish(f0);
350 }
351 
352 // nine arguments
353 template<typename F0, typename F1, typename F2, typename F3, typename F4,
354  typename F5, typename F6, typename F7, typename F8>
355 void parallel_invoke(const F0& f0, const F1& f1, const F2& f2, const F3& f3, const F4& f4,
356  const F5& f5, const F6& f6, const F7& f7, const F8& f8,
357  tbb::task_group_context& context)
358 {
359  internal::parallel_invoke_cleaner cleaner(4, context);
360  internal::parallel_invoke_helper& root = cleaner.root;
361 
362  root.add_children(f8, f7, f6);
363  root.add_children(f5, f4, f3);
364  root.add_children(f2, f1);
365 
366  root.run_and_finish(f0);
367 }
368 
369 // ten arguments
370 template<typename F0, typename F1, typename F2, typename F3, typename F4,
371  typename F5, typename F6, typename F7, typename F8, typename F9>
372 void parallel_invoke(const F0& f0, const F1& f1, const F2& f2, const F3& f3, const F4& f4,
373  const F5& f5, const F6& f6, const F7& f7, const F8& f8, const F9& f9,
374  tbb::task_group_context& context)
375 {
376  internal::parallel_invoke_cleaner cleaner(4, context);
377  internal::parallel_invoke_helper& root = cleaner.root;
378 
379  root.add_children(f9, f8, f7);
380  root.add_children(f6, f5, f4);
381  root.add_children(f3, f2, f1);
382 
383  root.run_and_finish(f0);
384 }
385 
386 // two arguments
387 template<typename F0, typename F1>
388 void parallel_invoke(const F0& f0, const F1& f1) {
389  task_group_context context;
390  parallel_invoke<F0, F1>(f0, f1, context);
391 }
392 // three arguments
393 template<typename F0, typename F1, typename F2>
394 void parallel_invoke(const F0& f0, const F1& f1, const F2& f2) {
395  task_group_context context;
396  parallel_invoke<F0, F1, F2>(f0, f1, f2, context);
397 }
398 // four arguments
399 template<typename F0, typename F1, typename F2, typename F3 >
400 void parallel_invoke(const F0& f0, const F1& f1, const F2& f2, const F3& f3) {
401  task_group_context context;
402  parallel_invoke<F0, F1, F2, F3>(f0, f1, f2, f3, context);
403 }
404 // five arguments
405 template<typename F0, typename F1, typename F2, typename F3, typename F4>
406 void parallel_invoke(const F0& f0, const F1& f1, const F2& f2, const F3& f3, const F4& f4) {
407  task_group_context context;
408  parallel_invoke<F0, F1, F2, F3, F4>(f0, f1, f2, f3, f4, context);
409 }
410 // six arguments
411 template<typename F0, typename F1, typename F2, typename F3, typename F4, typename F5>
412 void parallel_invoke(const F0& f0, const F1& f1, const F2& f2, const F3& f3, const F4& f4, const F5& f5) {
413  task_group_context context;
414  parallel_invoke<F0, F1, F2, F3, F4, F5>(f0, f1, f2, f3, f4, f5, context);
415 }
416 // seven arguments
417 template<typename F0, typename F1, typename F2, typename F3, typename F4, typename F5, typename F6>
418 void parallel_invoke(const F0& f0, const F1& f1, const F2& f2, const F3& f3, const F4& f4,
419  const F5& f5, const F6& f6)
420 {
421  task_group_context context;
422  parallel_invoke<F0, F1, F2, F3, F4, F5, F6>(f0, f1, f2, f3, f4, f5, f6, context);
423 }
424 // eight arguments
425 template<typename F0, typename F1, typename F2, typename F3, typename F4,
426  typename F5, typename F6, typename F7>
427 void parallel_invoke(const F0& f0, const F1& f1, const F2& f2, const F3& f3, const F4& f4,
428  const F5& f5, const F6& f6, const F7& f7)
429 {
430  task_group_context context;
431  parallel_invoke<F0, F1, F2, F3, F4, F5, F6, F7>(f0, f1, f2, f3, f4, f5, f6, f7, context);
432 }
433 // nine arguments
434 template<typename F0, typename F1, typename F2, typename F3, typename F4,
435  typename F5, typename F6, typename F7, typename F8>
436 void parallel_invoke(const F0& f0, const F1& f1, const F2& f2, const F3& f3, const F4& f4,
437  const F5& f5, const F6& f6, const F7& f7, const F8& f8)
438 {
439  task_group_context context;
440  parallel_invoke<F0, F1, F2, F3, F4, F5, F6, F7, F8>(f0, f1, f2, f3, f4, f5, f6, f7, f8, context);
441 }
442 // ten arguments
443 template<typename F0, typename F1, typename F2, typename F3, typename F4,
444  typename F5, typename F6, typename F7, typename F8, typename F9>
445 void parallel_invoke(const F0& f0, const F1& f1, const F2& f2, const F3& f3, const F4& f4,
446  const F5& f5, const F6& f6, const F7& f7, const F8& f8, const F9& f9)
447 {
448  task_group_context context;
449  parallel_invoke<F0, F1, F2, F3, F4, F5, F6, F7, F8, F9>(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, context);
450 }
451 #endif // __TBB_VARIADIC_PARALLEL_INVOKE
452 
453 
454 } // namespace
455 
456 #endif /* __TBB_parallel_invoke_H */
*/
Definition: material.h:665
Definition: _flow_graph_async_msg_impl.h:32
void parallel_invoke(const F0 &f0, const F1 &f1, tbb::task_group_context &context)
Executes a list of tasks in parallel and waits for all tasks to complete.
Definition: parallel_invoke.h:256
The namespace tbb contains all components of the library.
Definition: parallel_for.h:44