1 /*
2     Copyright (c) 2005-2021 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_for_each_H
18 #define __TBB_parallel_for_each_H
19 
20 #include "detail/_config.h"
21 #include "detail/_namespace_injection.h"
22 #include "detail/_exception.h"
23 #include "detail/_task.h"
24 #include "detail/_aligned_space.h"
25 #include "detail/_small_object_pool.h"
26 
27 #include "parallel_for.h"
28 #include "task_group.h" // task_group_context
29 
30 #include <iterator>
31 #include <type_traits>
32 
33 namespace tbb {
34 namespace detail {
35 #if __TBB_CPP20_CONCEPTS_PRESENT
36 namespace d1 {
37 template <typename Item>
38 class feeder;
39 
40 } // namespace d1
41 inline namespace d0 {
42 
43 template <typename Body, typename ItemType, typename FeederItemType>
44 concept parallel_for_each_body = requires( const std::remove_reference_t<Body>& body, ItemType&& item ) {
45                                     body(std::forward<ItemType>(item));
46                                  } ||
47                                  requires( const std::remove_reference_t<Body>& body, ItemType&& item,
48                                            tbb::detail::d1::feeder<FeederItemType>& feeder ) {
49                                     body(std::forward<ItemType>(item), feeder);
50 };
51 
52 } // namespace d0
53 #endif // __TBB_CPP20_CONCEPTS_PRESENT
54 namespace d2 {
55 template<typename Body, typename Item> class feeder_impl;
56 } // namespace d2
57 
58 namespace d1 {
59 //! Class the user supplied algorithm body uses to add new tasks
60 template<typename Item>
61 class feeder {
62     feeder() {}
63     feeder(const feeder&) = delete;
64     void operator=( const feeder&) = delete;
65 
66     virtual ~feeder () {}
67     virtual void internal_add_copy(const Item& item) = 0;
68     virtual void internal_add_move(Item&& item) = 0;
69 
70     template<typename Body_, typename Item_> friend class d2::feeder_impl;
71 public:
72     //! Add a work item to a running parallel_for_each.
73     void add(const Item& item) {internal_add_copy(item);}
74     void add(Item&& item) {internal_add_move(std::move(item));}
75 };
76 
77 } // namespace d1
78 
79 namespace d2 {
80 using namespace tbb::detail::d1;
81 /** Selects one of the two possible forms of function call member operator.
82     @ingroup algorithms **/
83 template<class Body>
84 struct parallel_for_each_operator_selector {
85 public:
86     template<typename ItemArg, typename FeederArg>
87     static auto call(const Body& body, ItemArg&& item, FeederArg*)
88     -> decltype(body(std::forward<ItemArg>(item)), void()) {
89         #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
90         // Suppression of Microsoft non-standard extension warnings
91         #pragma warning (push)
92         #pragma warning (disable: 4239)
93         #endif
94 
95         body(std::forward<ItemArg>(item));
96 
97         #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
98         #pragma warning (push)
99         #endif
100     }
101 
102     template<typename ItemArg, typename FeederArg>
103     static auto call(const Body& body, ItemArg&& item, FeederArg* feeder)
104     -> decltype(body(std::forward<ItemArg>(item), *feeder), void()) {
105         #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
106         // Suppression of Microsoft non-standard extension warnings
107         #pragma warning (push)
108         #pragma warning (disable: 4239)
109         #endif
110         __TBB_ASSERT(feeder, "Feeder was not created but should be");
111 
112         body(std::forward<ItemArg>(item), *feeder);
113 
114         #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
115         #pragma warning (push)
116         #endif
117     }
118 };
119 
120 template<typename Body, typename Item>
121 struct feeder_item_task: public task {
122     using feeder_type = feeder_impl<Body, Item>;
123 
124     template <typename ItemType>
125     feeder_item_task(ItemType&& input_item, feeder_type& feeder, small_object_allocator& alloc) :
126         item(std::forward<ItemType>(input_item)),
127         my_feeder(feeder),
128         my_allocator(alloc)
129     {}
130 
131     void finalize(const execution_data& ed) {
132         my_feeder.my_wait_context.release();
133         my_allocator.delete_object(this, ed);
134     }
135 
136     //! Hack for resolve ambiguity between calls to the body with and without moving the stored copy
137     //! Executing body with moving the copy should have higher priority
138     using first_priority = int;
139     using second_priority = double;
140 
141     template <typename BodyType, typename ItemType, typename FeederType>
142     static auto call(const BodyType& call_body, ItemType& call_item, FeederType& call_feeder, first_priority)
143     -> decltype(parallel_for_each_operator_selector<Body>::call(call_body, std::move(call_item), &call_feeder), void())
144     {
145         parallel_for_each_operator_selector<Body>::call(call_body, std::move(call_item), &call_feeder);
146     }
147 
148     template <typename BodyType, typename ItemType, typename FeederType>
149     static void call(const BodyType& call_body, ItemType& call_item, FeederType& call_feeder, second_priority) {
150         parallel_for_each_operator_selector<Body>::call(call_body, call_item, &call_feeder);
151     }
152 
153     task* execute(execution_data& ed) override {
154         call(my_feeder.my_body, item, my_feeder, first_priority{});
155         finalize(ed);
156         return nullptr;
157     }
158 
159     task* cancel(execution_data& ed) override {
160         finalize(ed);
161         return nullptr;
162     }
163 
164     Item item;
165     feeder_type& my_feeder;
166     small_object_allocator my_allocator;
167 }; // class feeder_item_task
168 
169 /** Implements new task adding procedure.
170     @ingroup algorithms **/
171 template<typename Body, typename Item>
172 class feeder_impl : public feeder<Item> {
173     // Avoiding use of copy constructor in a virtual method if the type does not support it
174     void internal_add_copy_impl(std::true_type, const Item& item) {
175         using feeder_task = feeder_item_task<Body, Item>;
176         small_object_allocator alloc;
177         auto task = alloc.new_object<feeder_task>(item, *this, alloc);
178 
179         my_wait_context.reserve();
180         spawn(*task, my_execution_context);
181     }
182 
183     void internal_add_copy_impl(std::false_type, const Item&) {
184         __TBB_ASSERT(false, "Overloading for r-value reference doesn't work or it's not movable and not copyable object");
185     }
186 
187     void internal_add_copy(const Item& item) override {
188         internal_add_copy_impl(typename std::is_copy_constructible<Item>::type(), item);
189     }
190 
191     void internal_add_move(Item&& item) override {
192         using feeder_task = feeder_item_task<Body, Item>;
193         small_object_allocator alloc{};
194         auto task = alloc.new_object<feeder_task>(std::move(item), *this, alloc);
195 
196         my_wait_context.reserve();
197         spawn(*task, my_execution_context);
198     }
199 public:
200     feeder_impl(const Body& body, wait_context& w_context, task_group_context &context)
201       : my_body(body),
202         my_wait_context(w_context)
203       , my_execution_context(context)
204     {}
205 
206     const Body& my_body;
207     wait_context& my_wait_context;
208     task_group_context& my_execution_context;
209 }; // class feeder_impl
210 
211 /** Execute computation under one element of the range
212     @ingroup algorithms **/
213 template<typename Iterator, typename Body, typename Item>
214 struct for_each_iteration_task: public task {
215     using feeder_type = feeder_impl<Body, Item>;
216 
217     for_each_iteration_task(Iterator input_item_ptr, const Body& body, feeder_impl<Body, Item>* feeder_ptr, wait_context& wait_context) :
218         item_ptr(input_item_ptr), my_body(body), my_feeder_ptr(feeder_ptr), parent_wait_context(wait_context)
219     {}
220 
221     void finalize() {
222         parent_wait_context.release();
223     }
224 
225     task* execute(execution_data&) override {
226         parallel_for_each_operator_selector<Body>::call(my_body, *item_ptr, my_feeder_ptr);
227         finalize();
228         return nullptr;
229     }
230 
231     task* cancel(execution_data&) override {
232         finalize();
233         return nullptr;
234     }
235 
236     Iterator item_ptr;
237     const Body& my_body;
238     feeder_impl<Body, Item>* my_feeder_ptr;
239     wait_context& parent_wait_context;
240 }; // class for_each_iteration_task
241 
242 // Helper to get the type of the iterator to the internal sequence of copies
243 // If the element can be passed to the body as an rvalue - this iterator should be move_iterator
244 template <typename Body, typename Item, typename = void>
245 struct input_iteration_task_iterator_helper {
246     // For input iterators we pass const lvalue reference to the body
247     // It is prohibited to take non-constant lvalue references for input iterators
248     using type = const Item*;
249 };
250 
251 template <typename Body, typename Item>
252 struct input_iteration_task_iterator_helper<Body, Item,
253     tbb::detail::void_t<decltype(parallel_for_each_operator_selector<Body>::call(std::declval<const Body&>(),
254                                                                                  std::declval<Item&&>(),
255                                                                                  std::declval<feeder_impl<Body, Item>*>()))>>
256 {
257     using type = std::move_iterator<Item*>;
258 };
259 
260 /** Split one block task to several(max_block_size) iteration tasks for input iterators
261     @ingroup algorithms **/
262 template <typename Body, typename Item>
263 struct input_block_handling_task : public task {
264     static constexpr size_t max_block_size = 4;
265 
266     using feeder_type = feeder_impl<Body, Item>;
267     using iteration_task_iterator_type = typename input_iteration_task_iterator_helper<Body, Item>::type;
268     using iteration_task = for_each_iteration_task<iteration_task_iterator_type, Body, Item>;
269 
270     input_block_handling_task(wait_context& root_wait_context, task_group_context& e_context,
271                               const Body& body, feeder_impl<Body, Item>* feeder_ptr, small_object_allocator& alloc)
272         :my_size(0), my_wait_context(0), my_root_wait_context(root_wait_context),
273          my_execution_context(e_context), my_allocator(alloc)
274     {
275         auto item_it = block_iteration_space.begin();
276         for (auto* it = task_pool.begin(); it != task_pool.end(); ++it) {
277             new (it) iteration_task(iteration_task_iterator_type(item_it++), body, feeder_ptr, my_wait_context);
278         }
279     }
280 
281     void finalize(const execution_data& ed) {
282         my_root_wait_context.release();
283         my_allocator.delete_object(this, ed);
284     }
285 
286     task* execute(execution_data& ed) override {
287         __TBB_ASSERT( my_size > 0, "Negative size was passed to task");
288         for (std::size_t counter = 1; counter < my_size; ++counter) {
289             my_wait_context.reserve();
290             spawn(*(task_pool.begin() + counter), my_execution_context);
291         }
292         my_wait_context.reserve();
293         execute_and_wait(*task_pool.begin(), my_execution_context,
294                          my_wait_context,    my_execution_context);
295 
296         // deallocate current task after children execution
297         finalize(ed);
298         return nullptr;
299     }
300 
301     task* cancel(execution_data& ed) override {
302         finalize(ed);
303         return nullptr;
304     }
305 
306     ~input_block_handling_task() {
307         for(std::size_t counter = 0; counter < max_block_size; ++counter) {
308             (task_pool.begin() + counter)->~iteration_task();
309             (block_iteration_space.begin() + counter)->~Item();
310         }
311     }
312 
313     aligned_space<Item, max_block_size> block_iteration_space;
314     aligned_space<iteration_task, max_block_size> task_pool;
315     std::size_t my_size;
316     wait_context my_wait_context;
317     wait_context& my_root_wait_context;
318     task_group_context& my_execution_context;
319     small_object_allocator my_allocator;
320 }; // class input_block_handling_task
321 
322 /** Split one block task to several(max_block_size) iteration tasks for forward iterators
323     @ingroup algorithms **/
324 template <typename Iterator, typename Body, typename Item>
325 struct forward_block_handling_task : public task {
326     static constexpr size_t max_block_size = 4;
327 
328     using iteration_task = for_each_iteration_task<Iterator, Body, Item>;
329 
330     forward_block_handling_task(Iterator first, std::size_t size,
331                                 wait_context& w_context, task_group_context& e_context,
332                                 const Body& body, feeder_impl<Body, Item>* feeder_ptr,
333                                 small_object_allocator& alloc)
334         : my_size(size), my_wait_context(0), my_root_wait_context(w_context),
335           my_execution_context(e_context), my_allocator(alloc)
336     {
337         auto* task_it = task_pool.begin();
338         for (std::size_t i = 0; i < size; i++) {
339             new (task_it++) iteration_task(first, body, feeder_ptr, my_wait_context);
340             ++first;
341         }
342     }
343 
344     void finalize(const execution_data& ed) {
345         my_root_wait_context.release();
346         my_allocator.delete_object(this, ed);
347     }
348 
349     task* execute(execution_data& ed) override {
350         __TBB_ASSERT( my_size > 0, "Negative size was passed to task");
351         for(std::size_t counter = 1; counter < my_size; ++counter) {
352             my_wait_context.reserve();
353             spawn(*(task_pool.begin() + counter), my_execution_context);
354         }
355         my_wait_context.reserve();
356         execute_and_wait(*task_pool.begin(), my_execution_context,
357                          my_wait_context,    my_execution_context);
358 
359         // deallocate current task after children execution
360         finalize(ed);
361         return nullptr;
362     }
363 
364     task* cancel(execution_data& ed) override {
365         finalize(ed);
366         return nullptr;
367     }
368 
369     ~forward_block_handling_task() {
370         for(std::size_t counter = 0; counter < my_size; ++counter) {
371             (task_pool.begin() + counter)->~iteration_task();
372         }
373     }
374 
375     aligned_space<iteration_task, max_block_size> task_pool;
376     std::size_t my_size;
377     wait_context my_wait_context;
378     wait_context& my_root_wait_context;
379     task_group_context& my_execution_context;
380     small_object_allocator my_allocator;
381 }; // class forward_block_handling_task
382 
383 /** Body for parallel_for algorithm.
384   * Allows to redirect operations under random access iterators range to the parallel_for algorithm.
385     @ingroup algorithms **/
386 template <typename Iterator, typename Body, typename Item>
387 class parallel_for_body_wrapper {
388     Iterator my_first;
389     const Body& my_body;
390     feeder_impl<Body, Item>* my_feeder_ptr;
391 public:
392     parallel_for_body_wrapper(Iterator first, const Body& body, feeder_impl<Body, Item>* feeder_ptr)
393         : my_first(first), my_body(body), my_feeder_ptr(feeder_ptr) {}
394 
395     void operator()(tbb::blocked_range<std::size_t> range) const {
396 #if __INTEL_COMPILER
397 #pragma ivdep
398 #endif
399         for (std::size_t count = range.begin(); count != range.end(); count++) {
400             parallel_for_each_operator_selector<Body>::call(my_body, *(my_first + count),
401                                                             my_feeder_ptr);
402         }
403     }
404 }; // class parallel_for_body_wrapper
405 
406 
407 /** Helper for getting iterators tag including inherited custom tags
408     @ingroup algorithms */
409 template<typename It>
410 using tag = typename std::iterator_traits<It>::iterator_category;
411 
412 template<typename It>
413 using iterator_tag_dispatch = typename
414     std::conditional<
415         std::is_base_of<std::random_access_iterator_tag, tag<It>>::value,
416         std::random_access_iterator_tag,
417         typename std::conditional<
418             std::is_base_of<std::forward_iterator_tag, tag<It>>::value,
419             std::forward_iterator_tag,
420             std::input_iterator_tag
421         >::type
422     >::type;
423 
424 template <typename Body, typename Iterator, typename Item>
425 using feeder_is_required = tbb::detail::void_t<decltype(std::declval<const Body>()(std::declval<typename std::iterator_traits<Iterator>::reference>(),
426                                                                                    std::declval<feeder<Item>&>()))>;
427 
428 // Creates feeder object only if the body can accept it
429 template <typename Iterator, typename Body, typename Item, typename = void>
430 struct feeder_holder {
431     feeder_holder( wait_context&, task_group_context&, const Body& ) {}
432 
433     feeder_impl<Body, Item>* feeder_ptr() { return nullptr; }
434 }; // class feeder_holder
435 
436 template <typename Iterator, typename Body, typename Item>
437 class feeder_holder<Iterator, Body, Item, feeder_is_required<Body, Iterator, Item>> {
438 public:
439     feeder_holder( wait_context& w_context, task_group_context& context, const Body& body )
440         : my_feeder(body, w_context, context) {}
441 
442     feeder_impl<Body, Item>* feeder_ptr() { return &my_feeder; }
443 private:
444     feeder_impl<Body, Item> my_feeder;
445 }; // class feeder_holder
446 
447 template <typename Iterator, typename Body, typename Item>
448 class for_each_root_task_base : public task {
449 public:
450     for_each_root_task_base(Iterator first, Iterator last, const Body& body, wait_context& w_context, task_group_context& e_context)
451         : my_first(first), my_last(last), my_wait_context(w_context), my_execution_context(e_context),
452           my_body(body), my_feeder_holder(my_wait_context, my_execution_context, my_body)
453     {
454         my_wait_context.reserve();
455     }
456 private:
457     task* cancel(execution_data&) override {
458         this->my_wait_context.release();
459         return nullptr;
460     }
461 protected:
462     Iterator my_first;
463     Iterator my_last;
464     wait_context& my_wait_context;
465     task_group_context& my_execution_context;
466     const Body& my_body;
467     feeder_holder<Iterator, Body, Item> my_feeder_holder;
468 }; // class for_each_root_task_base
469 
470 /** parallel_for_each algorithm root task - most generic version
471   * Splits input range to blocks
472     @ingroup algorithms **/
473 template <typename Iterator, typename Body, typename Item, typename IteratorTag = iterator_tag_dispatch<Iterator>>
474 class for_each_root_task : public for_each_root_task_base<Iterator, Body, Item>
475 {
476     using base_type = for_each_root_task_base<Iterator, Body, Item>;
477 public:
478     using base_type::base_type;
479 private:
480     task* execute(execution_data& ed) override {
481         using block_handling_type = input_block_handling_task<Body, Item>;
482 
483         if (this->my_first == this->my_last) {
484             this->my_wait_context.release();
485             return nullptr;
486         }
487 
488         this->my_wait_context.reserve();
489         small_object_allocator alloc{};
490         auto block_handling_task = alloc.new_object<block_handling_type>(ed, this->my_wait_context, this->my_execution_context,
491                                                                          this->my_body, this->my_feeder_holder.feeder_ptr(),
492                                                                          alloc);
493 
494         auto* block_iterator = block_handling_task->block_iteration_space.begin();
495         for (; !(this->my_first == this->my_last) && block_handling_task->my_size < block_handling_type::max_block_size; ++this->my_first) {
496             // Move semantics are automatically used when supported by the iterator
497             new (block_iterator++) Item(*this->my_first);
498             ++block_handling_task->my_size;
499         }
500 
501         // Do not access this after spawn to avoid races
502         spawn(*this, this->my_execution_context);
503         return block_handling_task;
504     }
505 }; // class for_each_root_task - most generic implementation
506 
507 /** parallel_for_each algorithm root task - forward iterator based specialization
508   * Splits input range to blocks
509     @ingroup algorithms **/
510 template <typename Iterator, typename Body, typename Item>
511 class for_each_root_task<Iterator, Body, Item, std::forward_iterator_tag>
512     : public for_each_root_task_base<Iterator, Body, Item>
513 {
514     using base_type = for_each_root_task_base<Iterator, Body, Item>;
515 public:
516     using base_type::base_type;
517 private:
518     task* execute(execution_data& ed) override {
519         using block_handling_type = forward_block_handling_task<Iterator, Body, Item>;
520         if (this->my_first == this->my_last) {
521             this->my_wait_context.release();
522             return nullptr;
523         }
524 
525         std::size_t block_size{0};
526         Iterator first_block_element = this->my_first;
527         for (; !(this->my_first == this->my_last) && block_size < block_handling_type::max_block_size; ++this->my_first) {
528             ++block_size;
529         }
530 
531         this->my_wait_context.reserve();
532         small_object_allocator alloc{};
533         auto block_handling_task = alloc.new_object<block_handling_type>(ed, first_block_element, block_size,
534                                                                          this->my_wait_context, this->my_execution_context,
535                                                                          this->my_body, this->my_feeder_holder.feeder_ptr(), alloc);
536 
537         // Do not access this after spawn to avoid races
538         spawn(*this, this->my_execution_context);
539         return block_handling_task;
540     }
541 }; // class for_each_root_task - forward iterator based specialization
542 
543 /** parallel_for_each algorithm root task - random access iterator based specialization
544   * Splits input range to blocks
545     @ingroup algorithms **/
546 template <typename Iterator, typename Body, typename Item>
547 class for_each_root_task<Iterator, Body, Item, std::random_access_iterator_tag>
548     : public for_each_root_task_base<Iterator, Body, Item>
549 {
550     using base_type = for_each_root_task_base<Iterator, Body, Item>;
551 public:
552     using base_type::base_type;
553 private:
554     task* execute(execution_data&) override {
555         tbb::parallel_for(
556             tbb::blocked_range<std::size_t>(0, std::distance(this->my_first, this->my_last)),
557             parallel_for_body_wrapper<Iterator, Body, Item>(this->my_first, this->my_body, this->my_feeder_holder.feeder_ptr())
558             , this->my_execution_context
559         );
560 
561         this->my_wait_context.release();
562         return nullptr;
563     }
564 }; // class for_each_root_task - random access iterator based specialization
565 
566 /** Helper for getting item type. If item type can be deduced from feeder - got it from feeder,
567     if feeder is generic - got item type from range.
568     @ingroup algorithms */
569 template<typename Body, typename Item, typename FeederArg>
570 auto feeder_argument_parser(void (Body::*)(Item, feeder<FeederArg>&) const) -> FeederArg;
571 
572 template<typename Body, typename>
573 decltype(feeder_argument_parser<Body>(&Body::operator())) get_item_type_impl(int); // for (T, feeder<T>)
574 template<typename Body, typename Item> Item get_item_type_impl(...); // stub
575 
576 template <typename Body, typename Item>
577 using get_item_type = decltype(get_item_type_impl<Body, Item>(0));
578 
579 #if __TBB_CPP20_CONCEPTS_PRESENT
580 template <typename Body, typename ItemType>
581 using feeder_item_type = std::remove_cvref_t<get_item_type<Body, ItemType>>;
582 
583 template <typename Body, typename Iterator>
584 concept parallel_for_each_iterator_body =
585     parallel_for_each_body<Body, iterator_reference_type<Iterator>, feeder_item_type<Body, iterator_reference_type<Iterator>>>;
586 
587 template <typename Body, typename Range>
588 concept parallel_for_each_range_body =
589     parallel_for_each_body<Body, range_reference_type<Range>, feeder_item_type<Body, range_reference_type<Range>>>;
590 #endif
591 
592 /** Implements parallel iteration over a range.
593     @ingroup algorithms */
594 template<typename Iterator, typename Body>
595 void run_parallel_for_each( Iterator first, Iterator last, const Body& body, task_group_context& context)
596 {
597     if (!(first == last)) {
598         using ItemType = get_item_type<Body, typename std::iterator_traits<Iterator>::value_type>;
599         wait_context w_context(0);
600 
601         for_each_root_task<Iterator, Body, ItemType> root_task(first, last, body, w_context, context);
602 
603         execute_and_wait(root_task, context, w_context, context);
604     }
605 }
606 
607 /** \page parallel_for_each_body_req Requirements on parallel_for_each body
608     Class \c Body implementing the concept of parallel_for_each body must define:
609     - \code
610         B::operator()(
611                 cv_item_type item,
612                 feeder<item_type>& feeder
613         ) const
614 
615         OR
616 
617         B::operator()( cv_item_type& item ) const
618       \endcode                                               Process item.
619                                                              May be invoked concurrently  for the same \c this but different \c item.
620 
621     - \code item_type( const item_type& ) \endcode
622                                                              Copy a work item.
623     - \code ~item_type() \endcode                            Destroy a work item
624 **/
625 
626 /** \name parallel_for_each
627     See also requirements on \ref parallel_for_each_body_req "parallel_for_each Body". **/
628 //@{
629 //! Parallel iteration over a range, with optional addition of more work.
630 /** @ingroup algorithms */
631 template<typename Iterator, typename Body>
632     __TBB_requires(std::input_iterator<Iterator> && parallel_for_each_iterator_body<Body, Iterator>)
633 void parallel_for_each(Iterator first, Iterator last, const Body& body) {
634     task_group_context context(PARALLEL_FOR_EACH);
635     run_parallel_for_each<Iterator, Body>(first, last, body, context);
636 }
637 
638 template<typename Range, typename Body>
639     __TBB_requires(container_based_sequence<Range, std::input_iterator_tag> && parallel_for_each_range_body<Body, Range>)
640 void parallel_for_each(Range& rng, const Body& body) {
641     parallel_for_each(std::begin(rng), std::end(rng), body);
642 }
643 
644 template<typename Range, typename Body>
645     __TBB_requires(container_based_sequence<Range, std::input_iterator_tag> && parallel_for_each_range_body<Body, Range>)
646 void parallel_for_each(const Range& rng, const Body& body) {
647     parallel_for_each(std::begin(rng), std::end(rng), body);
648 }
649 
650 //! Parallel iteration over a range, with optional addition of more work and user-supplied context
651 /** @ingroup algorithms */
652 template<typename Iterator, typename Body>
653     __TBB_requires(std::input_iterator<Iterator> && parallel_for_each_iterator_body<Body, Iterator>)
654 void parallel_for_each(Iterator first, Iterator last, const Body& body, task_group_context& context) {
655     run_parallel_for_each<Iterator, Body>(first, last, body, context);
656 }
657 
658 template<typename Range, typename Body>
659     __TBB_requires(container_based_sequence<Range, std::input_iterator_tag> && parallel_for_each_range_body<Body, Range>)
660 void parallel_for_each(Range& rng, const Body& body, task_group_context& context) {
661     parallel_for_each(std::begin(rng), std::end(rng), body, context);
662 }
663 
664 template<typename Range, typename Body>
665     __TBB_requires(container_based_sequence<Range, std::input_iterator_tag> && parallel_for_each_range_body<Body, Range>)
666 void parallel_for_each(const Range& rng, const Body& body, task_group_context& context) {
667     parallel_for_each(std::begin(rng), std::end(rng), body, context);
668 }
669 
670 } // namespace d2
671 } // namespace detail
672 //! @endcond
673 //@}
674 
675 inline namespace v1 {
676 using detail::d2::parallel_for_each;
677 using detail::d1::feeder;
678 } // namespace v1
679 
680 } // namespace tbb
681 
682 #endif /* __TBB_parallel_for_each_H */
683