149e08aacStbbdev /*
2e6e493f9SChristian Fersch     Copyright (c) 2005-2023 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"
26a088cfa0SKonstantin Boyarinov #include "detail/_utils.h"
2749e08aacStbbdev 
2849e08aacStbbdev #include "parallel_for.h"
2949e08aacStbbdev #include "task_group.h" // task_group_context
3049e08aacStbbdev 
3149e08aacStbbdev #include <iterator>
3249e08aacStbbdev #include <type_traits>
3349e08aacStbbdev 
3449e08aacStbbdev namespace tbb {
3549e08aacStbbdev namespace detail {
36478de5b1Stbbdev #if __TBB_CPP20_CONCEPTS_PRESENT
37478de5b1Stbbdev namespace d1 {
38478de5b1Stbbdev template <typename Item>
39478de5b1Stbbdev class feeder;
40478de5b1Stbbdev 
41478de5b1Stbbdev } // namespace d1
42478de5b1Stbbdev inline namespace d0 {
43478de5b1Stbbdev 
44478de5b1Stbbdev template <typename Body, typename ItemType, typename FeederItemType>
45a088cfa0SKonstantin Boyarinov concept parallel_for_each_body = std::invocable<const std::remove_reference_t<Body>&, ItemType&&> ||
46a088cfa0SKonstantin Boyarinov                                  std::invocable<const std::remove_reference_t<Body>&, ItemType&&, tbb::detail::d1::feeder<FeederItemType>&>;
47478de5b1Stbbdev 
48478de5b1Stbbdev } // namespace d0
49478de5b1Stbbdev #endif // __TBB_CPP20_CONCEPTS_PRESENT
50b15aabb3Stbbdev namespace d2 {
5149e08aacStbbdev template<typename Body, typename Item> class feeder_impl;
52b15aabb3Stbbdev } // namespace d2
5349e08aacStbbdev 
54b15aabb3Stbbdev namespace d1 {
5549e08aacStbbdev //! Class the user supplied algorithm body uses to add new tasks
5649e08aacStbbdev template<typename Item>
5749e08aacStbbdev class feeder {
feeder()5849e08aacStbbdev     feeder() {}
5949e08aacStbbdev     feeder(const feeder&) = delete;
6049e08aacStbbdev     void operator=( const feeder&) = delete;
6149e08aacStbbdev 
~feeder()6249e08aacStbbdev     virtual ~feeder () {}
6349e08aacStbbdev     virtual void internal_add_copy(const Item& item) = 0;
6449e08aacStbbdev     virtual void internal_add_move(Item&& item) = 0;
6549e08aacStbbdev 
66478de5b1Stbbdev     template<typename Body_, typename Item_> friend class d2::feeder_impl;
6749e08aacStbbdev public:
6849e08aacStbbdev     //! Add a work item to a running parallel_for_each.
add(const Item & item)6949e08aacStbbdev     void add(const Item& item) {internal_add_copy(item);}
add(Item && item)7049e08aacStbbdev     void add(Item&& item) {internal_add_move(std::move(item));}
7149e08aacStbbdev };
7249e08aacStbbdev 
73b15aabb3Stbbdev } // namespace d1
74b15aabb3Stbbdev 
75b15aabb3Stbbdev namespace d2 {
76b15aabb3Stbbdev using namespace tbb::detail::d1;
7749e08aacStbbdev /** Selects one of the two possible forms of function call member operator.
7849e08aacStbbdev     @ingroup algorithms **/
79b15aabb3Stbbdev template<class Body>
80b15aabb3Stbbdev struct parallel_for_each_operator_selector {
8149e08aacStbbdev public:
8249e08aacStbbdev     template<typename ItemArg, typename FeederArg>
83b15aabb3Stbbdev     static auto call(const Body& body, ItemArg&& item, FeederArg*)
84a088cfa0SKonstantin Boyarinov     -> decltype(tbb::detail::invoke(body, std::forward<ItemArg>(item)), void()) {
8549e08aacStbbdev         #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
8649e08aacStbbdev         // Suppression of Microsoft non-standard extension warnings
8749e08aacStbbdev         #pragma warning (push)
8849e08aacStbbdev         #pragma warning (disable: 4239)
8949e08aacStbbdev         #endif
9049e08aacStbbdev 
91a088cfa0SKonstantin Boyarinov         tbb::detail::invoke(body, std::forward<ItemArg>(item));
9249e08aacStbbdev 
9349e08aacStbbdev         #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
94e6e493f9SChristian Fersch         #pragma warning (pop)
9549e08aacStbbdev         #endif
9649e08aacStbbdev     }
9749e08aacStbbdev 
9849e08aacStbbdev     template<typename ItemArg, typename FeederArg>
99b15aabb3Stbbdev     static auto call(const Body& body, ItemArg&& item, FeederArg* feeder)
100a088cfa0SKonstantin Boyarinov     -> decltype(tbb::detail::invoke(body, std::forward<ItemArg>(item), *feeder), void()) {
10149e08aacStbbdev         #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
10249e08aacStbbdev         // Suppression of Microsoft non-standard extension warnings
10349e08aacStbbdev         #pragma warning (push)
10449e08aacStbbdev         #pragma warning (disable: 4239)
10549e08aacStbbdev         #endif
106b15aabb3Stbbdev         __TBB_ASSERT(feeder, "Feeder was not created but should be");
10749e08aacStbbdev 
108a088cfa0SKonstantin Boyarinov         tbb::detail::invoke(body, std::forward<ItemArg>(item), *feeder);
10949e08aacStbbdev 
11049e08aacStbbdev         #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
111e6e493f9SChristian Fersch         #pragma warning (pop)
11249e08aacStbbdev         #endif
11349e08aacStbbdev     }
11449e08aacStbbdev };
11549e08aacStbbdev 
11649e08aacStbbdev template<typename Body, typename Item>
11749e08aacStbbdev struct feeder_item_task: public task {
11849e08aacStbbdev     using feeder_type = feeder_impl<Body, Item>;
11949e08aacStbbdev 
12049e08aacStbbdev     template <typename ItemType>
feeder_item_taskfeeder_item_task12149e08aacStbbdev     feeder_item_task(ItemType&& input_item, feeder_type& feeder, small_object_allocator& alloc) :
12249e08aacStbbdev         item(std::forward<ItemType>(input_item)),
12349e08aacStbbdev         my_feeder(feeder),
12449e08aacStbbdev         my_allocator(alloc)
12549e08aacStbbdev     {}
12649e08aacStbbdev 
finalizefeeder_item_task12749e08aacStbbdev     void finalize(const execution_data& ed) {
12849e08aacStbbdev         my_feeder.my_wait_context.release();
12949e08aacStbbdev         my_allocator.delete_object(this, ed);
13049e08aacStbbdev     }
13149e08aacStbbdev 
132b15aabb3Stbbdev     //! Hack for resolve ambiguity between calls to the body with and without moving the stored copy
133b15aabb3Stbbdev     //! Executing body with moving the copy should have higher priority
134b15aabb3Stbbdev     using first_priority = int;
135b15aabb3Stbbdev     using second_priority = double;
136b15aabb3Stbbdev 
137b15aabb3Stbbdev     template <typename BodyType, typename ItemType, typename FeederType>
138b15aabb3Stbbdev     static auto call(const BodyType& call_body, ItemType& call_item, FeederType& call_feeder, first_priority)
139b15aabb3Stbbdev     -> decltype(parallel_for_each_operator_selector<Body>::call(call_body, std::move(call_item), &call_feeder), void())
140b15aabb3Stbbdev     {
141b15aabb3Stbbdev         parallel_for_each_operator_selector<Body>::call(call_body, std::move(call_item), &call_feeder);
142b15aabb3Stbbdev     }
143b15aabb3Stbbdev 
144b15aabb3Stbbdev     template <typename BodyType, typename ItemType, typename FeederType>
callfeeder_item_task145b15aabb3Stbbdev     static void call(const BodyType& call_body, ItemType& call_item, FeederType& call_feeder, second_priority) {
146b15aabb3Stbbdev         parallel_for_each_operator_selector<Body>::call(call_body, call_item, &call_feeder);
147b15aabb3Stbbdev     }
148b15aabb3Stbbdev 
executefeeder_item_task14949e08aacStbbdev     task* execute(execution_data& ed) override {
150b15aabb3Stbbdev         call(my_feeder.my_body, item, my_feeder, first_priority{});
15149e08aacStbbdev         finalize(ed);
15249e08aacStbbdev         return nullptr;
15349e08aacStbbdev     }
15449e08aacStbbdev 
cancelfeeder_item_task15549e08aacStbbdev     task* cancel(execution_data& ed) override {
15649e08aacStbbdev         finalize(ed);
15749e08aacStbbdev         return nullptr;
15849e08aacStbbdev     }
15949e08aacStbbdev 
16049e08aacStbbdev     Item item;
16149e08aacStbbdev     feeder_type& my_feeder;
16249e08aacStbbdev     small_object_allocator my_allocator;
16349e08aacStbbdev }; // class feeder_item_task
16449e08aacStbbdev 
16549e08aacStbbdev /** Implements new task adding procedure.
16649e08aacStbbdev     @ingroup algorithms **/
167b15aabb3Stbbdev template<typename Body, typename Item>
16849e08aacStbbdev class feeder_impl : public feeder<Item> {
16949e08aacStbbdev     // 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)17049e08aacStbbdev     void internal_add_copy_impl(std::true_type, const Item& item) {
17149e08aacStbbdev         using feeder_task = feeder_item_task<Body, Item>;
17249e08aacStbbdev         small_object_allocator alloc;
17349e08aacStbbdev         auto task = alloc.new_object<feeder_task>(item, *this, alloc);
17449e08aacStbbdev 
17549e08aacStbbdev         my_wait_context.reserve();
17649e08aacStbbdev         spawn(*task, my_execution_context);
17749e08aacStbbdev     }
17849e08aacStbbdev 
internal_add_copy_impl(std::false_type,const Item &)17949e08aacStbbdev     void internal_add_copy_impl(std::false_type, const Item&) {
18049e08aacStbbdev         __TBB_ASSERT(false, "Overloading for r-value reference doesn't work or it's not movable and not copyable object");
18149e08aacStbbdev     }
18249e08aacStbbdev 
internal_add_copy(const Item & item)18349e08aacStbbdev     void internal_add_copy(const Item& item) override {
18449e08aacStbbdev         internal_add_copy_impl(typename std::is_copy_constructible<Item>::type(), item);
18549e08aacStbbdev     }
18649e08aacStbbdev 
internal_add_move(Item && item)18749e08aacStbbdev     void internal_add_move(Item&& item) override {
18849e08aacStbbdev         using feeder_task = feeder_item_task<Body, Item>;
18949e08aacStbbdev         small_object_allocator alloc{};
19049e08aacStbbdev         auto task = alloc.new_object<feeder_task>(std::move(item), *this, alloc);
19149e08aacStbbdev 
19249e08aacStbbdev         my_wait_context.reserve();
19349e08aacStbbdev         spawn(*task, my_execution_context);
19449e08aacStbbdev     }
19549e08aacStbbdev public:
feeder_impl(const Body & body,wait_context & w_context,task_group_context & context)196b15aabb3Stbbdev     feeder_impl(const Body& body, wait_context& w_context, task_group_context &context)
197b15aabb3Stbbdev       : my_body(body),
198b15aabb3Stbbdev         my_wait_context(w_context)
19949e08aacStbbdev       , my_execution_context(context)
20049e08aacStbbdev     {}
20149e08aacStbbdev 
20249e08aacStbbdev     const Body& my_body;
203b15aabb3Stbbdev     wait_context& my_wait_context;
20449e08aacStbbdev     task_group_context& my_execution_context;
20549e08aacStbbdev }; // class feeder_impl
20649e08aacStbbdev 
20749e08aacStbbdev /** Execute computation under one element of the range
20849e08aacStbbdev     @ingroup algorithms **/
20949e08aacStbbdev template<typename Iterator, typename Body, typename Item>
21049e08aacStbbdev struct for_each_iteration_task: public task {
21149e08aacStbbdev     using feeder_type = feeder_impl<Body, Item>;
21249e08aacStbbdev 
for_each_iteration_taskfor_each_iteration_task213b15aabb3Stbbdev     for_each_iteration_task(Iterator input_item_ptr, const Body& body, feeder_impl<Body, Item>* feeder_ptr, wait_context& wait_context) :
214b15aabb3Stbbdev         item_ptr(input_item_ptr), my_body(body), my_feeder_ptr(feeder_ptr), parent_wait_context(wait_context)
21549e08aacStbbdev     {}
21649e08aacStbbdev 
finalizefor_each_iteration_task21749e08aacStbbdev     void finalize() {
21849e08aacStbbdev         parent_wait_context.release();
21949e08aacStbbdev     }
22049e08aacStbbdev 
executefor_each_iteration_task22149e08aacStbbdev     task* execute(execution_data&) override {
222b15aabb3Stbbdev         parallel_for_each_operator_selector<Body>::call(my_body, *item_ptr, my_feeder_ptr);
22349e08aacStbbdev         finalize();
22449e08aacStbbdev         return nullptr;
22549e08aacStbbdev     }
22649e08aacStbbdev 
cancelfor_each_iteration_task22749e08aacStbbdev     task* cancel(execution_data&) override {
22849e08aacStbbdev         finalize();
22949e08aacStbbdev         return nullptr;
23049e08aacStbbdev     }
23149e08aacStbbdev 
23249e08aacStbbdev     Iterator item_ptr;
233b15aabb3Stbbdev     const Body& my_body;
234b15aabb3Stbbdev     feeder_impl<Body, Item>* my_feeder_ptr;
23549e08aacStbbdev     wait_context& parent_wait_context;
236b15aabb3Stbbdev }; // class for_each_iteration_task
237b15aabb3Stbbdev 
238b15aabb3Stbbdev // Helper to get the type of the iterator to the internal sequence of copies
239b15aabb3Stbbdev // If the element can be passed to the body as an rvalue - this iterator should be move_iterator
240b15aabb3Stbbdev template <typename Body, typename Item, typename = void>
241b15aabb3Stbbdev struct input_iteration_task_iterator_helper {
242b15aabb3Stbbdev     // For input iterators we pass const lvalue reference to the body
243b15aabb3Stbbdev     // It is prohibited to take non-constant lvalue references for input iterators
244b15aabb3Stbbdev     using type = const Item*;
245b15aabb3Stbbdev };
246b15aabb3Stbbdev 
247b15aabb3Stbbdev template <typename Body, typename Item>
248b15aabb3Stbbdev struct input_iteration_task_iterator_helper<Body, Item,
249b15aabb3Stbbdev     tbb::detail::void_t<decltype(parallel_for_each_operator_selector<Body>::call(std::declval<const Body&>(),
250b15aabb3Stbbdev                                                                                  std::declval<Item&&>(),
251b15aabb3Stbbdev                                                                                  std::declval<feeder_impl<Body, Item>*>()))>>
252b15aabb3Stbbdev {
253b15aabb3Stbbdev     using type = std::move_iterator<Item*>;
254b15aabb3Stbbdev };
25549e08aacStbbdev 
25649e08aacStbbdev /** Split one block task to several(max_block_size) iteration tasks for input iterators
25749e08aacStbbdev     @ingroup algorithms **/
25849e08aacStbbdev template <typename Body, typename Item>
25949e08aacStbbdev struct input_block_handling_task : public task {
26049e08aacStbbdev     static constexpr size_t max_block_size = 4;
26149e08aacStbbdev 
26249e08aacStbbdev     using feeder_type = feeder_impl<Body, Item>;
263b15aabb3Stbbdev     using iteration_task_iterator_type = typename input_iteration_task_iterator_helper<Body, Item>::type;
264b15aabb3Stbbdev     using iteration_task = for_each_iteration_task<iteration_task_iterator_type, Body, Item>;
26549e08aacStbbdev 
266b15aabb3Stbbdev     input_block_handling_task(wait_context& root_wait_context, task_group_context& e_context,
267b15aabb3Stbbdev                               const Body& body, feeder_impl<Body, Item>* feeder_ptr, small_object_allocator& alloc)
268b15aabb3Stbbdev         :my_size(0), my_wait_context(0), my_root_wait_context(root_wait_context),
269b15aabb3Stbbdev          my_execution_context(e_context), my_allocator(alloc)
27049e08aacStbbdev     {
271b15aabb3Stbbdev         auto item_it = block_iteration_space.begin();
27249e08aacStbbdev         for (auto* it = task_pool.begin(); it != task_pool.end(); ++it) {
273b15aabb3Stbbdev             new (it) iteration_task(iteration_task_iterator_type(item_it++), body, feeder_ptr, my_wait_context);
27449e08aacStbbdev         }
27549e08aacStbbdev     }
27649e08aacStbbdev 
27749e08aacStbbdev     void finalize(const execution_data& ed) {
278b15aabb3Stbbdev         my_root_wait_context.release();
27949e08aacStbbdev         my_allocator.delete_object(this, ed);
28049e08aacStbbdev     }
28149e08aacStbbdev 
28249e08aacStbbdev     task* execute(execution_data& ed) override {
28349e08aacStbbdev         __TBB_ASSERT( my_size > 0, "Negative size was passed to task");
28449e08aacStbbdev         for (std::size_t counter = 1; counter < my_size; ++counter) {
28549e08aacStbbdev             my_wait_context.reserve();
286b15aabb3Stbbdev             spawn(*(task_pool.begin() + counter), my_execution_context);
28749e08aacStbbdev         }
28849e08aacStbbdev         my_wait_context.reserve();
289b15aabb3Stbbdev         execute_and_wait(*task_pool.begin(), my_execution_context,
290b15aabb3Stbbdev                          my_wait_context,    my_execution_context);
29149e08aacStbbdev 
29249e08aacStbbdev         // deallocate current task after children execution
29349e08aacStbbdev         finalize(ed);
29449e08aacStbbdev         return nullptr;
29549e08aacStbbdev     }
29649e08aacStbbdev 
29749e08aacStbbdev     task* cancel(execution_data& ed) override {
29849e08aacStbbdev         finalize(ed);
29949e08aacStbbdev         return nullptr;
30049e08aacStbbdev     }
30149e08aacStbbdev 
30249e08aacStbbdev     ~input_block_handling_task() {
30349e08aacStbbdev         for(std::size_t counter = 0; counter < max_block_size; ++counter) {
30449e08aacStbbdev             (task_pool.begin() + counter)->~iteration_task();
305aa247fa8SIlya Isaev             if (counter < my_size) {
30649e08aacStbbdev                 (block_iteration_space.begin() + counter)->~Item();
30749e08aacStbbdev             }
30849e08aacStbbdev         }
309aa247fa8SIlya Isaev     }
31049e08aacStbbdev 
31149e08aacStbbdev     aligned_space<Item, max_block_size> block_iteration_space;
31249e08aacStbbdev     aligned_space<iteration_task, max_block_size> task_pool;
31349e08aacStbbdev     std::size_t my_size;
31449e08aacStbbdev     wait_context my_wait_context;
315b15aabb3Stbbdev     wait_context& my_root_wait_context;
316b15aabb3Stbbdev     task_group_context& my_execution_context;
31749e08aacStbbdev     small_object_allocator my_allocator;
318b15aabb3Stbbdev }; // class input_block_handling_task
31949e08aacStbbdev 
32049e08aacStbbdev /** Split one block task to several(max_block_size) iteration tasks for forward iterators
32149e08aacStbbdev     @ingroup algorithms **/
32249e08aacStbbdev template <typename Iterator, typename Body, typename Item>
32349e08aacStbbdev struct forward_block_handling_task : public task {
32449e08aacStbbdev     static constexpr size_t max_block_size = 4;
32549e08aacStbbdev 
32649e08aacStbbdev     using iteration_task = for_each_iteration_task<Iterator, Body, Item>;
32749e08aacStbbdev 
328b15aabb3Stbbdev     forward_block_handling_task(Iterator first, std::size_t size,
329b15aabb3Stbbdev                                 wait_context& w_context, task_group_context& e_context,
330b15aabb3Stbbdev                                 const Body& body, feeder_impl<Body, Item>* feeder_ptr,
331b15aabb3Stbbdev                                 small_object_allocator& alloc)
332b15aabb3Stbbdev         : my_size(size), my_wait_context(0), my_root_wait_context(w_context),
333b15aabb3Stbbdev           my_execution_context(e_context), my_allocator(alloc)
33449e08aacStbbdev     {
33549e08aacStbbdev         auto* task_it = task_pool.begin();
33649e08aacStbbdev         for (std::size_t i = 0; i < size; i++) {
337b15aabb3Stbbdev             new (task_it++) iteration_task(first, body, feeder_ptr, my_wait_context);
33849e08aacStbbdev             ++first;
33949e08aacStbbdev         }
34049e08aacStbbdev     }
34149e08aacStbbdev 
34249e08aacStbbdev     void finalize(const execution_data& ed) {
343b15aabb3Stbbdev         my_root_wait_context.release();
34449e08aacStbbdev         my_allocator.delete_object(this, ed);
34549e08aacStbbdev     }
34649e08aacStbbdev 
34749e08aacStbbdev     task* execute(execution_data& ed) override {
34849e08aacStbbdev         __TBB_ASSERT( my_size > 0, "Negative size was passed to task");
34949e08aacStbbdev         for(std::size_t counter = 1; counter < my_size; ++counter) {
35049e08aacStbbdev             my_wait_context.reserve();
351b15aabb3Stbbdev             spawn(*(task_pool.begin() + counter), my_execution_context);
35249e08aacStbbdev         }
35349e08aacStbbdev         my_wait_context.reserve();
354b15aabb3Stbbdev         execute_and_wait(*task_pool.begin(), my_execution_context,
355b15aabb3Stbbdev                          my_wait_context,    my_execution_context);
35649e08aacStbbdev 
35749e08aacStbbdev         // deallocate current task after children execution
35849e08aacStbbdev         finalize(ed);
35949e08aacStbbdev         return nullptr;
36049e08aacStbbdev     }
36149e08aacStbbdev 
36249e08aacStbbdev     task* cancel(execution_data& ed) override {
36349e08aacStbbdev         finalize(ed);
36449e08aacStbbdev         return nullptr;
36549e08aacStbbdev     }
36649e08aacStbbdev 
36749e08aacStbbdev     ~forward_block_handling_task() {
36849e08aacStbbdev         for(std::size_t counter = 0; counter < my_size; ++counter) {
36949e08aacStbbdev             (task_pool.begin() + counter)->~iteration_task();
37049e08aacStbbdev         }
37149e08aacStbbdev     }
37249e08aacStbbdev 
37349e08aacStbbdev     aligned_space<iteration_task, max_block_size> task_pool;
37449e08aacStbbdev     std::size_t my_size;
37549e08aacStbbdev     wait_context my_wait_context;
376b15aabb3Stbbdev     wait_context& my_root_wait_context;
377b15aabb3Stbbdev     task_group_context& my_execution_context;
37849e08aacStbbdev     small_object_allocator my_allocator;
37949e08aacStbbdev }; // class forward_block_handling_task
38049e08aacStbbdev 
38149e08aacStbbdev /** Body for parallel_for algorithm.
38249e08aacStbbdev   * Allows to redirect operations under random access iterators range to the parallel_for algorithm.
38349e08aacStbbdev     @ingroup algorithms **/
38449e08aacStbbdev template <typename Iterator, typename Body, typename Item>
38549e08aacStbbdev class parallel_for_body_wrapper {
38649e08aacStbbdev     Iterator my_first;
387b15aabb3Stbbdev     const Body& my_body;
388b15aabb3Stbbdev     feeder_impl<Body, Item>* my_feeder_ptr;
38949e08aacStbbdev public:
390b15aabb3Stbbdev     parallel_for_body_wrapper(Iterator first, const Body& body, feeder_impl<Body, Item>* feeder_ptr)
391b15aabb3Stbbdev         : my_first(first), my_body(body), my_feeder_ptr(feeder_ptr) {}
39249e08aacStbbdev 
39349e08aacStbbdev     void operator()(tbb::blocked_range<std::size_t> range) const {
39449e08aacStbbdev #if __INTEL_COMPILER
39549e08aacStbbdev #pragma ivdep
39649e08aacStbbdev #endif
39749e08aacStbbdev         for (std::size_t count = range.begin(); count != range.end(); count++) {
398b15aabb3Stbbdev             parallel_for_each_operator_selector<Body>::call(my_body, *(my_first + count),
399b15aabb3Stbbdev                                                             my_feeder_ptr);
40049e08aacStbbdev         }
40149e08aacStbbdev     }
40249e08aacStbbdev }; // class parallel_for_body_wrapper
40349e08aacStbbdev 
40449e08aacStbbdev 
40549e08aacStbbdev /** Helper for getting iterators tag including inherited custom tags
40649e08aacStbbdev     @ingroup algorithms */
40749e08aacStbbdev template<typename It>
40849e08aacStbbdev using tag = typename std::iterator_traits<It>::iterator_category;
40949e08aacStbbdev 
410de0a3a65SKonstantin Boyarinov #if __TBB_CPP20_PRESENT
411de0a3a65SKonstantin Boyarinov template <typename It>
412*3b9f9baeSKonstantin Boyarinov struct move_iterator_dispatch_helper {
413*3b9f9baeSKonstantin Boyarinov     using type = It;
414*3b9f9baeSKonstantin Boyarinov };
415*3b9f9baeSKonstantin Boyarinov 
416*3b9f9baeSKonstantin Boyarinov // Until C++23, std::move_iterator::iterator_concept always defines
417*3b9f9baeSKonstantin Boyarinov // to std::input_iterator_tag and hence std::forward_iterator concept
418*3b9f9baeSKonstantin Boyarinov // always evaluates to false, so std::move_iterator dispatch should be
419*3b9f9baeSKonstantin Boyarinov // made according to the base iterator type.
420*3b9f9baeSKonstantin Boyarinov template <typename It>
421*3b9f9baeSKonstantin Boyarinov struct move_iterator_dispatch_helper<std::move_iterator<It>> {
422*3b9f9baeSKonstantin Boyarinov     using type = It;
423*3b9f9baeSKonstantin Boyarinov };
424*3b9f9baeSKonstantin Boyarinov 
425*3b9f9baeSKonstantin Boyarinov template <typename It>
426*3b9f9baeSKonstantin Boyarinov using iterator_tag_dispatch_impl =
427de0a3a65SKonstantin Boyarinov     std::conditional_t<std::random_access_iterator<It>,
428de0a3a65SKonstantin Boyarinov                        std::random_access_iterator_tag,
429de0a3a65SKonstantin Boyarinov                        std::conditional_t<std::forward_iterator<It>,
430de0a3a65SKonstantin Boyarinov                                           std::forward_iterator_tag,
431de0a3a65SKonstantin Boyarinov                                           std::input_iterator_tag>>;
432*3b9f9baeSKonstantin Boyarinov 
433*3b9f9baeSKonstantin Boyarinov template <typename It>
434*3b9f9baeSKonstantin Boyarinov using iterator_tag_dispatch =
435*3b9f9baeSKonstantin Boyarinov     iterator_tag_dispatch_impl<typename move_iterator_dispatch_helper<It>::type>;
436*3b9f9baeSKonstantin Boyarinov 
437de0a3a65SKonstantin Boyarinov #else
43849e08aacStbbdev template<typename It>
43949e08aacStbbdev using iterator_tag_dispatch = typename
44049e08aacStbbdev     std::conditional<
44149e08aacStbbdev         std::is_base_of<std::random_access_iterator_tag, tag<It>>::value,
44249e08aacStbbdev         std::random_access_iterator_tag,
44349e08aacStbbdev         typename std::conditional<
44449e08aacStbbdev             std::is_base_of<std::forward_iterator_tag, tag<It>>::value,
44549e08aacStbbdev             std::forward_iterator_tag,
44649e08aacStbbdev             std::input_iterator_tag
44749e08aacStbbdev         >::type
44849e08aacStbbdev     >::type;
449de0a3a65SKonstantin Boyarinov #endif // __TBB_CPP20_PRESENT
45049e08aacStbbdev 
451b15aabb3Stbbdev template <typename Body, typename Iterator, typename Item>
452a088cfa0SKonstantin Boyarinov using feeder_is_required = tbb::detail::void_t<decltype(tbb::detail::invoke(std::declval<const Body>(),
453a088cfa0SKonstantin Boyarinov                                                                             std::declval<typename std::iterator_traits<Iterator>::reference>(),
454b15aabb3Stbbdev                                                                             std::declval<feeder<Item>&>()))>;
455b15aabb3Stbbdev 
456b15aabb3Stbbdev // Creates feeder object only if the body can accept it
457b15aabb3Stbbdev template <typename Iterator, typename Body, typename Item, typename = void>
458b15aabb3Stbbdev struct feeder_holder {
459b15aabb3Stbbdev     feeder_holder( wait_context&, task_group_context&, const Body& ) {}
460b15aabb3Stbbdev 
461b15aabb3Stbbdev     feeder_impl<Body, Item>* feeder_ptr() { return nullptr; }
462b15aabb3Stbbdev }; // class feeder_holder
463b15aabb3Stbbdev 
464b15aabb3Stbbdev template <typename Iterator, typename Body, typename Item>
465b15aabb3Stbbdev class feeder_holder<Iterator, Body, Item, feeder_is_required<Body, Iterator, Item>> {
466b15aabb3Stbbdev public:
467b15aabb3Stbbdev     feeder_holder( wait_context& w_context, task_group_context& context, const Body& body )
468b15aabb3Stbbdev         : my_feeder(body, w_context, context) {}
469b15aabb3Stbbdev 
470b15aabb3Stbbdev     feeder_impl<Body, Item>* feeder_ptr() { return &my_feeder; }
471b15aabb3Stbbdev private:
472b15aabb3Stbbdev     feeder_impl<Body, Item> my_feeder;
473b15aabb3Stbbdev }; // class feeder_holder
474b15aabb3Stbbdev 
475b15aabb3Stbbdev template <typename Iterator, typename Body, typename Item>
476b15aabb3Stbbdev class for_each_root_task_base : public task {
477b15aabb3Stbbdev public:
478b15aabb3Stbbdev     for_each_root_task_base(Iterator first, Iterator last, const Body& body, wait_context& w_context, task_group_context& e_context)
479b15aabb3Stbbdev         : my_first(first), my_last(last), my_wait_context(w_context), my_execution_context(e_context),
480b15aabb3Stbbdev           my_body(body), my_feeder_holder(my_wait_context, my_execution_context, my_body)
481b15aabb3Stbbdev     {
482b15aabb3Stbbdev         my_wait_context.reserve();
483b15aabb3Stbbdev     }
484b15aabb3Stbbdev private:
485b15aabb3Stbbdev     task* cancel(execution_data&) override {
486b15aabb3Stbbdev         this->my_wait_context.release();
487b15aabb3Stbbdev         return nullptr;
488b15aabb3Stbbdev     }
489b15aabb3Stbbdev protected:
490b15aabb3Stbbdev     Iterator my_first;
491b15aabb3Stbbdev     Iterator my_last;
492b15aabb3Stbbdev     wait_context& my_wait_context;
493b15aabb3Stbbdev     task_group_context& my_execution_context;
494b15aabb3Stbbdev     const Body& my_body;
495b15aabb3Stbbdev     feeder_holder<Iterator, Body, Item> my_feeder_holder;
496b15aabb3Stbbdev }; // class for_each_root_task_base
497b15aabb3Stbbdev 
49849e08aacStbbdev /** parallel_for_each algorithm root task - most generic version
49949e08aacStbbdev   * Splits input range to blocks
50049e08aacStbbdev     @ingroup algorithms **/
50149e08aacStbbdev template <typename Iterator, typename Body, typename Item, typename IteratorTag = iterator_tag_dispatch<Iterator>>
502b15aabb3Stbbdev class for_each_root_task : public for_each_root_task_base<Iterator, Body, Item>
50349e08aacStbbdev {
504b15aabb3Stbbdev     using base_type = for_each_root_task_base<Iterator, Body, Item>;
505b15aabb3Stbbdev public:
506b15aabb3Stbbdev     using base_type::base_type;
50749e08aacStbbdev private:
50849e08aacStbbdev     task* execute(execution_data& ed) override {
50949e08aacStbbdev         using block_handling_type = input_block_handling_task<Body, Item>;
51049e08aacStbbdev 
511b15aabb3Stbbdev         if (this->my_first == this->my_last) {
512b15aabb3Stbbdev             this->my_wait_context.release();
51349e08aacStbbdev             return nullptr;
51449e08aacStbbdev         }
51549e08aacStbbdev 
516b15aabb3Stbbdev         this->my_wait_context.reserve();
51749e08aacStbbdev         small_object_allocator alloc{};
518b15aabb3Stbbdev         auto block_handling_task = alloc.new_object<block_handling_type>(ed, this->my_wait_context, this->my_execution_context,
519b15aabb3Stbbdev                                                                          this->my_body, this->my_feeder_holder.feeder_ptr(),
520b15aabb3Stbbdev                                                                          alloc);
52149e08aacStbbdev 
52249e08aacStbbdev         auto* block_iterator = block_handling_task->block_iteration_space.begin();
523b15aabb3Stbbdev         for (; !(this->my_first == this->my_last) && block_handling_task->my_size < block_handling_type::max_block_size; ++this->my_first) {
52449e08aacStbbdev             // Move semantics are automatically used when supported by the iterator
525b15aabb3Stbbdev             new (block_iterator++) Item(*this->my_first);
52649e08aacStbbdev             ++block_handling_task->my_size;
52749e08aacStbbdev         }
52849e08aacStbbdev 
52949e08aacStbbdev         // Do not access this after spawn to avoid races
530b15aabb3Stbbdev         spawn(*this, this->my_execution_context);
53149e08aacStbbdev         return block_handling_task;
53249e08aacStbbdev     }
53349e08aacStbbdev }; // class for_each_root_task - most generic implementation
53449e08aacStbbdev 
53549e08aacStbbdev /** parallel_for_each algorithm root task - forward iterator based specialization
53649e08aacStbbdev   * Splits input range to blocks
53749e08aacStbbdev     @ingroup algorithms **/
53849e08aacStbbdev template <typename Iterator, typename Body, typename Item>
539b15aabb3Stbbdev class for_each_root_task<Iterator, Body, Item, std::forward_iterator_tag>
540b15aabb3Stbbdev     : public for_each_root_task_base<Iterator, Body, Item>
54149e08aacStbbdev {
542b15aabb3Stbbdev     using base_type = for_each_root_task_base<Iterator, Body, Item>;
543b15aabb3Stbbdev public:
544b15aabb3Stbbdev     using base_type::base_type;
54549e08aacStbbdev private:
54649e08aacStbbdev     task* execute(execution_data& ed) override {
54749e08aacStbbdev         using block_handling_type = forward_block_handling_task<Iterator, Body, Item>;
548b15aabb3Stbbdev         if (this->my_first == this->my_last) {
549b15aabb3Stbbdev             this->my_wait_context.release();
55049e08aacStbbdev             return nullptr;
55149e08aacStbbdev         }
55249e08aacStbbdev 
55349e08aacStbbdev         std::size_t block_size{0};
554b15aabb3Stbbdev         Iterator first_block_element = this->my_first;
555b15aabb3Stbbdev         for (; !(this->my_first == this->my_last) && block_size < block_handling_type::max_block_size; ++this->my_first) {
55649e08aacStbbdev             ++block_size;
55749e08aacStbbdev         }
55849e08aacStbbdev 
559b15aabb3Stbbdev         this->my_wait_context.reserve();
56049e08aacStbbdev         small_object_allocator alloc{};
561b15aabb3Stbbdev         auto block_handling_task = alloc.new_object<block_handling_type>(ed, first_block_element, block_size,
562b15aabb3Stbbdev                                                                          this->my_wait_context, this->my_execution_context,
563b15aabb3Stbbdev                                                                          this->my_body, this->my_feeder_holder.feeder_ptr(), alloc);
56449e08aacStbbdev 
56549e08aacStbbdev         // Do not access this after spawn to avoid races
566b15aabb3Stbbdev         spawn(*this, this->my_execution_context);
56749e08aacStbbdev         return block_handling_task;
56849e08aacStbbdev     }
56949e08aacStbbdev }; // class for_each_root_task - forward iterator based specialization
57049e08aacStbbdev 
57149e08aacStbbdev /** parallel_for_each algorithm root task - random access iterator based specialization
57249e08aacStbbdev   * Splits input range to blocks
57349e08aacStbbdev     @ingroup algorithms **/
57449e08aacStbbdev template <typename Iterator, typename Body, typename Item>
575b15aabb3Stbbdev class for_each_root_task<Iterator, Body, Item, std::random_access_iterator_tag>
576b15aabb3Stbbdev     : public for_each_root_task_base<Iterator, Body, Item>
57749e08aacStbbdev {
578b15aabb3Stbbdev     using base_type = for_each_root_task_base<Iterator, Body, Item>;
579b15aabb3Stbbdev public:
580b15aabb3Stbbdev     using base_type::base_type;
58149e08aacStbbdev private:
58249e08aacStbbdev     task* execute(execution_data&) override {
58349e08aacStbbdev         tbb::parallel_for(
584b15aabb3Stbbdev             tbb::blocked_range<std::size_t>(0, std::distance(this->my_first, this->my_last)),
585b15aabb3Stbbdev             parallel_for_body_wrapper<Iterator, Body, Item>(this->my_first, this->my_body, this->my_feeder_holder.feeder_ptr())
586b15aabb3Stbbdev             , this->my_execution_context
58749e08aacStbbdev         );
58849e08aacStbbdev 
589b15aabb3Stbbdev         this->my_wait_context.release();
59049e08aacStbbdev         return nullptr;
59149e08aacStbbdev     }
59249e08aacStbbdev }; // class for_each_root_task - random access iterator based specialization
59349e08aacStbbdev 
59449e08aacStbbdev /** Helper for getting item type. If item type can be deduced from feeder - got it from feeder,
59549e08aacStbbdev     if feeder is generic - got item type from range.
59649e08aacStbbdev     @ingroup algorithms */
59749e08aacStbbdev template<typename Body, typename Item, typename FeederArg>
59849e08aacStbbdev auto feeder_argument_parser(void (Body::*)(Item, feeder<FeederArg>&) const) -> FeederArg;
59949e08aacStbbdev 
60049e08aacStbbdev template<typename Body, typename>
60149e08aacStbbdev decltype(feeder_argument_parser<Body>(&Body::operator())) get_item_type_impl(int); // for (T, feeder<T>)
60249e08aacStbbdev template<typename Body, typename Item> Item get_item_type_impl(...); // stub
60349e08aacStbbdev 
60449e08aacStbbdev template <typename Body, typename Item>
60549e08aacStbbdev using get_item_type = decltype(get_item_type_impl<Body, Item>(0));
60649e08aacStbbdev 
607478de5b1Stbbdev #if __TBB_CPP20_CONCEPTS_PRESENT
608478de5b1Stbbdev template <typename Body, typename ItemType>
609478de5b1Stbbdev using feeder_item_type = std::remove_cvref_t<get_item_type<Body, ItemType>>;
610478de5b1Stbbdev 
611478de5b1Stbbdev template <typename Body, typename Iterator>
612478de5b1Stbbdev concept parallel_for_each_iterator_body =
613478de5b1Stbbdev     parallel_for_each_body<Body, iterator_reference_type<Iterator>, feeder_item_type<Body, iterator_reference_type<Iterator>>>;
614478de5b1Stbbdev 
615478de5b1Stbbdev template <typename Body, typename Range>
616478de5b1Stbbdev concept parallel_for_each_range_body =
617478de5b1Stbbdev     parallel_for_each_body<Body, range_reference_type<Range>, feeder_item_type<Body, range_reference_type<Range>>>;
618478de5b1Stbbdev #endif
619478de5b1Stbbdev 
62049e08aacStbbdev /** Implements parallel iteration over a range.
62149e08aacStbbdev     @ingroup algorithms */
62249e08aacStbbdev template<typename Iterator, typename Body>
62349e08aacStbbdev void run_parallel_for_each( Iterator first, Iterator last, const Body& body, task_group_context& context)
62449e08aacStbbdev {
62549e08aacStbbdev     if (!(first == last)) {
626b15aabb3Stbbdev         using ItemType = get_item_type<Body, typename std::iterator_traits<Iterator>::value_type>;
627b15aabb3Stbbdev         wait_context w_context(0);
62849e08aacStbbdev 
629b15aabb3Stbbdev         for_each_root_task<Iterator, Body, ItemType> root_task(first, last, body, w_context, context);
63049e08aacStbbdev 
631b15aabb3Stbbdev         execute_and_wait(root_task, context, w_context, context);
63249e08aacStbbdev     }
63349e08aacStbbdev }
63449e08aacStbbdev 
63549e08aacStbbdev /** \page parallel_for_each_body_req Requirements on parallel_for_each body
63649e08aacStbbdev     Class \c Body implementing the concept of parallel_for_each body must define:
63749e08aacStbbdev     - \code
63849e08aacStbbdev         B::operator()(
63949e08aacStbbdev                 cv_item_type item,
64049e08aacStbbdev                 feeder<item_type>& feeder
64149e08aacStbbdev         ) const
64249e08aacStbbdev 
64349e08aacStbbdev         OR
64449e08aacStbbdev 
64549e08aacStbbdev         B::operator()( cv_item_type& item ) const
64649e08aacStbbdev       \endcode                                               Process item.
64749e08aacStbbdev                                                              May be invoked concurrently  for the same \c this but different \c item.
64849e08aacStbbdev 
64949e08aacStbbdev     - \code item_type( const item_type& ) \endcode
65049e08aacStbbdev                                                              Copy a work item.
65149e08aacStbbdev     - \code ~item_type() \endcode                            Destroy a work item
65249e08aacStbbdev **/
65349e08aacStbbdev 
65449e08aacStbbdev /** \name parallel_for_each
65549e08aacStbbdev     See also requirements on \ref parallel_for_each_body_req "parallel_for_each Body". **/
65649e08aacStbbdev //@{
65749e08aacStbbdev //! Parallel iteration over a range, with optional addition of more work.
65849e08aacStbbdev /** @ingroup algorithms */
65949e08aacStbbdev template<typename Iterator, typename Body>
660478de5b1Stbbdev     __TBB_requires(std::input_iterator<Iterator> && parallel_for_each_iterator_body<Body, Iterator>)
66149e08aacStbbdev void parallel_for_each(Iterator first, Iterator last, const Body& body) {
66249e08aacStbbdev     task_group_context context(PARALLEL_FOR_EACH);
66349e08aacStbbdev     run_parallel_for_each<Iterator, Body>(first, last, body, context);
66449e08aacStbbdev }
66549e08aacStbbdev 
66649e08aacStbbdev template<typename Range, typename Body>
667478de5b1Stbbdev     __TBB_requires(container_based_sequence<Range, std::input_iterator_tag> && parallel_for_each_range_body<Body, Range>)
66849e08aacStbbdev void parallel_for_each(Range& rng, const Body& body) {
66949e08aacStbbdev     parallel_for_each(std::begin(rng), std::end(rng), body);
67049e08aacStbbdev }
67149e08aacStbbdev 
67249e08aacStbbdev template<typename Range, typename Body>
673478de5b1Stbbdev     __TBB_requires(container_based_sequence<Range, std::input_iterator_tag> && parallel_for_each_range_body<Body, Range>)
67449e08aacStbbdev void parallel_for_each(const Range& rng, const Body& body) {
67549e08aacStbbdev     parallel_for_each(std::begin(rng), std::end(rng), body);
67649e08aacStbbdev }
67749e08aacStbbdev 
67849e08aacStbbdev //! Parallel iteration over a range, with optional addition of more work and user-supplied context
67949e08aacStbbdev /** @ingroup algorithms */
68049e08aacStbbdev template<typename Iterator, typename Body>
681478de5b1Stbbdev     __TBB_requires(std::input_iterator<Iterator> && parallel_for_each_iterator_body<Body, Iterator>)
68249e08aacStbbdev void parallel_for_each(Iterator first, Iterator last, const Body& body, task_group_context& context) {
68349e08aacStbbdev     run_parallel_for_each<Iterator, Body>(first, last, body, context);
68449e08aacStbbdev }
68549e08aacStbbdev 
68649e08aacStbbdev template<typename Range, typename Body>
687478de5b1Stbbdev     __TBB_requires(container_based_sequence<Range, std::input_iterator_tag> && parallel_for_each_range_body<Body, Range>)
68849e08aacStbbdev void parallel_for_each(Range& rng, const Body& body, task_group_context& context) {
68949e08aacStbbdev     parallel_for_each(std::begin(rng), std::end(rng), body, context);
69049e08aacStbbdev }
69149e08aacStbbdev 
69249e08aacStbbdev template<typename Range, typename Body>
693478de5b1Stbbdev     __TBB_requires(container_based_sequence<Range, std::input_iterator_tag> && parallel_for_each_range_body<Body, Range>)
69449e08aacStbbdev void parallel_for_each(const Range& rng, const Body& body, task_group_context& context) {
69549e08aacStbbdev     parallel_for_each(std::begin(rng), std::end(rng), body, context);
69649e08aacStbbdev }
69749e08aacStbbdev 
698b15aabb3Stbbdev } // namespace d2
69949e08aacStbbdev } // namespace detail
70049e08aacStbbdev //! @endcond
70149e08aacStbbdev //@}
70249e08aacStbbdev 
70349e08aacStbbdev inline namespace v1 {
704b15aabb3Stbbdev using detail::d2::parallel_for_each;
70549e08aacStbbdev using detail::d1::feeder;
70649e08aacStbbdev } // namespace v1
70749e08aacStbbdev 
70849e08aacStbbdev } // namespace tbb
70949e08aacStbbdev 
71049e08aacStbbdev #endif /* __TBB_parallel_for_each_H */
711