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