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