]> git.sesse.net Git - casparcg/blob - dependencies64/tbb/include/tbb/parallel_invoke.h
bc055850af3efadcf940af6dca968a28da2e23b6
[casparcg] / dependencies64 / tbb / include / tbb / parallel_invoke.h
1 /*
2     Copyright 2005-2014 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     /** 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 */
36
37 //! @cond INTERNAL
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
233 //! @endcond
234
235 /** \name parallel_invoke
236     **/
237 //@{
238 //! Executes a list of tasks in parallel and waits for all tasks to complete.
239 /** @ingroup algorithms */
240
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 */