149e08aacStbbdev /*
2b15aabb3Stbbdev     Copyright (c) 2005-2021 Intel Corporation
349e08aacStbbdev 
449e08aacStbbdev     Licensed under the Apache License, Version 2.0 (the "License");
549e08aacStbbdev     you may not use this file except in compliance with the License.
649e08aacStbbdev     You may obtain a copy of the License at
749e08aacStbbdev 
849e08aacStbbdev         http://www.apache.org/licenses/LICENSE-2.0
949e08aacStbbdev 
1049e08aacStbbdev     Unless required by applicable law or agreed to in writing, software
1149e08aacStbbdev     distributed under the License is distributed on an "AS IS" BASIS,
1249e08aacStbbdev     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1349e08aacStbbdev     See the License for the specific language governing permissions and
1449e08aacStbbdev     limitations under the License.
1549e08aacStbbdev */
1649e08aacStbbdev 
1749e08aacStbbdev #ifndef __TBB_parallel_for_each_H
1849e08aacStbbdev #define __TBB_parallel_for_each_H
1949e08aacStbbdev 
2049e08aacStbbdev #include "detail/_config.h"
2149e08aacStbbdev #include "detail/_namespace_injection.h"
2249e08aacStbbdev #include "detail/_exception.h"
2349e08aacStbbdev #include "detail/_task.h"
2449e08aacStbbdev #include "detail/_aligned_space.h"
2549e08aacStbbdev #include "detail/_small_object_pool.h"
2649e08aacStbbdev 
2749e08aacStbbdev #include "parallel_for.h"
2849e08aacStbbdev #include "task_group.h" // task_group_context
2949e08aacStbbdev 
3049e08aacStbbdev #include <iterator>
3149e08aacStbbdev #include <type_traits>
3249e08aacStbbdev 
3349e08aacStbbdev namespace tbb {
3449e08aacStbbdev namespace detail {
35*478de5b1Stbbdev #if __TBB_CPP20_CONCEPTS_PRESENT
36*478de5b1Stbbdev namespace d1 {
37*478de5b1Stbbdev template <typename Item>
38*478de5b1Stbbdev class feeder;
39*478de5b1Stbbdev 
40*478de5b1Stbbdev } // namespace d1
41*478de5b1Stbbdev inline namespace d0 {
42*478de5b1Stbbdev 
43*478de5b1Stbbdev template <typename Body, typename ItemType, typename FeederItemType>
44*478de5b1Stbbdev concept parallel_for_each_body = requires( const std::remove_reference_t<Body>& body, ItemType&& item ) {
45*478de5b1Stbbdev                                     body(std::forward<ItemType>(item));
46*478de5b1Stbbdev                                  } ||
47*478de5b1Stbbdev                                  requires( const std::remove_reference_t<Body>& body, ItemType&& item,
48*478de5b1Stbbdev                                            tbb::detail::d1::feeder<FeederItemType>& feeder ) {
49*478de5b1Stbbdev                                     body(std::forward<ItemType>(item), feeder);
50*478de5b1Stbbdev };
51*478de5b1Stbbdev 
52*478de5b1Stbbdev } // namespace d0
53*478de5b1Stbbdev #endif // __TBB_CPP20_CONCEPTS_PRESENT
54b15aabb3Stbbdev namespace d2 {
5549e08aacStbbdev template<typename Body, typename Item> class feeder_impl;
56b15aabb3Stbbdev } // namespace d2
5749e08aacStbbdev 
58b15aabb3Stbbdev namespace d1 {
5949e08aacStbbdev //! Class the user supplied algorithm body uses to add new tasks
6049e08aacStbbdev template<typename Item>
6149e08aacStbbdev class feeder {
6249e08aacStbbdev     feeder() {}
6349e08aacStbbdev     feeder(const feeder&) = delete;
6449e08aacStbbdev     void operator=( const feeder&) = delete;
6549e08aacStbbdev 
6649e08aacStbbdev     virtual ~feeder () {}
6749e08aacStbbdev     virtual void internal_add_copy(const Item& item) = 0;
6849e08aacStbbdev     virtual void internal_add_move(Item&& item) = 0;
6949e08aacStbbdev 
70*478de5b1Stbbdev     template<typename Body_, typename Item_> friend class d2::feeder_impl;
7149e08aacStbbdev public:
7249e08aacStbbdev     //! Add a work item to a running parallel_for_each.
7349e08aacStbbdev     void add(const Item& item) {internal_add_copy(item);}
7449e08aacStbbdev     void add(Item&& item) {internal_add_move(std::move(item));}
7549e08aacStbbdev };
7649e08aacStbbdev 
77b15aabb3Stbbdev } // namespace d1
78b15aabb3Stbbdev 
79b15aabb3Stbbdev namespace d2 {
80b15aabb3Stbbdev using namespace tbb::detail::d1;
8149e08aacStbbdev /** Selects one of the two possible forms of function call member operator.
8249e08aacStbbdev     @ingroup algorithms **/
83b15aabb3Stbbdev template<class Body>
84b15aabb3Stbbdev struct parallel_for_each_operator_selector {
8549e08aacStbbdev public:
8649e08aacStbbdev     template<typename ItemArg, typename FeederArg>
87b15aabb3Stbbdev     static auto call(const Body& body, ItemArg&& item, FeederArg*)
8849e08aacStbbdev     -> decltype(body(std::forward<ItemArg>(item)), void()) {
8949e08aacStbbdev         #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
9049e08aacStbbdev         // Suppression of Microsoft non-standard extension warnings
9149e08aacStbbdev         #pragma warning (push)
9249e08aacStbbdev         #pragma warning (disable: 4239)
9349e08aacStbbdev         #endif
9449e08aacStbbdev 
9549e08aacStbbdev         body(std::forward<ItemArg>(item));
9649e08aacStbbdev 
9749e08aacStbbdev         #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
9849e08aacStbbdev         #pragma warning (push)
9949e08aacStbbdev         #endif
10049e08aacStbbdev     }
10149e08aacStbbdev 
10249e08aacStbbdev     template<typename ItemArg, typename FeederArg>
103b15aabb3Stbbdev     static auto call(const Body& body, ItemArg&& item, FeederArg* feeder)
104b15aabb3Stbbdev     -> decltype(body(std::forward<ItemArg>(item), *feeder), void()) {
10549e08aacStbbdev         #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
10649e08aacStbbdev         // Suppression of Microsoft non-standard extension warnings
10749e08aacStbbdev         #pragma warning (push)
10849e08aacStbbdev         #pragma warning (disable: 4239)
10949e08aacStbbdev         #endif
110b15aabb3Stbbdev         __TBB_ASSERT(feeder, "Feeder was not created but should be");
11149e08aacStbbdev 
112b15aabb3Stbbdev         body(std::forward<ItemArg>(item), *feeder);
11349e08aacStbbdev 
11449e08aacStbbdev         #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
11549e08aacStbbdev         #pragma warning (push)
11649e08aacStbbdev         #endif
11749e08aacStbbdev     }
11849e08aacStbbdev };
11949e08aacStbbdev 
12049e08aacStbbdev template<typename Body, typename Item>
12149e08aacStbbdev struct feeder_item_task: public task {
12249e08aacStbbdev     using feeder_type = feeder_impl<Body, Item>;
12349e08aacStbbdev 
12449e08aacStbbdev     template <typename ItemType>
12549e08aacStbbdev     feeder_item_task(ItemType&& input_item, feeder_type& feeder, small_object_allocator& alloc) :
12649e08aacStbbdev         item(std::forward<ItemType>(input_item)),
12749e08aacStbbdev         my_feeder(feeder),
12849e08aacStbbdev         my_allocator(alloc)
12949e08aacStbbdev     {}
13049e08aacStbbdev 
13149e08aacStbbdev     void finalize(const execution_data& ed) {
13249e08aacStbbdev         my_feeder.my_wait_context.release();
13349e08aacStbbdev         my_allocator.delete_object(this, ed);
13449e08aacStbbdev     }
13549e08aacStbbdev 
136b15aabb3Stbbdev     //! Hack for resolve ambiguity between calls to the body with and without moving the stored copy
137b15aabb3Stbbdev     //! Executing body with moving the copy should have higher priority
138b15aabb3Stbbdev     using first_priority = int;
139b15aabb3Stbbdev     using second_priority = double;
140b15aabb3Stbbdev 
141b15aabb3Stbbdev     template <typename BodyType, typename ItemType, typename FeederType>
142b15aabb3Stbbdev     static auto call(const BodyType& call_body, ItemType& call_item, FeederType& call_feeder, first_priority)
143b15aabb3Stbbdev     -> decltype(parallel_for_each_operator_selector<Body>::call(call_body, std::move(call_item), &call_feeder), void())
144b15aabb3Stbbdev     {
145b15aabb3Stbbdev         parallel_for_each_operator_selector<Body>::call(call_body, std::move(call_item), &call_feeder);
146b15aabb3Stbbdev     }
147b15aabb3Stbbdev 
148b15aabb3Stbbdev     template <typename BodyType, typename ItemType, typename FeederType>
149b15aabb3Stbbdev     static void call(const BodyType& call_body, ItemType& call_item, FeederType& call_feeder, second_priority) {
150b15aabb3Stbbdev         parallel_for_each_operator_selector<Body>::call(call_body, call_item, &call_feeder);
151b15aabb3Stbbdev     }
152b15aabb3Stbbdev 
15349e08aacStbbdev     task* execute(execution_data& ed) override {
154b15aabb3Stbbdev         call(my_feeder.my_body, item, my_feeder, first_priority{});
15549e08aacStbbdev         finalize(ed);
15649e08aacStbbdev         return nullptr;
15749e08aacStbbdev     }
15849e08aacStbbdev 
15949e08aacStbbdev     task* cancel(execution_data& ed) override {
16049e08aacStbbdev         finalize(ed);
16149e08aacStbbdev         return nullptr;
16249e08aacStbbdev     }
16349e08aacStbbdev 
16449e08aacStbbdev     Item item;
16549e08aacStbbdev     feeder_type& my_feeder;
16649e08aacStbbdev     small_object_allocator my_allocator;
16749e08aacStbbdev }; // class feeder_item_task
16849e08aacStbbdev 
16949e08aacStbbdev /** Implements new task adding procedure.
17049e08aacStbbdev     @ingroup algorithms **/
171b15aabb3Stbbdev template<typename Body, typename Item>
17249e08aacStbbdev class feeder_impl : public feeder<Item> {
17349e08aacStbbdev     // Avoiding use of copy constructor in a virtual method if the type does not support it
17449e08aacStbbdev     void internal_add_copy_impl(std::true_type, const Item& item) {
17549e08aacStbbdev         using feeder_task = feeder_item_task<Body, Item>;
17649e08aacStbbdev         small_object_allocator alloc;
17749e08aacStbbdev         auto task = alloc.new_object<feeder_task>(item, *this, alloc);
17849e08aacStbbdev 
17949e08aacStbbdev         my_wait_context.reserve();
18049e08aacStbbdev         spawn(*task, my_execution_context);
18149e08aacStbbdev     }
18249e08aacStbbdev 
18349e08aacStbbdev     void internal_add_copy_impl(std::false_type, const Item&) {
18449e08aacStbbdev         __TBB_ASSERT(false, "Overloading for r-value reference doesn't work or it's not movable and not copyable object");
18549e08aacStbbdev     }
18649e08aacStbbdev 
18749e08aacStbbdev     void internal_add_copy(const Item& item) override {
18849e08aacStbbdev         internal_add_copy_impl(typename std::is_copy_constructible<Item>::type(), item);
18949e08aacStbbdev     }
19049e08aacStbbdev 
19149e08aacStbbdev     void internal_add_move(Item&& item) override {
19249e08aacStbbdev         using feeder_task = feeder_item_task<Body, Item>;
19349e08aacStbbdev         small_object_allocator alloc{};
19449e08aacStbbdev         auto task = alloc.new_object<feeder_task>(std::move(item), *this, alloc);
19549e08aacStbbdev 
19649e08aacStbbdev         my_wait_context.reserve();
19749e08aacStbbdev         spawn(*task, my_execution_context);
19849e08aacStbbdev     }
19949e08aacStbbdev public:
200b15aabb3Stbbdev     feeder_impl(const Body& body, wait_context& w_context, task_group_context &context)
201b15aabb3Stbbdev       : my_body(body),
202b15aabb3Stbbdev         my_wait_context(w_context)
20349e08aacStbbdev       , my_execution_context(context)
20449e08aacStbbdev     {}
20549e08aacStbbdev 
20649e08aacStbbdev     const Body& my_body;
207b15aabb3Stbbdev     wait_context& my_wait_context;
20849e08aacStbbdev     task_group_context& my_execution_context;
20949e08aacStbbdev }; // class feeder_impl
21049e08aacStbbdev 
21149e08aacStbbdev /** Execute computation under one element of the range
21249e08aacStbbdev     @ingroup algorithms **/
21349e08aacStbbdev template<typename Iterator, typename Body, typename Item>
21449e08aacStbbdev struct for_each_iteration_task: public task {
21549e08aacStbbdev     using feeder_type = feeder_impl<Body, Item>;
21649e08aacStbbdev 
217b15aabb3Stbbdev     for_each_iteration_task(Iterator input_item_ptr, const Body& body, feeder_impl<Body, Item>* feeder_ptr, wait_context& wait_context) :
218b15aabb3Stbbdev         item_ptr(input_item_ptr), my_body(body), my_feeder_ptr(feeder_ptr), parent_wait_context(wait_context)
21949e08aacStbbdev     {}
22049e08aacStbbdev 
22149e08aacStbbdev     void finalize() {
22249e08aacStbbdev         parent_wait_context.release();
22349e08aacStbbdev     }
22449e08aacStbbdev 
22549e08aacStbbdev     task* execute(execution_data&) override {
226b15aabb3Stbbdev         parallel_for_each_operator_selector<Body>::call(my_body, *item_ptr, my_feeder_ptr);
22749e08aacStbbdev         finalize();
22849e08aacStbbdev         return nullptr;
22949e08aacStbbdev     }
23049e08aacStbbdev 
23149e08aacStbbdev     task* cancel(execution_data&) override {
23249e08aacStbbdev         finalize();
23349e08aacStbbdev         return nullptr;
23449e08aacStbbdev     }
23549e08aacStbbdev 
23649e08aacStbbdev     Iterator item_ptr;
237b15aabb3Stbbdev     const Body& my_body;
238b15aabb3Stbbdev     feeder_impl<Body, Item>* my_feeder_ptr;
23949e08aacStbbdev     wait_context& parent_wait_context;
240b15aabb3Stbbdev }; // class for_each_iteration_task
241b15aabb3Stbbdev 
242b15aabb3Stbbdev // Helper to get the type of the iterator to the internal sequence of copies
243b15aabb3Stbbdev // If the element can be passed to the body as an rvalue - this iterator should be move_iterator
244b15aabb3Stbbdev template <typename Body, typename Item, typename = void>
245b15aabb3Stbbdev struct input_iteration_task_iterator_helper {
246b15aabb3Stbbdev     // For input iterators we pass const lvalue reference to the body
247b15aabb3Stbbdev     // It is prohibited to take non-constant lvalue references for input iterators
248b15aabb3Stbbdev     using type = const Item*;
249b15aabb3Stbbdev };
250b15aabb3Stbbdev 
251b15aabb3Stbbdev template <typename Body, typename Item>
252b15aabb3Stbbdev struct input_iteration_task_iterator_helper<Body, Item,
253b15aabb3Stbbdev     tbb::detail::void_t<decltype(parallel_for_each_operator_selector<Body>::call(std::declval<const Body&>(),
254b15aabb3Stbbdev                                                                                  std::declval<Item&&>(),
255b15aabb3Stbbdev                                                                                  std::declval<feeder_impl<Body, Item>*>()))>>
256b15aabb3Stbbdev {
257b15aabb3Stbbdev     using type = std::move_iterator<Item*>;
258b15aabb3Stbbdev };
25949e08aacStbbdev 
26049e08aacStbbdev /** Split one block task to several(max_block_size) iteration tasks for input iterators
26149e08aacStbbdev     @ingroup algorithms **/
26249e08aacStbbdev template <typename Body, typename Item>
26349e08aacStbbdev struct input_block_handling_task : public task {
26449e08aacStbbdev     static constexpr size_t max_block_size = 4;
26549e08aacStbbdev 
26649e08aacStbbdev     using feeder_type = feeder_impl<Body, Item>;
267b15aabb3Stbbdev     using iteration_task_iterator_type = typename input_iteration_task_iterator_helper<Body, Item>::type;
268b15aabb3Stbbdev     using iteration_task = for_each_iteration_task<iteration_task_iterator_type, Body, Item>;
26949e08aacStbbdev 
270b15aabb3Stbbdev     input_block_handling_task(wait_context& root_wait_context, task_group_context& e_context,
271b15aabb3Stbbdev                               const Body& body, feeder_impl<Body, Item>* feeder_ptr, small_object_allocator& alloc)
272b15aabb3Stbbdev         :my_size(0), my_wait_context(0), my_root_wait_context(root_wait_context),
273b15aabb3Stbbdev          my_execution_context(e_context), my_allocator(alloc)
27449e08aacStbbdev     {
275b15aabb3Stbbdev         auto item_it = block_iteration_space.begin();
27649e08aacStbbdev         for (auto* it = task_pool.begin(); it != task_pool.end(); ++it) {
277b15aabb3Stbbdev             new (it) iteration_task(iteration_task_iterator_type(item_it++), body, feeder_ptr, my_wait_context);
27849e08aacStbbdev         }
27949e08aacStbbdev     }
28049e08aacStbbdev 
28149e08aacStbbdev     void finalize(const execution_data& ed) {
282b15aabb3Stbbdev         my_root_wait_context.release();
28349e08aacStbbdev         my_allocator.delete_object(this, ed);
28449e08aacStbbdev     }
28549e08aacStbbdev 
28649e08aacStbbdev     task* execute(execution_data& ed) override {
28749e08aacStbbdev         __TBB_ASSERT( my_size > 0, "Negative size was passed to task");
28849e08aacStbbdev         for (std::size_t counter = 1; counter < my_size; ++counter) {
28949e08aacStbbdev             my_wait_context.reserve();
290b15aabb3Stbbdev             spawn(*(task_pool.begin() + counter), my_execution_context);
29149e08aacStbbdev         }
29249e08aacStbbdev         my_wait_context.reserve();
293b15aabb3Stbbdev         execute_and_wait(*task_pool.begin(), my_execution_context,
294b15aabb3Stbbdev                          my_wait_context,    my_execution_context);
29549e08aacStbbdev 
29649e08aacStbbdev         // deallocate current task after children execution
29749e08aacStbbdev         finalize(ed);
29849e08aacStbbdev         return nullptr;
29949e08aacStbbdev     }
30049e08aacStbbdev 
30149e08aacStbbdev     task* cancel(execution_data& ed) override {
30249e08aacStbbdev         finalize(ed);
30349e08aacStbbdev         return nullptr;
30449e08aacStbbdev     }
30549e08aacStbbdev 
30649e08aacStbbdev     ~input_block_handling_task() {
30749e08aacStbbdev         for(std::size_t counter = 0; counter < max_block_size; ++counter) {
30849e08aacStbbdev             (task_pool.begin() + counter)->~iteration_task();
30949e08aacStbbdev             (block_iteration_space.begin() + counter)->~Item();
31049e08aacStbbdev         }
31149e08aacStbbdev     }
31249e08aacStbbdev 
31349e08aacStbbdev     aligned_space<Item, max_block_size> block_iteration_space;
31449e08aacStbbdev     aligned_space<iteration_task, max_block_size> task_pool;
31549e08aacStbbdev     std::size_t my_size;
31649e08aacStbbdev     wait_context my_wait_context;
317b15aabb3Stbbdev     wait_context& my_root_wait_context;
318b15aabb3Stbbdev     task_group_context& my_execution_context;
31949e08aacStbbdev     small_object_allocator my_allocator;
320b15aabb3Stbbdev }; // class input_block_handling_task
32149e08aacStbbdev 
32249e08aacStbbdev /** Split one block task to several(max_block_size) iteration tasks for forward iterators
32349e08aacStbbdev     @ingroup algorithms **/
32449e08aacStbbdev template <typename Iterator, typename Body, typename Item>
32549e08aacStbbdev struct forward_block_handling_task : public task {
32649e08aacStbbdev     static constexpr size_t max_block_size = 4;
32749e08aacStbbdev 
32849e08aacStbbdev     using iteration_task = for_each_iteration_task<Iterator, Body, Item>;
32949e08aacStbbdev 
330b15aabb3Stbbdev     forward_block_handling_task(Iterator first, std::size_t size,
331b15aabb3Stbbdev                                 wait_context& w_context, task_group_context& e_context,
332b15aabb3Stbbdev                                 const Body& body, feeder_impl<Body, Item>* feeder_ptr,
333b15aabb3Stbbdev                                 small_object_allocator& alloc)
334b15aabb3Stbbdev         : my_size(size), my_wait_context(0), my_root_wait_context(w_context),
335b15aabb3Stbbdev           my_execution_context(e_context), my_allocator(alloc)
33649e08aacStbbdev     {
33749e08aacStbbdev         auto* task_it = task_pool.begin();
33849e08aacStbbdev         for (std::size_t i = 0; i < size; i++) {
339b15aabb3Stbbdev             new (task_it++) iteration_task(first, body, feeder_ptr, my_wait_context);
34049e08aacStbbdev             ++first;
34149e08aacStbbdev         }
34249e08aacStbbdev     }
34349e08aacStbbdev 
34449e08aacStbbdev     void finalize(const execution_data& ed) {
345b15aabb3Stbbdev         my_root_wait_context.release();
34649e08aacStbbdev         my_allocator.delete_object(this, ed);
34749e08aacStbbdev     }
34849e08aacStbbdev 
34949e08aacStbbdev     task* execute(execution_data& ed) override {
35049e08aacStbbdev         __TBB_ASSERT( my_size > 0, "Negative size was passed to task");
35149e08aacStbbdev         for(std::size_t counter = 1; counter < my_size; ++counter) {
35249e08aacStbbdev             my_wait_context.reserve();
353b15aabb3Stbbdev             spawn(*(task_pool.begin() + counter), my_execution_context);
35449e08aacStbbdev         }
35549e08aacStbbdev         my_wait_context.reserve();
356b15aabb3Stbbdev         execute_and_wait(*task_pool.begin(), my_execution_context,
357b15aabb3Stbbdev                          my_wait_context,    my_execution_context);
35849e08aacStbbdev 
35949e08aacStbbdev         // deallocate current task after children execution
36049e08aacStbbdev         finalize(ed);
36149e08aacStbbdev         return nullptr;
36249e08aacStbbdev     }
36349e08aacStbbdev 
36449e08aacStbbdev     task* cancel(execution_data& ed) override {
36549e08aacStbbdev         finalize(ed);
36649e08aacStbbdev         return nullptr;
36749e08aacStbbdev     }
36849e08aacStbbdev 
36949e08aacStbbdev     ~forward_block_handling_task() {
37049e08aacStbbdev         for(std::size_t counter = 0; counter < my_size; ++counter) {
37149e08aacStbbdev             (task_pool.begin() + counter)->~iteration_task();
37249e08aacStbbdev         }
37349e08aacStbbdev     }
37449e08aacStbbdev 
37549e08aacStbbdev     aligned_space<iteration_task, max_block_size> task_pool;
37649e08aacStbbdev     std::size_t my_size;
37749e08aacStbbdev     wait_context my_wait_context;
378b15aabb3Stbbdev     wait_context& my_root_wait_context;
379b15aabb3Stbbdev     task_group_context& my_execution_context;
38049e08aacStbbdev     small_object_allocator my_allocator;
38149e08aacStbbdev }; // class forward_block_handling_task
38249e08aacStbbdev 
38349e08aacStbbdev /** Body for parallel_for algorithm.
38449e08aacStbbdev   * Allows to redirect operations under random access iterators range to the parallel_for algorithm.
38549e08aacStbbdev     @ingroup algorithms **/
38649e08aacStbbdev template <typename Iterator, typename Body, typename Item>
38749e08aacStbbdev class parallel_for_body_wrapper {
38849e08aacStbbdev     Iterator my_first;
389b15aabb3Stbbdev     const Body& my_body;
390b15aabb3Stbbdev     feeder_impl<Body, Item>* my_feeder_ptr;
39149e08aacStbbdev public:
392b15aabb3Stbbdev     parallel_for_body_wrapper(Iterator first, const Body& body, feeder_impl<Body, Item>* feeder_ptr)
393b15aabb3Stbbdev         : my_first(first), my_body(body), my_feeder_ptr(feeder_ptr) {}
39449e08aacStbbdev 
39549e08aacStbbdev     void operator()(tbb::blocked_range<std::size_t> range) const {
39649e08aacStbbdev #if __INTEL_COMPILER
39749e08aacStbbdev #pragma ivdep
39849e08aacStbbdev #endif
39949e08aacStbbdev         for (std::size_t count = range.begin(); count != range.end(); count++) {
400b15aabb3Stbbdev             parallel_for_each_operator_selector<Body>::call(my_body, *(my_first + count),
401b15aabb3Stbbdev                                                             my_feeder_ptr);
40249e08aacStbbdev         }
40349e08aacStbbdev     }
40449e08aacStbbdev }; // class parallel_for_body_wrapper
40549e08aacStbbdev 
40649e08aacStbbdev 
40749e08aacStbbdev /** Helper for getting iterators tag including inherited custom tags
40849e08aacStbbdev     @ingroup algorithms */
40949e08aacStbbdev template<typename It>
41049e08aacStbbdev using tag = typename std::iterator_traits<It>::iterator_category;
41149e08aacStbbdev 
41249e08aacStbbdev template<typename It>
41349e08aacStbbdev using iterator_tag_dispatch = typename
41449e08aacStbbdev     std::conditional<
41549e08aacStbbdev         std::is_base_of<std::random_access_iterator_tag, tag<It>>::value,
41649e08aacStbbdev         std::random_access_iterator_tag,
41749e08aacStbbdev         typename std::conditional<
41849e08aacStbbdev             std::is_base_of<std::forward_iterator_tag, tag<It>>::value,
41949e08aacStbbdev             std::forward_iterator_tag,
42049e08aacStbbdev             std::input_iterator_tag
42149e08aacStbbdev         >::type
42249e08aacStbbdev     >::type;
42349e08aacStbbdev 
424b15aabb3Stbbdev template <typename Body, typename Iterator, typename Item>
425b15aabb3Stbbdev using feeder_is_required = tbb::detail::void_t<decltype(std::declval<const Body>()(std::declval<typename std::iterator_traits<Iterator>::reference>(),
426b15aabb3Stbbdev                                                                                    std::declval<feeder<Item>&>()))>;
427b15aabb3Stbbdev 
428b15aabb3Stbbdev // Creates feeder object only if the body can accept it
429b15aabb3Stbbdev template <typename Iterator, typename Body, typename Item, typename = void>
430b15aabb3Stbbdev struct feeder_holder {
431b15aabb3Stbbdev     feeder_holder( wait_context&, task_group_context&, const Body& ) {}
432b15aabb3Stbbdev 
433b15aabb3Stbbdev     feeder_impl<Body, Item>* feeder_ptr() { return nullptr; }
434b15aabb3Stbbdev }; // class feeder_holder
435b15aabb3Stbbdev 
436b15aabb3Stbbdev template <typename Iterator, typename Body, typename Item>
437b15aabb3Stbbdev class feeder_holder<Iterator, Body, Item, feeder_is_required<Body, Iterator, Item>> {
438b15aabb3Stbbdev public:
439b15aabb3Stbbdev     feeder_holder( wait_context& w_context, task_group_context& context, const Body& body )
440b15aabb3Stbbdev         : my_feeder(body, w_context, context) {}
441b15aabb3Stbbdev 
442b15aabb3Stbbdev     feeder_impl<Body, Item>* feeder_ptr() { return &my_feeder; }
443b15aabb3Stbbdev private:
444b15aabb3Stbbdev     feeder_impl<Body, Item> my_feeder;
445b15aabb3Stbbdev }; // class feeder_holder
446b15aabb3Stbbdev 
447b15aabb3Stbbdev template <typename Iterator, typename Body, typename Item>
448b15aabb3Stbbdev class for_each_root_task_base : public task {
449b15aabb3Stbbdev public:
450b15aabb3Stbbdev     for_each_root_task_base(Iterator first, Iterator last, const Body& body, wait_context& w_context, task_group_context& e_context)
451b15aabb3Stbbdev         : my_first(first), my_last(last), my_wait_context(w_context), my_execution_context(e_context),
452b15aabb3Stbbdev           my_body(body), my_feeder_holder(my_wait_context, my_execution_context, my_body)
453b15aabb3Stbbdev     {
454b15aabb3Stbbdev         my_wait_context.reserve();
455b15aabb3Stbbdev     }
456b15aabb3Stbbdev private:
457b15aabb3Stbbdev     task* cancel(execution_data&) override {
458b15aabb3Stbbdev         this->my_wait_context.release();
459b15aabb3Stbbdev         return nullptr;
460b15aabb3Stbbdev     }
461b15aabb3Stbbdev protected:
462b15aabb3Stbbdev     Iterator my_first;
463b15aabb3Stbbdev     Iterator my_last;
464b15aabb3Stbbdev     wait_context& my_wait_context;
465b15aabb3Stbbdev     task_group_context& my_execution_context;
466b15aabb3Stbbdev     const Body& my_body;
467b15aabb3Stbbdev     feeder_holder<Iterator, Body, Item> my_feeder_holder;
468b15aabb3Stbbdev }; // class for_each_root_task_base
469b15aabb3Stbbdev 
47049e08aacStbbdev /** parallel_for_each algorithm root task - most generic version
47149e08aacStbbdev   * Splits input range to blocks
47249e08aacStbbdev     @ingroup algorithms **/
47349e08aacStbbdev template <typename Iterator, typename Body, typename Item, typename IteratorTag = iterator_tag_dispatch<Iterator>>
474b15aabb3Stbbdev class for_each_root_task : public for_each_root_task_base<Iterator, Body, Item>
47549e08aacStbbdev {
476b15aabb3Stbbdev     using base_type = for_each_root_task_base<Iterator, Body, Item>;
477b15aabb3Stbbdev public:
478b15aabb3Stbbdev     using base_type::base_type;
47949e08aacStbbdev private:
48049e08aacStbbdev     task* execute(execution_data& ed) override {
48149e08aacStbbdev         using block_handling_type = input_block_handling_task<Body, Item>;
48249e08aacStbbdev 
483b15aabb3Stbbdev         if (this->my_first == this->my_last) {
484b15aabb3Stbbdev             this->my_wait_context.release();
48549e08aacStbbdev             return nullptr;
48649e08aacStbbdev         }
48749e08aacStbbdev 
488b15aabb3Stbbdev         this->my_wait_context.reserve();
48949e08aacStbbdev         small_object_allocator alloc{};
490b15aabb3Stbbdev         auto block_handling_task = alloc.new_object<block_handling_type>(ed, this->my_wait_context, this->my_execution_context,
491b15aabb3Stbbdev                                                                          this->my_body, this->my_feeder_holder.feeder_ptr(),
492b15aabb3Stbbdev                                                                          alloc);
49349e08aacStbbdev 
49449e08aacStbbdev         auto* block_iterator = block_handling_task->block_iteration_space.begin();
495b15aabb3Stbbdev         for (; !(this->my_first == this->my_last) && block_handling_task->my_size < block_handling_type::max_block_size; ++this->my_first) {
49649e08aacStbbdev             // Move semantics are automatically used when supported by the iterator
497b15aabb3Stbbdev             new (block_iterator++) Item(*this->my_first);
49849e08aacStbbdev             ++block_handling_task->my_size;
49949e08aacStbbdev         }
50049e08aacStbbdev 
50149e08aacStbbdev         // Do not access this after spawn to avoid races
502b15aabb3Stbbdev         spawn(*this, this->my_execution_context);
50349e08aacStbbdev         return block_handling_task;
50449e08aacStbbdev     }
50549e08aacStbbdev }; // class for_each_root_task - most generic implementation
50649e08aacStbbdev 
50749e08aacStbbdev /** parallel_for_each algorithm root task - forward iterator based specialization
50849e08aacStbbdev   * Splits input range to blocks
50949e08aacStbbdev     @ingroup algorithms **/
51049e08aacStbbdev template <typename Iterator, typename Body, typename Item>
511b15aabb3Stbbdev class for_each_root_task<Iterator, Body, Item, std::forward_iterator_tag>
512b15aabb3Stbbdev     : public for_each_root_task_base<Iterator, Body, Item>
51349e08aacStbbdev {
514b15aabb3Stbbdev     using base_type = for_each_root_task_base<Iterator, Body, Item>;
515b15aabb3Stbbdev public:
516b15aabb3Stbbdev     using base_type::base_type;
51749e08aacStbbdev private:
51849e08aacStbbdev     task* execute(execution_data& ed) override {
51949e08aacStbbdev         using block_handling_type = forward_block_handling_task<Iterator, Body, Item>;
520b15aabb3Stbbdev         if (this->my_first == this->my_last) {
521b15aabb3Stbbdev             this->my_wait_context.release();
52249e08aacStbbdev             return nullptr;
52349e08aacStbbdev         }
52449e08aacStbbdev 
52549e08aacStbbdev         std::size_t block_size{0};
526b15aabb3Stbbdev         Iterator first_block_element = this->my_first;
527b15aabb3Stbbdev         for (; !(this->my_first == this->my_last) && block_size < block_handling_type::max_block_size; ++this->my_first) {
52849e08aacStbbdev             ++block_size;
52949e08aacStbbdev         }
53049e08aacStbbdev 
531b15aabb3Stbbdev         this->my_wait_context.reserve();
53249e08aacStbbdev         small_object_allocator alloc{};
533b15aabb3Stbbdev         auto block_handling_task = alloc.new_object<block_handling_type>(ed, first_block_element, block_size,
534b15aabb3Stbbdev                                                                          this->my_wait_context, this->my_execution_context,
535b15aabb3Stbbdev                                                                          this->my_body, this->my_feeder_holder.feeder_ptr(), alloc);
53649e08aacStbbdev 
53749e08aacStbbdev         // Do not access this after spawn to avoid races
538b15aabb3Stbbdev         spawn(*this, this->my_execution_context);
53949e08aacStbbdev         return block_handling_task;
54049e08aacStbbdev     }
54149e08aacStbbdev }; // class for_each_root_task - forward iterator based specialization
54249e08aacStbbdev 
54349e08aacStbbdev /** parallel_for_each algorithm root task - random access iterator based specialization
54449e08aacStbbdev   * Splits input range to blocks
54549e08aacStbbdev     @ingroup algorithms **/
54649e08aacStbbdev template <typename Iterator, typename Body, typename Item>
547b15aabb3Stbbdev class for_each_root_task<Iterator, Body, Item, std::random_access_iterator_tag>
548b15aabb3Stbbdev     : public for_each_root_task_base<Iterator, Body, Item>
54949e08aacStbbdev {
550b15aabb3Stbbdev     using base_type = for_each_root_task_base<Iterator, Body, Item>;
551b15aabb3Stbbdev public:
552b15aabb3Stbbdev     using base_type::base_type;
55349e08aacStbbdev private:
55449e08aacStbbdev     task* execute(execution_data&) override {
55549e08aacStbbdev         tbb::parallel_for(
556b15aabb3Stbbdev             tbb::blocked_range<std::size_t>(0, std::distance(this->my_first, this->my_last)),
557b15aabb3Stbbdev             parallel_for_body_wrapper<Iterator, Body, Item>(this->my_first, this->my_body, this->my_feeder_holder.feeder_ptr())
558b15aabb3Stbbdev             , this->my_execution_context
55949e08aacStbbdev         );
56049e08aacStbbdev 
561b15aabb3Stbbdev         this->my_wait_context.release();
56249e08aacStbbdev         return nullptr;
56349e08aacStbbdev     }
56449e08aacStbbdev }; // class for_each_root_task - random access iterator based specialization
56549e08aacStbbdev 
56649e08aacStbbdev /** Helper for getting item type. If item type can be deduced from feeder - got it from feeder,
56749e08aacStbbdev     if feeder is generic - got item type from range.
56849e08aacStbbdev     @ingroup algorithms */
56949e08aacStbbdev template<typename Body, typename Item, typename FeederArg>
57049e08aacStbbdev auto feeder_argument_parser(void (Body::*)(Item, feeder<FeederArg>&) const) -> FeederArg;
57149e08aacStbbdev 
57249e08aacStbbdev template<typename Body, typename>
57349e08aacStbbdev decltype(feeder_argument_parser<Body>(&Body::operator())) get_item_type_impl(int); // for (T, feeder<T>)
57449e08aacStbbdev template<typename Body, typename Item> Item get_item_type_impl(...); // stub
57549e08aacStbbdev 
57649e08aacStbbdev template <typename Body, typename Item>
57749e08aacStbbdev using get_item_type = decltype(get_item_type_impl<Body, Item>(0));
57849e08aacStbbdev 
579*478de5b1Stbbdev #if __TBB_CPP20_CONCEPTS_PRESENT
580*478de5b1Stbbdev template <typename Body, typename ItemType>
581*478de5b1Stbbdev using feeder_item_type = std::remove_cvref_t<get_item_type<Body, ItemType>>;
582*478de5b1Stbbdev 
583*478de5b1Stbbdev template <typename Body, typename Iterator>
584*478de5b1Stbbdev concept parallel_for_each_iterator_body =
585*478de5b1Stbbdev     parallel_for_each_body<Body, iterator_reference_type<Iterator>, feeder_item_type<Body, iterator_reference_type<Iterator>>>;
586*478de5b1Stbbdev 
587*478de5b1Stbbdev template <typename Body, typename Range>
588*478de5b1Stbbdev concept parallel_for_each_range_body =
589*478de5b1Stbbdev     parallel_for_each_body<Body, range_reference_type<Range>, feeder_item_type<Body, range_reference_type<Range>>>;
590*478de5b1Stbbdev #endif
591*478de5b1Stbbdev 
59249e08aacStbbdev /** Implements parallel iteration over a range.
59349e08aacStbbdev     @ingroup algorithms */
59449e08aacStbbdev template<typename Iterator, typename Body>
59549e08aacStbbdev void run_parallel_for_each( Iterator first, Iterator last, const Body& body, task_group_context& context)
59649e08aacStbbdev {
59749e08aacStbbdev     if (!(first == last)) {
598b15aabb3Stbbdev         using ItemType = get_item_type<Body, typename std::iterator_traits<Iterator>::value_type>;
599b15aabb3Stbbdev         wait_context w_context(0);
60049e08aacStbbdev 
601b15aabb3Stbbdev         for_each_root_task<Iterator, Body, ItemType> root_task(first, last, body, w_context, context);
60249e08aacStbbdev 
603b15aabb3Stbbdev         execute_and_wait(root_task, context, w_context, context);
60449e08aacStbbdev     }
60549e08aacStbbdev }
60649e08aacStbbdev 
60749e08aacStbbdev /** \page parallel_for_each_body_req Requirements on parallel_for_each body
60849e08aacStbbdev     Class \c Body implementing the concept of parallel_for_each body must define:
60949e08aacStbbdev     - \code
61049e08aacStbbdev         B::operator()(
61149e08aacStbbdev                 cv_item_type item,
61249e08aacStbbdev                 feeder<item_type>& feeder
61349e08aacStbbdev         ) const
61449e08aacStbbdev 
61549e08aacStbbdev         OR
61649e08aacStbbdev 
61749e08aacStbbdev         B::operator()( cv_item_type& item ) const
61849e08aacStbbdev       \endcode                                               Process item.
61949e08aacStbbdev                                                              May be invoked concurrently  for the same \c this but different \c item.
62049e08aacStbbdev 
62149e08aacStbbdev     - \code item_type( const item_type& ) \endcode
62249e08aacStbbdev                                                              Copy a work item.
62349e08aacStbbdev     - \code ~item_type() \endcode                            Destroy a work item
62449e08aacStbbdev **/
62549e08aacStbbdev 
62649e08aacStbbdev /** \name parallel_for_each
62749e08aacStbbdev     See also requirements on \ref parallel_for_each_body_req "parallel_for_each Body". **/
62849e08aacStbbdev //@{
62949e08aacStbbdev //! Parallel iteration over a range, with optional addition of more work.
63049e08aacStbbdev /** @ingroup algorithms */
63149e08aacStbbdev template<typename Iterator, typename Body>
632*478de5b1Stbbdev     __TBB_requires(std::input_iterator<Iterator> && parallel_for_each_iterator_body<Body, Iterator>)
63349e08aacStbbdev void parallel_for_each(Iterator first, Iterator last, const Body& body) {
63449e08aacStbbdev     task_group_context context(PARALLEL_FOR_EACH);
63549e08aacStbbdev     run_parallel_for_each<Iterator, Body>(first, last, body, context);
63649e08aacStbbdev }
63749e08aacStbbdev 
63849e08aacStbbdev template<typename Range, typename Body>
639*478de5b1Stbbdev     __TBB_requires(container_based_sequence<Range, std::input_iterator_tag> && parallel_for_each_range_body<Body, Range>)
64049e08aacStbbdev void parallel_for_each(Range& rng, const Body& body) {
64149e08aacStbbdev     parallel_for_each(std::begin(rng), std::end(rng), body);
64249e08aacStbbdev }
64349e08aacStbbdev 
64449e08aacStbbdev template<typename Range, typename Body>
645*478de5b1Stbbdev     __TBB_requires(container_based_sequence<Range, std::input_iterator_tag> && parallel_for_each_range_body<Body, Range>)
64649e08aacStbbdev void parallel_for_each(const Range& rng, const Body& body) {
64749e08aacStbbdev     parallel_for_each(std::begin(rng), std::end(rng), body);
64849e08aacStbbdev }
64949e08aacStbbdev 
65049e08aacStbbdev //! Parallel iteration over a range, with optional addition of more work and user-supplied context
65149e08aacStbbdev /** @ingroup algorithms */
65249e08aacStbbdev template<typename Iterator, typename Body>
653*478de5b1Stbbdev     __TBB_requires(std::input_iterator<Iterator> && parallel_for_each_iterator_body<Body, Iterator>)
65449e08aacStbbdev void parallel_for_each(Iterator first, Iterator last, const Body& body, task_group_context& context) {
65549e08aacStbbdev     run_parallel_for_each<Iterator, Body>(first, last, body, context);
65649e08aacStbbdev }
65749e08aacStbbdev 
65849e08aacStbbdev template<typename Range, typename Body>
659*478de5b1Stbbdev     __TBB_requires(container_based_sequence<Range, std::input_iterator_tag> && parallel_for_each_range_body<Body, Range>)
66049e08aacStbbdev void parallel_for_each(Range& rng, const Body& body, task_group_context& context) {
66149e08aacStbbdev     parallel_for_each(std::begin(rng), std::end(rng), body, context);
66249e08aacStbbdev }
66349e08aacStbbdev 
66449e08aacStbbdev template<typename Range, typename Body>
665*478de5b1Stbbdev     __TBB_requires(container_based_sequence<Range, std::input_iterator_tag> && parallel_for_each_range_body<Body, Range>)
66649e08aacStbbdev void parallel_for_each(const Range& rng, const Body& body, task_group_context& context) {
66749e08aacStbbdev     parallel_for_each(std::begin(rng), std::end(rng), body, context);
66849e08aacStbbdev }
66949e08aacStbbdev 
670b15aabb3Stbbdev } // namespace d2
67149e08aacStbbdev } // namespace detail
67249e08aacStbbdev //! @endcond
67349e08aacStbbdev //@}
67449e08aacStbbdev 
67549e08aacStbbdev inline namespace v1 {
676b15aabb3Stbbdev using detail::d2::parallel_for_each;
67749e08aacStbbdev using detail::d1::feeder;
67849e08aacStbbdev } // namespace v1
67949e08aacStbbdev 
68049e08aacStbbdev } // namespace tbb
68149e08aacStbbdev 
68249e08aacStbbdev #endif /* __TBB_parallel_for_each_H */
683