File indexing completed on 2025-12-18 10:24:26
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017 #ifndef __TBB_parallel_for_each_H
0018 #define __TBB_parallel_for_each_H
0019
0020 #include "detail/_config.h"
0021 #include "detail/_namespace_injection.h"
0022 #include "detail/_exception.h"
0023 #include "detail/_task.h"
0024 #include "detail/_aligned_space.h"
0025 #include "detail/_small_object_pool.h"
0026 #include "detail/_utils.h"
0027
0028 #include "parallel_for.h"
0029 #include "task_group.h" // task_group_context
0030
0031 #include <iterator>
0032 #include <type_traits>
0033
0034 namespace tbb {
0035 namespace detail {
0036 #if __TBB_CPP20_CONCEPTS_PRESENT
0037 namespace d1 {
0038 template <typename Item>
0039 class feeder;
0040
0041 }
0042 inline namespace d0 {
0043
0044 template <typename Body, typename ItemType, typename FeederItemType>
0045 concept parallel_for_each_body = std::invocable<const std::remove_reference_t<Body>&, ItemType&&> ||
0046 std::invocable<const std::remove_reference_t<Body>&, ItemType&&, tbb::detail::d1::feeder<FeederItemType>&>;
0047
0048 }
0049 #endif
0050 namespace d2 {
0051 template<typename Body, typename Item> class feeder_impl;
0052 }
0053
0054 namespace d1 {
0055
0056 template<typename Item>
0057 class feeder {
0058 feeder() {}
0059 feeder(const feeder&) = delete;
0060 void operator=( const feeder&) = delete;
0061
0062 virtual ~feeder () {}
0063 virtual void internal_add_copy(const Item& item) = 0;
0064 virtual void internal_add_move(Item&& item) = 0;
0065
0066 template<typename Body_, typename Item_> friend class d2::feeder_impl;
0067 public:
0068
0069 void add(const Item& item) {internal_add_copy(item);}
0070 void add(Item&& item) {internal_add_move(std::move(item));}
0071 };
0072
0073 }
0074
0075 namespace d2 {
0076 using namespace tbb::detail::d1;
0077
0078
0079 template<class Body>
0080 struct parallel_for_each_operator_selector {
0081 public:
0082 template<typename ItemArg, typename FeederArg>
0083 static auto call(const Body& body, ItemArg&& item, FeederArg*)
0084 -> decltype(tbb::detail::invoke(body, std::forward<ItemArg>(item)), void()) {
0085 #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
0086
0087 #pragma warning (push)
0088 #pragma warning (disable: 4239)
0089 #endif
0090
0091 tbb::detail::invoke(body, std::forward<ItemArg>(item));
0092
0093 #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
0094 #pragma warning (pop)
0095 #endif
0096 }
0097
0098 template<typename ItemArg, typename FeederArg>
0099 static auto call(const Body& body, ItemArg&& item, FeederArg* feeder)
0100 -> decltype(tbb::detail::invoke(body, std::forward<ItemArg>(item), *feeder), void()) {
0101 #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
0102
0103 #pragma warning (push)
0104 #pragma warning (disable: 4239)
0105 #endif
0106 __TBB_ASSERT(feeder, "Feeder was not created but should be");
0107
0108 tbb::detail::invoke(body, std::forward<ItemArg>(item), *feeder);
0109
0110 #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
0111 #pragma warning (pop)
0112 #endif
0113 }
0114 };
0115
0116 template<typename Body, typename Item>
0117 struct feeder_item_task: public task {
0118 using feeder_type = feeder_impl<Body, Item>;
0119
0120 template <typename ItemType>
0121 feeder_item_task(ItemType&& input_item, feeder_type& feeder, small_object_allocator& alloc, wait_tree_vertex_interface& wait_vertex) :
0122 item(std::forward<ItemType>(input_item)),
0123 my_feeder(feeder),
0124 my_allocator(alloc),
0125 m_wait_tree_vertex(r1::get_thread_reference_vertex(&wait_vertex))
0126 {
0127 m_wait_tree_vertex->reserve();
0128 }
0129
0130 void finalize(const execution_data& ed) {
0131 m_wait_tree_vertex->release();
0132 my_allocator.delete_object(this, ed);
0133 }
0134
0135
0136
0137 using first_priority = int;
0138 using second_priority = double;
0139
0140 template <typename BodyType, typename ItemType, typename FeederType>
0141 static auto call(const BodyType& call_body, ItemType& call_item, FeederType& call_feeder, first_priority)
0142 -> decltype(parallel_for_each_operator_selector<Body>::call(call_body, std::move(call_item), &call_feeder), void())
0143 {
0144 parallel_for_each_operator_selector<Body>::call(call_body, std::move(call_item), &call_feeder);
0145 }
0146
0147 template <typename BodyType, typename ItemType, typename FeederType>
0148 static void call(const BodyType& call_body, ItemType& call_item, FeederType& call_feeder, second_priority) {
0149 parallel_for_each_operator_selector<Body>::call(call_body, call_item, &call_feeder);
0150 }
0151
0152 task* execute(execution_data& ed) override {
0153 call(my_feeder.my_body, item, my_feeder, first_priority{});
0154 finalize(ed);
0155 return nullptr;
0156 }
0157
0158 task* cancel(execution_data& ed) override {
0159 finalize(ed);
0160 return nullptr;
0161 }
0162
0163 Item item;
0164 feeder_type& my_feeder;
0165 small_object_allocator my_allocator;
0166 wait_tree_vertex_interface* m_wait_tree_vertex;
0167 };
0168
0169
0170
0171 template<typename Body, typename Item>
0172 class feeder_impl : public feeder<Item> {
0173
0174 void internal_add_copy_impl(std::true_type, const Item& item) {
0175 using feeder_task = feeder_item_task<Body, Item>;
0176 small_object_allocator alloc;
0177 auto task = alloc.new_object<feeder_task>(item, *this, alloc, my_wait_context);
0178
0179 spawn(*task, my_execution_context);
0180 }
0181
0182 void internal_add_copy_impl(std::false_type, const Item&) {
0183 __TBB_ASSERT(false, "Overloading for r-value reference doesn't work or it's not movable and not copyable object");
0184 }
0185
0186 void internal_add_copy(const Item& item) override {
0187 internal_add_copy_impl(typename std::is_copy_constructible<Item>::type(), item);
0188 }
0189
0190 void internal_add_move(Item&& item) override {
0191 using feeder_task = feeder_item_task<Body, Item>;
0192 small_object_allocator alloc{};
0193 auto task = alloc.new_object<feeder_task>(std::move(item), *this, alloc, my_wait_context);
0194
0195 spawn(*task, my_execution_context);
0196 }
0197 public:
0198 feeder_impl(const Body& body, wait_context_vertex& w_context, task_group_context &context)
0199 : my_body(body),
0200 my_wait_context(w_context)
0201 , my_execution_context(context)
0202 {}
0203
0204 const Body& my_body;
0205 wait_context_vertex& my_wait_context;
0206 task_group_context& my_execution_context;
0207 };
0208
0209
0210
0211 template<typename Iterator, typename Body, typename Item>
0212 struct for_each_iteration_task: public task {
0213 using feeder_type = feeder_impl<Body, Item>;
0214
0215 for_each_iteration_task(Iterator input_item_ptr, const Body& body, feeder_impl<Body, Item>* feeder_ptr, wait_context& wait_context) :
0216 item_ptr(input_item_ptr), my_body(body), my_feeder_ptr(feeder_ptr), parent_wait_context(wait_context)
0217 {}
0218
0219 void finalize() {
0220 parent_wait_context.release();
0221 }
0222
0223 task* execute(execution_data&) override {
0224 parallel_for_each_operator_selector<Body>::call(my_body, *item_ptr, my_feeder_ptr);
0225 finalize();
0226 return nullptr;
0227 }
0228
0229 task* cancel(execution_data&) override {
0230 finalize();
0231 return nullptr;
0232 }
0233
0234 Iterator item_ptr;
0235 const Body& my_body;
0236 feeder_impl<Body, Item>* my_feeder_ptr;
0237 wait_context& parent_wait_context;
0238 };
0239
0240
0241
0242 template <typename Body, typename Item, typename = void>
0243 struct input_iteration_task_iterator_helper {
0244
0245
0246 using type = const Item*;
0247 };
0248
0249 template <typename Body, typename Item>
0250 struct input_iteration_task_iterator_helper<Body, Item,
0251 tbb::detail::void_t<decltype(parallel_for_each_operator_selector<Body>::call(std::declval<const Body&>(),
0252 std::declval<Item&&>(),
0253 std::declval<feeder_impl<Body, Item>*>()))>>
0254 {
0255 using type = std::move_iterator<Item*>;
0256 };
0257
0258
0259
0260 template <typename Body, typename Item>
0261 struct input_block_handling_task : public task {
0262 static constexpr size_t max_block_size = 4;
0263
0264 using feeder_type = feeder_impl<Body, Item>;
0265 using iteration_task_iterator_type = typename input_iteration_task_iterator_helper<Body, Item>::type;
0266 using iteration_task = for_each_iteration_task<iteration_task_iterator_type, Body, Item>;
0267
0268 input_block_handling_task(wait_context_vertex& root_wait_context, task_group_context& e_context,
0269 const Body& body, feeder_impl<Body, Item>* feeder_ptr, small_object_allocator& alloc)
0270 :my_size(0), my_wait_context(0), my_root_wait_context(root_wait_context),
0271 my_execution_context(e_context), my_allocator(alloc)
0272 {
0273 auto item_it = block_iteration_space.begin();
0274 for (auto* it = task_pool.begin(); it != task_pool.end(); ++it) {
0275 new (it) iteration_task(iteration_task_iterator_type(item_it++), body, feeder_ptr, my_wait_context);
0276 }
0277 }
0278
0279 void finalize(const execution_data& ed) {
0280 my_root_wait_context.release();
0281 my_allocator.delete_object(this, ed);
0282 }
0283
0284 task* execute(execution_data& ed) override {
0285 __TBB_ASSERT( my_size > 0, "Negative size was passed to task");
0286 for (std::size_t counter = 1; counter < my_size; ++counter) {
0287 my_wait_context.reserve();
0288 spawn(*(task_pool.begin() + counter), my_execution_context);
0289 }
0290 my_wait_context.reserve();
0291 execute_and_wait(*task_pool.begin(), my_execution_context,
0292 my_wait_context, my_execution_context);
0293
0294
0295 finalize(ed);
0296 return nullptr;
0297 }
0298
0299 task* cancel(execution_data& ed) override {
0300 finalize(ed);
0301 return nullptr;
0302 }
0303
0304 ~input_block_handling_task() {
0305 for(std::size_t counter = 0; counter < max_block_size; ++counter) {
0306 (task_pool.begin() + counter)->~iteration_task();
0307 if (counter < my_size) {
0308 (block_iteration_space.begin() + counter)->~Item();
0309 }
0310 }
0311 }
0312
0313 aligned_space<Item, max_block_size> block_iteration_space;
0314 aligned_space<iteration_task, max_block_size> task_pool;
0315 std::size_t my_size;
0316 wait_context my_wait_context;
0317 wait_context_vertex& my_root_wait_context;
0318 task_group_context& my_execution_context;
0319 small_object_allocator my_allocator;
0320 };
0321
0322
0323
0324 template <typename Iterator, typename Body, typename Item>
0325 struct forward_block_handling_task : public task {
0326 static constexpr size_t max_block_size = 4;
0327
0328 using iteration_task = for_each_iteration_task<Iterator, Body, Item>;
0329
0330 forward_block_handling_task(Iterator first, std::size_t size,
0331 wait_context_vertex& w_context, task_group_context& e_context,
0332 const Body& body, feeder_impl<Body, Item>* feeder_ptr,
0333 small_object_allocator& alloc)
0334 : my_size(size), my_wait_context(0), my_root_wait_context(w_context),
0335 my_execution_context(e_context), my_allocator(alloc)
0336 {
0337 auto* task_it = task_pool.begin();
0338 for (std::size_t i = 0; i < size; i++) {
0339 new (task_it++) iteration_task(first, body, feeder_ptr, my_wait_context);
0340 ++first;
0341 }
0342 }
0343
0344 void finalize(const execution_data& ed) {
0345 my_root_wait_context.release();
0346 my_allocator.delete_object(this, ed);
0347 }
0348
0349 task* execute(execution_data& ed) override {
0350 __TBB_ASSERT( my_size > 0, "Negative size was passed to task");
0351 for(std::size_t counter = 1; counter < my_size; ++counter) {
0352 my_wait_context.reserve();
0353 spawn(*(task_pool.begin() + counter), my_execution_context);
0354 }
0355 my_wait_context.reserve();
0356 execute_and_wait(*task_pool.begin(), my_execution_context,
0357 my_wait_context, my_execution_context);
0358
0359
0360 finalize(ed);
0361 return nullptr;
0362 }
0363
0364 task* cancel(execution_data& ed) override {
0365 finalize(ed);
0366 return nullptr;
0367 }
0368
0369 ~forward_block_handling_task() {
0370 for(std::size_t counter = 0; counter < my_size; ++counter) {
0371 (task_pool.begin() + counter)->~iteration_task();
0372 }
0373 }
0374
0375 aligned_space<iteration_task, max_block_size> task_pool;
0376 std::size_t my_size;
0377 wait_context my_wait_context;
0378 wait_context_vertex& my_root_wait_context;
0379 task_group_context& my_execution_context;
0380 small_object_allocator my_allocator;
0381 };
0382
0383
0384
0385
0386 template <typename Iterator, typename Body, typename Item>
0387 class parallel_for_body_wrapper {
0388 Iterator my_first;
0389 const Body& my_body;
0390 feeder_impl<Body, Item>* my_feeder_ptr;
0391 public:
0392 parallel_for_body_wrapper(Iterator first, const Body& body, feeder_impl<Body, Item>* feeder_ptr)
0393 : my_first(first), my_body(body), my_feeder_ptr(feeder_ptr) {}
0394
0395 void operator()(tbb::blocked_range<std::size_t> range) const {
0396 #if __INTEL_COMPILER
0397 #pragma ivdep
0398 #endif
0399 for (std::size_t count = range.begin(); count != range.end(); count++) {
0400 parallel_for_each_operator_selector<Body>::call(my_body, *(my_first + count),
0401 my_feeder_ptr);
0402 }
0403 }
0404 };
0405
0406
0407
0408
0409 template<typename It>
0410 using tag = typename std::iterator_traits<It>::iterator_category;
0411
0412 #if __TBB_CPP20_PRESENT
0413 template <typename It>
0414 struct move_iterator_dispatch_helper {
0415 using type = It;
0416 };
0417
0418
0419
0420
0421
0422 template <typename It>
0423 struct move_iterator_dispatch_helper<std::move_iterator<It>> {
0424 using type = It;
0425 };
0426
0427 template <typename It>
0428 using iterator_tag_dispatch_impl =
0429 std::conditional_t<std::random_access_iterator<It>,
0430 std::random_access_iterator_tag,
0431 std::conditional_t<std::forward_iterator<It>,
0432 std::forward_iterator_tag,
0433 std::input_iterator_tag>>;
0434
0435 template <typename It>
0436 using iterator_tag_dispatch =
0437 iterator_tag_dispatch_impl<typename move_iterator_dispatch_helper<It>::type>;
0438
0439 #else
0440 template<typename It>
0441 using iterator_tag_dispatch = typename
0442 std::conditional<
0443 std::is_base_of<std::random_access_iterator_tag, tag<It>>::value,
0444 std::random_access_iterator_tag,
0445 typename std::conditional<
0446 std::is_base_of<std::forward_iterator_tag, tag<It>>::value,
0447 std::forward_iterator_tag,
0448 std::input_iterator_tag
0449 >::type
0450 >::type;
0451 #endif
0452
0453 template <typename Body, typename Iterator, typename Item>
0454 using feeder_is_required = tbb::detail::void_t<decltype(tbb::detail::invoke(std::declval<const Body>(),
0455 std::declval<typename std::iterator_traits<Iterator>::reference>(),
0456 std::declval<feeder<Item>&>()))>;
0457
0458
0459 template <typename Iterator, typename Body, typename Item, typename = void>
0460 struct feeder_holder {
0461 feeder_holder( wait_context_vertex&, task_group_context&, const Body& ) {}
0462
0463 feeder_impl<Body, Item>* feeder_ptr() { return nullptr; }
0464 };
0465
0466 template <typename Iterator, typename Body, typename Item>
0467 class feeder_holder<Iterator, Body, Item, feeder_is_required<Body, Iterator, Item>> {
0468 public:
0469 feeder_holder( wait_context_vertex& w_context, task_group_context& context, const Body& body )
0470 : my_feeder(body, w_context, context) {}
0471
0472 feeder_impl<Body, Item>* feeder_ptr() { return &my_feeder; }
0473 private:
0474 feeder_impl<Body, Item> my_feeder;
0475 };
0476
0477 template <typename Iterator, typename Body, typename Item>
0478 class for_each_root_task_base : public task {
0479 public:
0480 for_each_root_task_base(Iterator first, Iterator last, const Body& body, wait_context_vertex& w_context, task_group_context& e_context)
0481 : my_first(first), my_last(last), my_wait_context(w_context), my_execution_context(e_context),
0482 my_body(body), my_feeder_holder(my_wait_context, my_execution_context, my_body)
0483 {
0484 my_wait_context.reserve();
0485 }
0486 private:
0487 task* cancel(execution_data&) override {
0488 this->my_wait_context.release();
0489 return nullptr;
0490 }
0491 protected:
0492 Iterator my_first;
0493 Iterator my_last;
0494 wait_context_vertex& my_wait_context;
0495 task_group_context& my_execution_context;
0496 const Body& my_body;
0497 feeder_holder<Iterator, Body, Item> my_feeder_holder;
0498 };
0499
0500
0501
0502
0503 template <typename Iterator, typename Body, typename Item, typename IteratorTag = iterator_tag_dispatch<Iterator>>
0504 class for_each_root_task : public for_each_root_task_base<Iterator, Body, Item>
0505 {
0506 using base_type = for_each_root_task_base<Iterator, Body, Item>;
0507 public:
0508 using base_type::base_type;
0509 private:
0510 task* execute(execution_data& ed) override {
0511 using block_handling_type = input_block_handling_task<Body, Item>;
0512
0513 if (this->my_first == this->my_last) {
0514 this->my_wait_context.release();
0515 return nullptr;
0516 }
0517
0518 this->my_wait_context.reserve();
0519 small_object_allocator alloc{};
0520 auto block_handling_task = alloc.new_object<block_handling_type>(ed, this->my_wait_context, this->my_execution_context,
0521 this->my_body, this->my_feeder_holder.feeder_ptr(),
0522 alloc);
0523
0524 auto* block_iterator = block_handling_task->block_iteration_space.begin();
0525 for (; !(this->my_first == this->my_last) && block_handling_task->my_size < block_handling_type::max_block_size; ++this->my_first) {
0526
0527 new (block_iterator++) Item(*this->my_first);
0528 ++block_handling_task->my_size;
0529 }
0530
0531
0532 spawn(*this, this->my_execution_context);
0533 return block_handling_task;
0534 }
0535 };
0536
0537
0538
0539
0540 template <typename Iterator, typename Body, typename Item>
0541 class for_each_root_task<Iterator, Body, Item, std::forward_iterator_tag>
0542 : public for_each_root_task_base<Iterator, Body, Item>
0543 {
0544 using base_type = for_each_root_task_base<Iterator, Body, Item>;
0545 public:
0546 using base_type::base_type;
0547 private:
0548 task* execute(execution_data& ed) override {
0549 using block_handling_type = forward_block_handling_task<Iterator, Body, Item>;
0550 if (this->my_first == this->my_last) {
0551 this->my_wait_context.release();
0552 return nullptr;
0553 }
0554
0555 std::size_t block_size{0};
0556 Iterator first_block_element = this->my_first;
0557 for (; !(this->my_first == this->my_last) && block_size < block_handling_type::max_block_size; ++this->my_first) {
0558 ++block_size;
0559 }
0560
0561 this->my_wait_context.reserve();
0562 small_object_allocator alloc{};
0563 auto block_handling_task = alloc.new_object<block_handling_type>(ed, first_block_element, block_size,
0564 this->my_wait_context, this->my_execution_context,
0565 this->my_body, this->my_feeder_holder.feeder_ptr(), alloc);
0566
0567
0568 spawn(*this, this->my_execution_context);
0569 return block_handling_task;
0570 }
0571 };
0572
0573
0574
0575
0576 template <typename Iterator, typename Body, typename Item>
0577 class for_each_root_task<Iterator, Body, Item, std::random_access_iterator_tag>
0578 : public for_each_root_task_base<Iterator, Body, Item>
0579 {
0580 using base_type = for_each_root_task_base<Iterator, Body, Item>;
0581 public:
0582 using base_type::base_type;
0583 private:
0584 task* execute(execution_data&) override {
0585 tbb::parallel_for(
0586 tbb::blocked_range<std::size_t>(0, std::distance(this->my_first, this->my_last)),
0587 parallel_for_body_wrapper<Iterator, Body, Item>(this->my_first, this->my_body, this->my_feeder_holder.feeder_ptr())
0588 , this->my_execution_context
0589 );
0590
0591 this->my_wait_context.release();
0592 return nullptr;
0593 }
0594 };
0595
0596
0597
0598
0599 template<typename Body, typename Item, typename FeederArg>
0600 auto feeder_argument_parser(void (Body::*)(Item, feeder<FeederArg>&) const) -> FeederArg;
0601
0602 template<typename Body, typename>
0603 decltype(feeder_argument_parser<Body>(&Body::operator())) get_item_type_impl(int);
0604 template<typename Body, typename Item> Item get_item_type_impl(...);
0605
0606 template <typename Body, typename Item>
0607 using get_item_type = decltype(get_item_type_impl<Body, Item>(0));
0608
0609 #if __TBB_CPP20_CONCEPTS_PRESENT
0610 template <typename Body, typename ItemType>
0611 using feeder_item_type = std::remove_cvref_t<get_item_type<Body, ItemType>>;
0612
0613 template <typename Body, typename Iterator>
0614 concept parallel_for_each_iterator_body =
0615 parallel_for_each_body<Body, iterator_reference_type<Iterator>, feeder_item_type<Body, iterator_reference_type<Iterator>>>;
0616
0617 template <typename Body, typename Range>
0618 concept parallel_for_each_range_body =
0619 parallel_for_each_body<Body, range_reference_type<Range>, feeder_item_type<Body, range_reference_type<Range>>>;
0620 #endif
0621
0622
0623
0624 template<typename Iterator, typename Body>
0625 void run_parallel_for_each( Iterator first, Iterator last, const Body& body, task_group_context& context)
0626 {
0627 if (!(first == last)) {
0628 using ItemType = get_item_type<Body, typename std::iterator_traits<Iterator>::value_type>;
0629 wait_context_vertex w_context(0);
0630
0631 for_each_root_task<Iterator, Body, ItemType> root_task(first, last, body, w_context, context);
0632
0633 execute_and_wait(root_task, context, w_context.get_context(), context);
0634 }
0635 }
0636
0637
0638
0639
0640
0641
0642
0643
0644
0645
0646
0647
0648
0649
0650
0651
0652
0653
0654
0655
0656
0657
0658
0659
0660
0661 template<typename Iterator, typename Body>
0662 __TBB_requires(std::input_iterator<Iterator> && parallel_for_each_iterator_body<Body, Iterator>)
0663 void parallel_for_each(Iterator first, Iterator last, const Body& body) {
0664 task_group_context context(PARALLEL_FOR_EACH);
0665 run_parallel_for_each<Iterator, Body>(first, last, body, context);
0666 }
0667
0668 template<typename Range, typename Body>
0669 __TBB_requires(container_based_sequence<Range, std::input_iterator_tag> && parallel_for_each_range_body<Body, Range>)
0670 void parallel_for_each(Range& rng, const Body& body) {
0671 parallel_for_each(std::begin(rng), std::end(rng), body);
0672 }
0673
0674 template<typename Range, typename Body>
0675 __TBB_requires(container_based_sequence<Range, std::input_iterator_tag> && parallel_for_each_range_body<Body, Range>)
0676 void parallel_for_each(const Range& rng, const Body& body) {
0677 parallel_for_each(std::begin(rng), std::end(rng), body);
0678 }
0679
0680
0681
0682 template<typename Iterator, typename Body>
0683 __TBB_requires(std::input_iterator<Iterator> && parallel_for_each_iterator_body<Body, Iterator>)
0684 void parallel_for_each(Iterator first, Iterator last, const Body& body, task_group_context& context) {
0685 run_parallel_for_each<Iterator, Body>(first, last, body, context);
0686 }
0687
0688 template<typename Range, typename Body>
0689 __TBB_requires(container_based_sequence<Range, std::input_iterator_tag> && parallel_for_each_range_body<Body, Range>)
0690 void parallel_for_each(Range& rng, const Body& body, task_group_context& context) {
0691 parallel_for_each(std::begin(rng), std::end(rng), body, context);
0692 }
0693
0694 template<typename Range, typename Body>
0695 __TBB_requires(container_based_sequence<Range, std::input_iterator_tag> && parallel_for_each_range_body<Body, Range>)
0696 void parallel_for_each(const Range& rng, const Body& body, task_group_context& context) {
0697 parallel_for_each(std::begin(rng), std::end(rng), body, context);
0698 }
0699
0700 }
0701 }
0702
0703
0704
0705 inline namespace v1 {
0706 using detail::d2::parallel_for_each;
0707 using detail::d1::feeder;
0708 }
0709
0710 }
0711
0712 #endif