1 /*
2     Copyright (c) 2005-2017 Intel Corporation
3 
4     Licensed under the Apache License, Version 2.0 (the "License");
5     you may not use this file except in compliance with the License.
6     You may obtain a copy of the License at
7 
8         http://www.apache.org/licenses/LICENSE-2.0
9 
10     Unless required by applicable law or agreed to in writing, software
11     distributed under the License is distributed on an "AS IS" BASIS,
12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13     See the License for the specific language governing permissions and
14     limitations under the License.
15 
16 
17 
18 
19 */
20 
21 #ifndef __TBB_parallel_do_H
22 #define __TBB_parallel_do_H
23 
24 #include "internal/_range_iterator.h"
25 #include "internal/_template_helpers.h"
26 #include "task.h"
27 #include "aligned_space.h"
28 #include <iterator>
29 
30 namespace tbb {
31 namespace interface9 {
32 //! @cond INTERNAL
33 namespace internal {
34     template<typename Body, typename Item> class parallel_do_feeder_impl;
35 } // namespace internal
36 //! @endcond
37 
38 //! Class the user supplied algorithm body uses to add new tasks
39 /** \param Item Work item type **/
40     template<typename Item>
41     class parallel_do_feeder: ::tbb::internal::no_copy
42     {
parallel_do_feeder()43         parallel_do_feeder() {}
~parallel_do_feeder()44         virtual ~parallel_do_feeder () {}
45         virtual void internal_add_copy( const Item& item ) = 0;
46 #if __TBB_CPP11_RVALUE_REF_PRESENT
47         virtual void internal_add_move( Item&& item ) = 0;
48 #endif
49         template<typename Body_, typename Item_> friend class internal::parallel_do_feeder_impl;
50     public:
51         //! Add a work item to a running parallel_do.
add(const Item & item)52         void add( const Item& item ) {internal_add_copy(item);}
53 #if __TBB_CPP11_RVALUE_REF_PRESENT
add(Item && item)54         void add( Item&& item ) {internal_add_move(std::move(item));}
55 #endif
56     };
57 
58 //! @cond INTERNAL
59 namespace internal {
60     template<typename Body> class do_group_task;
61 
62     //! For internal use only.
63     /** Selects one of the two possible forms of function call member operator.
64         @ingroup algorithms **/
65     template<class Body, typename Item>
66     class parallel_do_operator_selector
67     {
68         typedef parallel_do_feeder<Item> Feeder;
69         template<typename A1, typename A2, typename CvItem >
internal_call(const Body & obj,__TBB_FORWARDING_REF (A1)arg1,A2 &,void (Body::*)(CvItem)const)70         static void internal_call( const Body& obj, __TBB_FORWARDING_REF(A1) arg1, A2&, void (Body::*)(CvItem) const ) {
71             obj(tbb::internal::forward<A1>(arg1));
72         }
73         template<typename A1, typename A2, typename CvItem >
internal_call(const Body & obj,__TBB_FORWARDING_REF (A1)arg1,A2 & arg2,void (Body::*)(CvItem,parallel_do_feeder<Item> &)const)74         static void internal_call( const Body& obj, __TBB_FORWARDING_REF(A1) arg1, A2& arg2, void (Body::*)(CvItem, parallel_do_feeder<Item>&) const ) {
75             obj(tbb::internal::forward<A1>(arg1), arg2);
76         }
77         template<typename A1, typename A2, typename CvItem >
internal_call(const Body & obj,__TBB_FORWARDING_REF (A1)arg1,A2 &,void (Body::*)(CvItem &)const)78         static void internal_call( const Body& obj, __TBB_FORWARDING_REF(A1) arg1, A2&, void (Body::*)(CvItem&) const ) {
79             obj(arg1);
80         }
81         template<typename A1, typename A2, typename CvItem >
internal_call(const Body & obj,__TBB_FORWARDING_REF (A1)arg1,A2 & arg2,void (Body::*)(CvItem &,parallel_do_feeder<Item> &)const)82         static void internal_call( const Body& obj, __TBB_FORWARDING_REF(A1) arg1, A2& arg2, void (Body::*)(CvItem&, parallel_do_feeder<Item>&) const ) {
83             obj(arg1, arg2);
84         }
85     public:
86         template<typename A1, typename A2>
call(const Body & obj,__TBB_FORWARDING_REF (A1)arg1,A2 & arg2)87         static void call( const Body& obj, __TBB_FORWARDING_REF(A1) arg1, A2& arg2 )
88         {
89             internal_call( obj, tbb::internal::forward<A1>(arg1), arg2, &Body::operator() );
90         }
91     };
92 
93     //! For internal use only.
94     /** Executes one iteration of a do.
95         @ingroup algorithms */
96     template<typename Body, typename Item>
97     class do_iteration_task: public task
98     {
99         typedef parallel_do_feeder_impl<Body, Item> feeder_type;
100 
101         Item my_value;
102         feeder_type& my_feeder;
103 
do_iteration_task(const Item & value,feeder_type & feeder)104         do_iteration_task( const Item& value, feeder_type& feeder ) :
105             my_value(value), my_feeder(feeder)
106         {}
107 
108 #if __TBB_CPP11_RVALUE_REF_PRESENT
do_iteration_task(Item && value,feeder_type & feeder)109         do_iteration_task( Item&& value, feeder_type& feeder ) :
110             my_value(std::move(value)), my_feeder(feeder)
111         {}
112 #endif
113 
execute()114         task* execute() __TBB_override
115         {
116             parallel_do_operator_selector<Body, Item>::call(*my_feeder.my_body, tbb::internal::move(my_value), my_feeder);
117             return NULL;
118         }
119 
120         template<typename Body_, typename Item_> friend class parallel_do_feeder_impl;
121     }; // class do_iteration_task
122 
123     template<typename Iterator, typename Body, typename Item>
124     class do_iteration_task_iter: public task
125     {
126         typedef parallel_do_feeder_impl<Body, Item> feeder_type;
127 
128         Iterator my_iter;
129         feeder_type& my_feeder;
130 
do_iteration_task_iter(const Iterator & iter,feeder_type & feeder)131         do_iteration_task_iter( const Iterator& iter, feeder_type& feeder ) :
132             my_iter(iter), my_feeder(feeder)
133         {}
134 
execute()135         task* execute() __TBB_override
136         {
137             parallel_do_operator_selector<Body, Item>::call(*my_feeder.my_body, *my_iter, my_feeder);
138             return NULL;
139         }
140 
141         template<typename Iterator_, typename Body_, typename Item_> friend class do_group_task_forward;
142         template<typename Body_, typename Item_> friend class do_group_task_input;
143         template<typename Iterator_, typename Body_, typename Item_> friend class do_task_iter;
144     }; // class do_iteration_task_iter
145 
146     //! For internal use only.
147     /** Implements new task adding procedure.
148         @ingroup algorithms **/
149     template<class Body, typename Item>
150     class parallel_do_feeder_impl : public parallel_do_feeder<Item>
151     {
152 #if __TBB_CPP11_RVALUE_REF_PRESENT
153         //Avoiding use of copy constructor in a virtual method if the type does not support it
internal_add_copy_impl(std::true_type,const Item & item)154         void internal_add_copy_impl(std::true_type, const Item& item) {
155             typedef do_iteration_task<Body, Item> iteration_type;
156             iteration_type& t = *new (task::allocate_additional_child_of(*my_barrier)) iteration_type(item, *this);
157             task::spawn(t);
158         }
internal_add_copy_impl(std::false_type,const Item &)159         void internal_add_copy_impl(std::false_type, const Item&) {
160             __TBB_ASSERT(false, "Overloading for r-value reference doesn't work or it's not movable and not copyable object");
161         }
internal_add_copy(const Item & item)162         void internal_add_copy( const Item& item ) __TBB_override
163         {
164 #if __TBB_CPP11_IS_COPY_CONSTRUCTIBLE_PRESENT
165             internal_add_copy_impl(typename std::is_copy_constructible<Item>::type(), item);
166 #else
167             internal_add_copy_impl(std::true_type(), item);
168 #endif
169         }
internal_add_move(Item && item)170         void internal_add_move( Item&& item ) __TBB_override
171         {
172             typedef do_iteration_task<Body, Item> iteration_type;
173             iteration_type& t = *new (task::allocate_additional_child_of(*my_barrier)) iteration_type(std::move(item), *this);
174             task::spawn(t);
175         }
176 #else /* ! __TBB_CPP11_RVALUE_REF_PRESENT */
177         void internal_add_copy(const Item& item) __TBB_override {
178             typedef do_iteration_task<Body, Item> iteration_type;
179             iteration_type& t = *new (task::allocate_additional_child_of(*my_barrier)) iteration_type(item, *this);
180             task::spawn(t);
181         }
182 #endif /* __TBB_CPP11_RVALUE_REF_PRESENT */
183     public:
184         const Body* my_body;
185         empty_task* my_barrier;
186 
parallel_do_feeder_impl()187         parallel_do_feeder_impl()
188         {
189             my_barrier = new( task::allocate_root() ) empty_task();
190             __TBB_ASSERT(my_barrier, "root task allocation failed");
191         }
192 
193 #if __TBB_TASK_GROUP_CONTEXT
parallel_do_feeder_impl(tbb::task_group_context & context)194         parallel_do_feeder_impl(tbb::task_group_context &context)
195         {
196             my_barrier = new( task::allocate_root(context) ) empty_task();
197             __TBB_ASSERT(my_barrier, "root task allocation failed");
198         }
199 #endif
200 
~parallel_do_feeder_impl()201         ~parallel_do_feeder_impl()
202         {
203             my_barrier->destroy(*my_barrier);
204         }
205     }; // class parallel_do_feeder_impl
206 
207 
208     //! For internal use only
209     /** Unpacks a block of iterations.
210         @ingroup algorithms */
211 
212     template<typename Iterator, typename Body, typename Item>
213     class do_group_task_forward: public task
214     {
215         static const size_t max_arg_size = 4;
216 
217         typedef parallel_do_feeder_impl<Body, Item> feeder_type;
218 
219         feeder_type& my_feeder;
220         Iterator my_first;
221         size_t my_size;
222 
do_group_task_forward(Iterator first,size_t size,feeder_type & feeder)223         do_group_task_forward( Iterator first, size_t size, feeder_type& feeder )
224             : my_feeder(feeder), my_first(first), my_size(size)
225         {}
226 
execute()227         task* execute() __TBB_override
228         {
229             typedef do_iteration_task_iter<Iterator, Body, Item> iteration_type;
230             __TBB_ASSERT( my_size>0, NULL );
231             task_list list;
232             task* t;
233             size_t k=0;
234             for(;;) {
235                 t = new( allocate_child() ) iteration_type( my_first, my_feeder );
236                 ++my_first;
237                 if( ++k==my_size ) break;
238                 list.push_back(*t);
239             }
240             set_ref_count(int(k+1));
241             spawn(list);
242             spawn_and_wait_for_all(*t);
243             return NULL;
244         }
245 
246         template<typename Iterator_, typename Body_, typename _Item> friend class do_task_iter;
247     }; // class do_group_task_forward
248 
249     template<typename Body, typename Item>
250     class do_group_task_input: public task
251     {
252         static const size_t max_arg_size = 4;
253 
254         typedef parallel_do_feeder_impl<Body, Item> feeder_type;
255 
256         feeder_type& my_feeder;
257         size_t my_size;
258         aligned_space<Item, max_arg_size> my_arg;
259 
do_group_task_input(feeder_type & feeder)260         do_group_task_input( feeder_type& feeder )
261             : my_feeder(feeder), my_size(0)
262         {}
263 
execute()264         task* execute() __TBB_override
265         {
266 #if __TBB_CPP11_RVALUE_REF_PRESENT
267             typedef std::move_iterator<Item*> Item_iterator;
268 #else
269             typedef Item* Item_iterator;
270 #endif
271             typedef do_iteration_task_iter<Item_iterator, Body, Item> iteration_type;
272             __TBB_ASSERT( my_size>0, NULL );
273             task_list list;
274             task* t;
275             size_t k=0;
276             for(;;) {
277                 t = new( allocate_child() ) iteration_type( Item_iterator(my_arg.begin() + k), my_feeder );
278                 if( ++k==my_size ) break;
279                 list.push_back(*t);
280             }
281             set_ref_count(int(k+1));
282             spawn(list);
283             spawn_and_wait_for_all(*t);
284             return NULL;
285         }
286 
~do_group_task_input()287         ~do_group_task_input(){
288             for( size_t k=0; k<my_size; ++k)
289                 (my_arg.begin() + k)->~Item();
290         }
291 
292         template<typename Iterator_, typename Body_, typename Item_> friend class do_task_iter;
293     }; // class do_group_task_input
294 
295     //! For internal use only.
296     /** Gets block of iterations and packages them into a do_group_task.
297         @ingroup algorithms */
298     template<typename Iterator, typename Body, typename Item>
299     class do_task_iter: public task
300     {
301         typedef parallel_do_feeder_impl<Body, Item> feeder_type;
302 
303     public:
do_task_iter(Iterator first,Iterator last,feeder_type & feeder)304         do_task_iter( Iterator first, Iterator last , feeder_type& feeder ) :
305             my_first(first), my_last(last), my_feeder(feeder)
306         {}
307 
308     private:
309         Iterator my_first;
310         Iterator my_last;
311         feeder_type& my_feeder;
312 
313         /* Do not merge run(xxx) and run_xxx() methods. They are separated in order
314             to make sure that compilers will eliminate unused argument of type xxx
315             (that is will not put it on stack). The sole purpose of this argument
316             is overload resolution.
317 
318             An alternative could be using template functions, but explicit specialization
319             of member function templates is not supported for non specialized class
320             templates. Besides template functions would always fall back to the least
321             efficient variant (the one for input iterators) in case of iterators having
322             custom tags derived from basic ones. */
execute()323         task* execute() __TBB_override
324         {
325             typedef typename std::iterator_traits<Iterator>::iterator_category iterator_tag;
326             return run( (iterator_tag*)NULL );
327         }
328 
329         /** This is the most restricted variant that operates on input iterators or
330             iterators with unknown tags (tags not derived from the standard ones). **/
run(void *)331         inline task* run( void* ) { return run_for_input_iterator(); }
332 
run_for_input_iterator()333         task* run_for_input_iterator() {
334             typedef do_group_task_input<Body, Item> block_type;
335 
336             block_type& t = *new( allocate_additional_child_of(*my_feeder.my_barrier) ) block_type(my_feeder);
337             size_t k=0;
338             while( !(my_first == my_last) ) {
339                 // Move semantics are automatically used when supported by the iterator
340                 new (t.my_arg.begin() + k) Item(*my_first);
341                 ++my_first;
342                 if( ++k==block_type::max_arg_size ) {
343                     if ( !(my_first == my_last) )
344                         recycle_to_reexecute();
345                     break;
346                 }
347             }
348             if( k==0 ) {
349                 destroy(t);
350                 return NULL;
351             } else {
352                 t.my_size = k;
353                 return &t;
354             }
355         }
356 
run(std::forward_iterator_tag *)357         inline task* run( std::forward_iterator_tag* ) { return run_for_forward_iterator(); }
358 
run_for_forward_iterator()359         task* run_for_forward_iterator() {
360             typedef do_group_task_forward<Iterator, Body, Item> block_type;
361 
362             Iterator first = my_first;
363             size_t k=0;
364             while( !(my_first==my_last) ) {
365                 ++my_first;
366                 if( ++k==block_type::max_arg_size ) {
367                     if ( !(my_first==my_last) )
368                         recycle_to_reexecute();
369                     break;
370                 }
371             }
372             return k==0 ? NULL : new( allocate_additional_child_of(*my_feeder.my_barrier) ) block_type(first, k, my_feeder);
373         }
374 
run(std::random_access_iterator_tag *)375         inline task* run( std::random_access_iterator_tag* ) { return run_for_random_access_iterator(); }
376 
run_for_random_access_iterator()377         task* run_for_random_access_iterator() {
378             typedef do_group_task_forward<Iterator, Body, Item> block_type;
379             typedef do_iteration_task_iter<Iterator, Body, Item> iteration_type;
380 
381             size_t k = static_cast<size_t>(my_last-my_first);
382             if( k > block_type::max_arg_size ) {
383                 Iterator middle = my_first + k/2;
384 
385                 empty_task& c = *new( allocate_continuation() ) empty_task;
386                 do_task_iter& b = *new( c.allocate_child() ) do_task_iter(middle, my_last, my_feeder);
387                 recycle_as_child_of(c);
388 
389                 my_last = middle;
390                 c.set_ref_count(2);
391                 c.spawn(b);
392                 return this;
393             }else if( k != 0 ) {
394                 task_list list;
395                 task* t;
396                 size_t k1=0;
397                 for(;;) {
398                     t = new( allocate_child() ) iteration_type(my_first, my_feeder);
399                     ++my_first;
400                     if( ++k1==k ) break;
401                     list.push_back(*t);
402                 }
403                 set_ref_count(int(k+1));
404                 spawn(list);
405                 spawn_and_wait_for_all(*t);
406             }
407             return NULL;
408         }
409     }; // class do_task_iter
410 
411     //! For internal use only.
412     /** Implements parallel iteration over a range.
413         @ingroup algorithms */
414     template<typename Iterator, typename Body, typename Item>
run_parallel_do(Iterator first,Iterator last,const Body & body,task_group_context & context)415     void run_parallel_do( Iterator first, Iterator last, const Body& body
416 #if __TBB_TASK_GROUP_CONTEXT
417         , task_group_context& context
418 #endif
419         )
420     {
421         typedef do_task_iter<Iterator, Body, Item> root_iteration_task;
422 #if __TBB_TASK_GROUP_CONTEXT
423         parallel_do_feeder_impl<Body, Item> feeder(context);
424 #else
425         parallel_do_feeder_impl<Body, Item> feeder;
426 #endif
427         feeder.my_body = &body;
428 
429         root_iteration_task &t = *new( feeder.my_barrier->allocate_child() ) root_iteration_task(first, last, feeder);
430 
431         feeder.my_barrier->set_ref_count(2);
432         feeder.my_barrier->spawn_and_wait_for_all(t);
433     }
434 
435     //! For internal use only.
436     /** Detects types of Body's operator function arguments.
437         @ingroup algorithms **/
438     template<typename Iterator, typename Body, typename Item>
select_parallel_do(Iterator first,Iterator last,const Body & body,void (Body::*)(Item)const,task_group_context & context)439     void select_parallel_do( Iterator first, Iterator last, const Body& body, void (Body::*)(Item) const
440 #if __TBB_TASK_GROUP_CONTEXT
441         , task_group_context& context
442 #endif
443         )
444     {
445         run_parallel_do<Iterator, Body, typename ::tbb::internal::strip<Item>::type>( first, last, body
446 #if __TBB_TASK_GROUP_CONTEXT
447             , context
448 #endif
449             );
450     }
451 
452     //! For internal use only.
453     /** Detects types of Body's operator function arguments.
454         @ingroup algorithms **/
455     template<typename Iterator, typename Body, typename Item, typename _Item>
select_parallel_do(Iterator first,Iterator last,const Body & body,void (Body::*)(Item,parallel_do_feeder<_Item> &)const,task_group_context & context)456     void select_parallel_do( Iterator first, Iterator last, const Body& body, void (Body::*)(Item, parallel_do_feeder<_Item>&) const
457 #if __TBB_TASK_GROUP_CONTEXT
458         , task_group_context& context
459 #endif
460         )
461     {
462         run_parallel_do<Iterator, Body, typename ::tbb::internal::strip<Item>::type>( first, last, body
463 #if __TBB_TASK_GROUP_CONTEXT
464             , context
465 #endif
466             );
467     }
468 
469 } // namespace internal
470 } // namespace interface9
471 //! @endcond
472 
473 /** \page parallel_do_body_req Requirements on parallel_do body
474     Class \c Body implementing the concept of parallel_do body must define:
475     - \code
476         B::operator()(
477                 cv_item_type item,
478                 parallel_do_feeder<item_type>& feeder
479         ) const
480 
481         OR
482 
483         B::operator()( cv_item_type& item ) const
484       \endcode                                               Process item.
485                                                              May be invoked concurrently  for the same \c this but different \c item.
486 
487     - \code item_type( const item_type& ) \endcode
488                                                              Copy a work item.
489     - \code ~item_type() \endcode                            Destroy a work item
490 **/
491 
492 /** \name parallel_do
493     See also requirements on \ref parallel_do_body_req "parallel_do Body". **/
494 //@{
495 //! Parallel iteration over a range, with optional addition of more work.
496 /** @ingroup algorithms */
497 template<typename Iterator, typename Body>
parallel_do(Iterator first,Iterator last,const Body & body)498 void parallel_do( Iterator first, Iterator last, const Body& body )
499 {
500     if ( first == last )
501         return;
502 #if __TBB_TASK_GROUP_CONTEXT
503     task_group_context context;
504 #endif
505     interface9::internal::select_parallel_do( first, last, body, &Body::operator()
506 #if __TBB_TASK_GROUP_CONTEXT
507         , context
508 #endif
509         );
510 }
511 
512 template<typename Range, typename Body>
parallel_do(Range & rng,const Body & body)513 void parallel_do(Range& rng, const Body& body) {
514     parallel_do(tbb::internal::first(rng), tbb::internal::last(rng), body);
515 }
516 
517 template<typename Range, typename Body>
parallel_do(const Range & rng,const Body & body)518 void parallel_do(const Range& rng, const Body& body) {
519     parallel_do(tbb::internal::first(rng), tbb::internal::last(rng), body);
520 }
521 
522 #if __TBB_TASK_GROUP_CONTEXT
523 //! Parallel iteration over a range, with optional addition of more work and user-supplied context
524 /** @ingroup algorithms */
525 template<typename Iterator, typename Body>
parallel_do(Iterator first,Iterator last,const Body & body,task_group_context & context)526 void parallel_do( Iterator first, Iterator last, const Body& body, task_group_context& context  )
527 {
528     if ( first == last )
529         return;
530     interface9::internal::select_parallel_do( first, last, body, &Body::operator(), context );
531 }
532 
533 template<typename Range, typename Body>
parallel_do(Range & rng,const Body & body,task_group_context & context)534 void parallel_do(Range& rng, const Body& body, task_group_context& context) {
535     parallel_do(tbb::internal::first(rng), tbb::internal::last(rng), body, context);
536 }
537 
538 template<typename Range, typename Body>
parallel_do(const Range & rng,const Body & body,task_group_context & context)539 void parallel_do(const Range& rng, const Body& body, task_group_context& context) {
540     parallel_do(tbb::internal::first(rng), tbb::internal::last(rng), body, context);
541 }
542 
543 #endif // __TBB_TASK_GROUP_CONTEXT
544 
545 //@}
546 
547 using interface9::parallel_do_feeder;
548 
549 } // namespace
550 
551 #endif /* __TBB_parallel_do_H */
552