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