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