2 Copyright 2005-2014 Intel Corporation. All Rights Reserved.
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
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.
21 #ifndef __TBB_parallel_invoke_H
22 #define __TBB_parallel_invoke_H
26 #if __TBB_VARIADIC_PARALLEL_INVOKE
32 #if !__TBB_TASK_GROUP_CONTEXT
33 /** Dummy to avoid cluttering the bulk of the header with enormous amount of ifdefs. **/
34 struct task_group_context {};
35 #endif /* __TBB_TASK_GROUP_CONTEXT */
39 // Simple task object, executing user method
40 template<typename function>
41 class function_invoker : public task{
43 function_invoker(const function& _function) : my_function(_function) {}
45 const function &my_function;
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 {
58 const function1& my_func1;
59 const function2& my_func2;
60 const function3& my_func3;
67 __TBB_ASSERT(N==2 || N==3, "Number of arguments passed to spawner is wrong");
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");
73 size_t n = N; // To prevent compiler warnings
75 internal::function_invoker<function3>* invoker3 = new (allocate_child()) internal::function_invoker<function3>(my_func3);
76 __TBB_ASSERT(invoker3, "Child task allocation failed");
86 spawner(const function1& _func1, const function2& _func2, const function3& _func3) : my_func1(_func1), my_func2(_func2), my_func3(_func3), is_recycled(false) {}
89 // Creates and spawns child tasks
90 class parallel_invoke_helper : public empty_task {
92 // Dummy functor class
93 class parallel_invoke_noop {
95 void operator() () const {}
97 // Creates a helper object with user-defined number of children expected
98 parallel_invoke_helper(int number_of_children)
100 set_ref_count(number_of_children + 1);
103 #if __TBB_VARIADIC_PARALLEL_INVOKE
104 void add_children() {}
105 void add_children(tbb::task_group_context&) {}
107 template <typename function>
108 void add_children(function&& _func)
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");
115 template<typename function>
116 void add_children(function&& _func, tbb::task_group_context&)
118 add_children(std::forward<function>(_func));
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)
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);
130 add_children(std::forward<function>(_func)...);
133 // Adds child task and spawns it
134 template <typename function>
135 void add_children (const function &_func)
137 internal::function_invoker<function>* invoker = new (allocate_child()) internal::function_invoker<function>(_func);
138 __TBB_ASSERT(invoker, "Child task allocation failed");
142 // Adds a task with multiple child tasks and spawns it
144 template <typename function1, typename function2>
145 void add_children (const function1& _func1, const function2& _func2)
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);
153 template <typename function1, typename function2, typename function3>
154 void add_children (const function1& _func1, const function2& _func2, const function3& _func3)
156 internal::spawner<3, function1, function2, function3>& sub_root = *new(allocate_child())internal::spawner<3, function1, function2, function3>(_func1, _func2, _func3);
159 #endif // __TBB_VARIADIC_PARALLEL_INVOKE
161 // Waits for all child tasks
162 template <typename F0>
163 void run_and_finish(const F0& f0)
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);
170 // The class destroys root if exception occurred as well as in normal case
171 class parallel_invoke_cleaner: internal::no_copy {
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))
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 */
182 ~parallel_invoke_cleaner(){
185 internal::parallel_invoke_helper& root;
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
192 template<typename T1, typename... T> struct impl_selector<T1, T...> {
193 typedef typename impl_selector<T...>::type type;
196 template<typename T> struct impl_selector<T> {
197 typedef false_type type;
199 template<> struct impl_selector<task_group_context&> {
200 typedef true_type type;
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; }
206 template<typename T1, typename... T>
207 task_group_context& get_context( T1&& /*ignored*/, T&&... t )
208 { return get_context( std::forward<T>(t)... ); }
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;
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));
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);
232 } // namespace internal
235 /** \name parallel_invoke
238 //! Executes a list of tasks in parallel and waits for all tasks to complete.
239 /** @ingroup algorithms */
241 #if __TBB_VARIADIC_PARALLEL_INVOKE
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)...);
253 // parallel_invoke with user-defined context
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;
260 root.add_children(f1);
262 root.run_and_finish(f0);
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;
271 root.add_children(f2);
272 root.add_children(f1);
274 root.run_and_finish(f0);
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)
282 internal::parallel_invoke_cleaner cleaner(4, context);
283 internal::parallel_invoke_helper& root = cleaner.root;
285 root.add_children(f3);
286 root.add_children(f2);
287 root.add_children(f1);
289 root.run_and_finish(f0);
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)
297 internal::parallel_invoke_cleaner cleaner(3, context);
298 internal::parallel_invoke_helper& root = cleaner.root;
300 root.add_children(f4, f3);
301 root.add_children(f2, f1);
303 root.run_and_finish(f0);
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)
311 internal::parallel_invoke_cleaner cleaner(3, context);
312 internal::parallel_invoke_helper& root = cleaner.root;
314 root.add_children(f5, f4, f3);
315 root.add_children(f2, f1);
317 root.run_and_finish(f0);
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)
326 internal::parallel_invoke_cleaner cleaner(3, context);
327 internal::parallel_invoke_helper& root = cleaner.root;
329 root.add_children(f6, f5, f4);
330 root.add_children(f3, f2, f1);
332 root.run_and_finish(f0);
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)
342 internal::parallel_invoke_cleaner cleaner(4, context);
343 internal::parallel_invoke_helper& root = cleaner.root;
345 root.add_children(f7, f6, f5);
346 root.add_children(f4, f3);
347 root.add_children(f2, f1);
349 root.run_and_finish(f0);
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)
359 internal::parallel_invoke_cleaner cleaner(4, context);
360 internal::parallel_invoke_helper& root = cleaner.root;
362 root.add_children(f8, f7, f6);
363 root.add_children(f5, f4, f3);
364 root.add_children(f2, f1);
366 root.run_and_finish(f0);
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)
376 internal::parallel_invoke_cleaner cleaner(4, context);
377 internal::parallel_invoke_helper& root = cleaner.root;
379 root.add_children(f9, f8, f7);
380 root.add_children(f6, f5, f4);
381 root.add_children(f3, f2, f1);
383 root.run_and_finish(f0);
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);
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);
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);
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);
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);
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)
421 task_group_context context;
422 parallel_invoke<F0, F1, F2, F3, F4, F5, F6>(f0, f1, f2, f3, f4, f5, f6, context);
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)
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);
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)
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);
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)
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);
451 #endif // __TBB_VARIADIC_PARALLEL_INVOKE
456 #endif /* __TBB_parallel_invoke_H */