151c0b2f7Stbbdev /*
2de0a3a65SKonstantin Boyarinov     Copyright (c) 2005-2023 Intel Corporation
351c0b2f7Stbbdev 
451c0b2f7Stbbdev     Licensed under the Apache License, Version 2.0 (the "License");
551c0b2f7Stbbdev     you may not use this file except in compliance with the License.
651c0b2f7Stbbdev     You may obtain a copy of the License at
751c0b2f7Stbbdev 
851c0b2f7Stbbdev         http://www.apache.org/licenses/LICENSE-2.0
951c0b2f7Stbbdev 
1051c0b2f7Stbbdev     Unless required by applicable law or agreed to in writing, software
1151c0b2f7Stbbdev     distributed under the License is distributed on an "AS IS" BASIS,
1251c0b2f7Stbbdev     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1351c0b2f7Stbbdev     See the License for the specific language governing permissions and
1451c0b2f7Stbbdev     limitations under the License.
1551c0b2f7Stbbdev */
1651c0b2f7Stbbdev 
1751c0b2f7Stbbdev #include "common/parallel_for_each_common.h"
18478de5b1Stbbdev #include "common/concepts_common.h"
19478de5b1Stbbdev #include <vector>
20478de5b1Stbbdev #include <iterator>
2151c0b2f7Stbbdev 
2251c0b2f7Stbbdev //! \file test_parallel_for_each.cpp
2351c0b2f7Stbbdev //! \brief Test for [algorithms.parallel_for_each]
2451c0b2f7Stbbdev 
25de0a3a65SKonstantin Boyarinov #if __TBB_CPP20_PRESENT
26de0a3a65SKonstantin Boyarinov // Fancy iterator type that models the C++20 iterator type
27de0a3a65SKonstantin Boyarinov // that defines the real iterator category using iterator_concept type
28de0a3a65SKonstantin Boyarinov // and iterator_category is always std::input_iterator_type
29de0a3a65SKonstantin Boyarinov // Similar iterators are used by C++20 ranges (e.g. std::ranges::iota_view::iterator)
30de0a3a65SKonstantin Boyarinov // parallel_for_each algorithm should detect such iterators with respect to iterator_concept value
31de0a3a65SKonstantin Boyarinov 
32de0a3a65SKonstantin Boyarinov template <typename T, typename Category>
33de0a3a65SKonstantin Boyarinov struct cpp20_iterator {
34de0a3a65SKonstantin Boyarinov     static_assert(std::derived_from<Category, std::forward_iterator_tag>,
35de0a3a65SKonstantin Boyarinov                   "cpp20_iterator should be of at least forward iterator category");
36de0a3a65SKonstantin Boyarinov 
37de0a3a65SKonstantin Boyarinov     using iterator_concept = Category;
38de0a3a65SKonstantin Boyarinov     using iterator_category = std::input_iterator_tag;
39de0a3a65SKonstantin Boyarinov     using value_type = T;
40de0a3a65SKonstantin Boyarinov     using difference_type = std::ptrdiff_t;
41de0a3a65SKonstantin Boyarinov 
42de0a3a65SKonstantin Boyarinov     cpp20_iterator() = default;
cpp20_iteratorcpp20_iterator43de0a3a65SKonstantin Boyarinov     explicit cpp20_iterator(T* ptr) : my_ptr(ptr) {}
44de0a3a65SKonstantin Boyarinov 
operator *cpp20_iterator45de0a3a65SKonstantin Boyarinov     T& operator*() const { return *my_ptr; }
46de0a3a65SKonstantin Boyarinov 
operator ++cpp20_iterator47de0a3a65SKonstantin Boyarinov     cpp20_iterator& operator++() {
48de0a3a65SKonstantin Boyarinov         ++my_ptr;
49de0a3a65SKonstantin Boyarinov         return *this;
50de0a3a65SKonstantin Boyarinov     }
51de0a3a65SKonstantin Boyarinov 
operator ++cpp20_iterator52de0a3a65SKonstantin Boyarinov     cpp20_iterator operator++(int) {
53de0a3a65SKonstantin Boyarinov         auto it = *this;
54de0a3a65SKonstantin Boyarinov         ++*this;
55de0a3a65SKonstantin Boyarinov         return it;
56de0a3a65SKonstantin Boyarinov     }
57de0a3a65SKonstantin Boyarinov 
operator --cpp20_iterator58de0a3a65SKonstantin Boyarinov     cpp20_iterator& operator--()
59de0a3a65SKonstantin Boyarinov         requires std::derived_from<Category, std::bidirectional_iterator_tag>
60de0a3a65SKonstantin Boyarinov     {
61de0a3a65SKonstantin Boyarinov         --my_ptr;
62de0a3a65SKonstantin Boyarinov         return *this;
63de0a3a65SKonstantin Boyarinov     }
64de0a3a65SKonstantin Boyarinov 
operator --cpp20_iterator65de0a3a65SKonstantin Boyarinov     cpp20_iterator operator--(int)
66de0a3a65SKonstantin Boyarinov         requires std::derived_from<Category, std::bidirectional_iterator_tag>
67de0a3a65SKonstantin Boyarinov     {
68de0a3a65SKonstantin Boyarinov         auto it = *this;
69de0a3a65SKonstantin Boyarinov         --*this;
70de0a3a65SKonstantin Boyarinov         return it;
71de0a3a65SKonstantin Boyarinov     }
72de0a3a65SKonstantin Boyarinov 
operator +=cpp20_iterator73de0a3a65SKonstantin Boyarinov     cpp20_iterator& operator+=(difference_type n)
74de0a3a65SKonstantin Boyarinov         requires std::derived_from<Category, std::random_access_iterator_tag>
75de0a3a65SKonstantin Boyarinov     {
76de0a3a65SKonstantin Boyarinov         my_ptr += n;
77de0a3a65SKonstantin Boyarinov         return *this;
78de0a3a65SKonstantin Boyarinov     }
79de0a3a65SKonstantin Boyarinov 
operator -=cpp20_iterator80de0a3a65SKonstantin Boyarinov     cpp20_iterator& operator-=(difference_type n)
81de0a3a65SKonstantin Boyarinov         requires std::derived_from<Category, std::random_access_iterator_tag>
82de0a3a65SKonstantin Boyarinov     {
83de0a3a65SKonstantin Boyarinov         my_ptr -= n;
84de0a3a65SKonstantin Boyarinov         return *this;
85de0a3a65SKonstantin Boyarinov     }
86de0a3a65SKonstantin Boyarinov 
operator []cpp20_iterator87de0a3a65SKonstantin Boyarinov     T& operator[](difference_type n) const
88de0a3a65SKonstantin Boyarinov         requires std::derived_from<Category, std::random_access_iterator_tag>
89de0a3a65SKonstantin Boyarinov     {
90de0a3a65SKonstantin Boyarinov         return my_ptr[n];
91de0a3a65SKonstantin Boyarinov     }
92de0a3a65SKonstantin Boyarinov 
93de0a3a65SKonstantin Boyarinov     friend bool operator==(const cpp20_iterator&, const cpp20_iterator&) = default;
94de0a3a65SKonstantin Boyarinov 
95de0a3a65SKonstantin Boyarinov     friend auto operator<=>(const cpp20_iterator&, const cpp20_iterator&)
96de0a3a65SKonstantin Boyarinov         requires std::derived_from<Category, std::random_access_iterator_tag> = default;
97de0a3a65SKonstantin Boyarinov 
operator +(cpp20_iterator i,difference_type n)98de0a3a65SKonstantin Boyarinov     friend cpp20_iterator operator+(cpp20_iterator i, difference_type n)
99de0a3a65SKonstantin Boyarinov         requires std::derived_from<Category, std::random_access_iterator_tag>
100de0a3a65SKonstantin Boyarinov     {
101de0a3a65SKonstantin Boyarinov         return cpp20_iterator(i.my_ptr + n);
102de0a3a65SKonstantin Boyarinov     }
103de0a3a65SKonstantin Boyarinov 
operator +(difference_type n,cpp20_iterator i)104de0a3a65SKonstantin Boyarinov     friend cpp20_iterator operator+(difference_type n, cpp20_iterator i)
105de0a3a65SKonstantin Boyarinov         requires std::derived_from<Category, std::random_access_iterator_tag>
106de0a3a65SKonstantin Boyarinov     {
107de0a3a65SKonstantin Boyarinov         return i + n;
108de0a3a65SKonstantin Boyarinov     }
109de0a3a65SKonstantin Boyarinov 
operator -(cpp20_iterator i,difference_type n)110de0a3a65SKonstantin Boyarinov     friend cpp20_iterator operator-(cpp20_iterator i, difference_type n)
111de0a3a65SKonstantin Boyarinov         requires std::derived_from<Category, std::random_access_iterator_tag>
112de0a3a65SKonstantin Boyarinov     {
113de0a3a65SKonstantin Boyarinov         return cpp20_iterator(i.my_ptr - n);
114de0a3a65SKonstantin Boyarinov     }
115de0a3a65SKonstantin Boyarinov 
operator -(const cpp20_iterator & x,const cpp20_iterator & y)116de0a3a65SKonstantin Boyarinov     friend difference_type operator-(const cpp20_iterator& x, const cpp20_iterator& y) {
117de0a3a65SKonstantin Boyarinov         return x.my_ptr - y.my_ptr;
118de0a3a65SKonstantin Boyarinov     }
119de0a3a65SKonstantin Boyarinov private:
120de0a3a65SKonstantin Boyarinov     T* my_ptr = nullptr;
121de0a3a65SKonstantin Boyarinov }; // class cpp20_iterator
122de0a3a65SKonstantin Boyarinov #endif // __TBB_CPP20_PRESENT
123de0a3a65SKonstantin Boyarinov 
12451c0b2f7Stbbdev //! Test forward access iterator support
12551c0b2f7Stbbdev //! \brief \ref error_guessing \ref interface
12651c0b2f7Stbbdev TEST_CASE("Forward iterator support") {
12751c0b2f7Stbbdev     for ( auto concurrency_level : utils::concurrency_range() ) {
12851c0b2f7Stbbdev         tbb::global_control control(tbb::global_control::max_allowed_parallelism, concurrency_level);
12951c0b2f7Stbbdev         for(size_t depth = 0; depth <= depths_nubmer; ++depth) {
13051c0b2f7Stbbdev             g_tasks_expected = 0;
13151c0b2f7Stbbdev             for (size_t i=0; i < depth; ++i)
13251c0b2f7Stbbdev                 g_tasks_expected += FindNumOfTasks(g_depths[i].value());
13351c0b2f7Stbbdev             TestIterator_Modifiable<utils::ForwardIterator<value_t>>(depth);
13451c0b2f7Stbbdev         }
13551c0b2f7Stbbdev     }
13651c0b2f7Stbbdev }
13751c0b2f7Stbbdev 
13851c0b2f7Stbbdev //! Test random access iterator support
13951c0b2f7Stbbdev //! \brief \ref error_guessing \ref interface
14051c0b2f7Stbbdev TEST_CASE("Random access iterator support") {
14151c0b2f7Stbbdev     for ( auto concurrency_level : utils::concurrency_range() ) {
14251c0b2f7Stbbdev         tbb::global_control control(tbb::global_control::max_allowed_parallelism, concurrency_level);
14351c0b2f7Stbbdev         for(size_t depth = 0; depth <= depths_nubmer; ++depth) {
14451c0b2f7Stbbdev             g_tasks_expected = 0;
14551c0b2f7Stbbdev             for (size_t i=0; i < depth; ++i)
14651c0b2f7Stbbdev                 g_tasks_expected += FindNumOfTasks(g_depths[i].value());
14751c0b2f7Stbbdev             TestIterator_Modifiable<value_t*>(depth);
14851c0b2f7Stbbdev         }
14951c0b2f7Stbbdev     }
15051c0b2f7Stbbdev }
15151c0b2f7Stbbdev 
15251c0b2f7Stbbdev //! Test const random access iterator support
15351c0b2f7Stbbdev //! \brief \ref error_guessing \ref interface
15451c0b2f7Stbbdev TEST_CASE("Const random access iterator support") {
15551c0b2f7Stbbdev     for ( auto concurrency_level : utils::concurrency_range() ) {
15651c0b2f7Stbbdev         tbb::global_control control(tbb::global_control::max_allowed_parallelism, concurrency_level);
15751c0b2f7Stbbdev         for(size_t depth = 0; depth <= depths_nubmer; ++depth) {
15851c0b2f7Stbbdev             g_tasks_expected = 0;
15951c0b2f7Stbbdev             for (size_t i=0; i < depth; ++i)
16051c0b2f7Stbbdev                 g_tasks_expected += FindNumOfTasks(g_depths[i].value());
16151c0b2f7Stbbdev             TestIterator_Const<utils::ConstRandomIterator<value_t>>(depth);
16251c0b2f7Stbbdev         }
16351c0b2f7Stbbdev     }
16451c0b2f7Stbbdev 
16551c0b2f7Stbbdev }
16651c0b2f7Stbbdev 
16751c0b2f7Stbbdev //! Test container based overload
16851c0b2f7Stbbdev //! \brief \ref error_guessing \ref interface
16951c0b2f7Stbbdev TEST_CASE("Container based overload - forward iterator based container") {
17051c0b2f7Stbbdev     container_based_overload_test_case<utils::ForwardIterator>(/*expected_value*/1);
17151c0b2f7Stbbdev }
17251c0b2f7Stbbdev 
17351c0b2f7Stbbdev //! Test container based overload
17451c0b2f7Stbbdev //! \brief \ref error_guessing \ref interface
17551c0b2f7Stbbdev TEST_CASE("Container based overload - random access iterator based container") {
17651c0b2f7Stbbdev     container_based_overload_test_case<utils::RandomIterator>(/*expected_value*/1);
17751c0b2f7Stbbdev }
17851c0b2f7Stbbdev 
17951c0b2f7Stbbdev // Test for iterators over values convertible to work item type
18051c0b2f7Stbbdev //! \brief \ref error_guessing \ref interface
18151c0b2f7Stbbdev TEST_CASE("Using with values convertible to work item type") {
18251c0b2f7Stbbdev     for ( auto concurrency_level : utils::concurrency_range() ) {
18351c0b2f7Stbbdev         tbb::global_control control(tbb::global_control::max_allowed_parallelism, concurrency_level);
18451c0b2f7Stbbdev         using Iterator = size_t*;
18551c0b2f7Stbbdev         for(size_t depth = 0; depth <= depths_nubmer; ++depth) {
18651c0b2f7Stbbdev             g_tasks_expected = 0;
18751c0b2f7Stbbdev             for (size_t i=0; i < depth; ++i)
18851c0b2f7Stbbdev                 g_tasks_expected += FindNumOfTasks(g_depths[i].value());
18951c0b2f7Stbbdev             // Test for iterators over values convertible to work item type
19051c0b2f7Stbbdev             TestIterator_Common<Iterator>(depth);
19151c0b2f7Stbbdev             TestBody<FakeTaskGeneratorBody_RvalueRefVersion, Iterator>(depth);
19251c0b2f7Stbbdev             TestBody<TaskGeneratorBody_RvalueRefVersion, Iterator>(depth);
19351c0b2f7Stbbdev         }
19451c0b2f7Stbbdev     }
19551c0b2f7Stbbdev }
19651c0b2f7Stbbdev 
19751c0b2f7Stbbdev //! Testing workers going to sleep
19851c0b2f7Stbbdev //! \brief \ref resource_usage \ref stress
19951c0b2f7Stbbdev TEST_CASE("That all workers sleep when no work") {
20051c0b2f7Stbbdev     const std::size_t N = 100000;
20151c0b2f7Stbbdev     std::vector<std::size_t> vec(N, 0);
20251c0b2f7Stbbdev 
__anon577ef66d0102(std::size_t& in) 20351c0b2f7Stbbdev     tbb::parallel_for_each(vec.begin(), vec.end(), [&](std::size_t& in) {
204b15aabb3Stbbdev         for (int i = 0; i < 1000; ++i) {
20551c0b2f7Stbbdev             ++in;
20651c0b2f7Stbbdev         }
20751c0b2f7Stbbdev     });
20851c0b2f7Stbbdev     TestCPUUserTime(utils::get_platform_max_threads());
20951c0b2f7Stbbdev }
210478de5b1Stbbdev 
211478de5b1Stbbdev #if __TBB_CPP20_CONCEPTS_PRESENT
212478de5b1Stbbdev 
213478de5b1Stbbdev template <typename Iterator, typename Body>
214478de5b1Stbbdev concept can_call_parallel_for_each_with_iterator = requires( Iterator it, const Body& body, tbb::task_group_context ctx ) {
215478de5b1Stbbdev     tbb::parallel_for_each(it, it, body);
216478de5b1Stbbdev     tbb::parallel_for_each(it, it, body, ctx);
217478de5b1Stbbdev };
218478de5b1Stbbdev 
219478de5b1Stbbdev template <typename ContainerBasedSequence, typename Body>
220478de5b1Stbbdev concept can_call_parallel_for_each_with_cbs = requires( ContainerBasedSequence cbs,
221478de5b1Stbbdev                                                         const ContainerBasedSequence const_cbs,
222478de5b1Stbbdev                                                         const Body& body, tbb::task_group_context ctx ) {
223478de5b1Stbbdev     tbb::parallel_for_each(cbs, body);
224478de5b1Stbbdev     tbb::parallel_for_each(cbs, body, ctx);
225478de5b1Stbbdev     tbb::parallel_for_each(const_cbs, body);
226478de5b1Stbbdev     tbb::parallel_for_each(const_cbs, body, ctx);
227478de5b1Stbbdev };
228478de5b1Stbbdev 
2294eec89feSIvan Kochin using CorrectCBS = test_concepts::container_based_sequence::Correct;
2304eec89feSIvan Kochin 
231478de5b1Stbbdev template <typename Body>
232478de5b1Stbbdev concept can_call_parallel_for_each =
2334eec89feSIvan Kochin     can_call_parallel_for_each_with_iterator<CorrectCBS::iterator, Body> &&
2344eec89feSIvan Kochin     can_call_parallel_for_each_with_cbs<CorrectCBS, Body>;
235478de5b1Stbbdev 
236478de5b1Stbbdev template <typename Iterator>
237478de5b1Stbbdev using CorrectBody = test_concepts::parallel_for_each_body::Correct<decltype(*std::declval<Iterator>())>;
238478de5b1Stbbdev 
test_pfor_each_iterator_constraints()239478de5b1Stbbdev void test_pfor_each_iterator_constraints() {
240478de5b1Stbbdev     using CorrectIterator = typename std::vector<int>::iterator; // random_access_iterator
241478de5b1Stbbdev     using IncorrectIterator = std::ostream_iterator<int>; // output_iterator
242478de5b1Stbbdev     static_assert(can_call_parallel_for_each_with_iterator<CorrectIterator, CorrectBody<CorrectIterator>>);
243478de5b1Stbbdev     static_assert(!can_call_parallel_for_each_with_iterator<IncorrectIterator, CorrectBody<IncorrectIterator>>);
244478de5b1Stbbdev }
245478de5b1Stbbdev 
test_pfor_each_container_based_sequence_constraints()246478de5b1Stbbdev void test_pfor_each_container_based_sequence_constraints() {
247478de5b1Stbbdev     using namespace test_concepts::container_based_sequence;
2484eec89feSIvan Kochin     static_assert(can_call_parallel_for_each_with_cbs<Correct, CorrectBody<Correct::iterator>>);
2494eec89feSIvan Kochin     static_assert(!can_call_parallel_for_each_with_cbs<NoBegin, CorrectBody<NoBegin::iterator>>);
2504eec89feSIvan Kochin     static_assert(!can_call_parallel_for_each_with_cbs<NoEnd, CorrectBody<NoEnd::iterator>>);
251478de5b1Stbbdev }
252478de5b1Stbbdev 
test_pfor_each_body_constraints()253478de5b1Stbbdev void test_pfor_each_body_constraints() {
254478de5b1Stbbdev     using namespace test_concepts::parallel_for_each_body;
255478de5b1Stbbdev     static_assert(can_call_parallel_for_each<Correct<int>>);
256478de5b1Stbbdev     static_assert(can_call_parallel_for_each<WithFeeder<int>>);
257478de5b1Stbbdev     static_assert(!can_call_parallel_for_each<NoOperatorRoundBrackets<int>>);
258478de5b1Stbbdev     static_assert(!can_call_parallel_for_each<WithFeederNoOperatorRoundBrackets<int>>);
259478de5b1Stbbdev     static_assert(!can_call_parallel_for_each<OperatorRoundBracketsNonConst<int>>);
260478de5b1Stbbdev     static_assert(!can_call_parallel_for_each<WithFeederOperatorRoundBracketsNonConst<int>>);
261478de5b1Stbbdev     static_assert(!can_call_parallel_for_each<WrongInputOperatorRoundBrackets<int>>);
262478de5b1Stbbdev     static_assert(!can_call_parallel_for_each<WithFeederWrongFirstInputOperatorRoundBrackets<int>>);
263478de5b1Stbbdev     static_assert(!can_call_parallel_for_each<WithFeederWrongSecondInputOperatorRoundBrackets<int>>);
264478de5b1Stbbdev }
265478de5b1Stbbdev 
266478de5b1Stbbdev //! \brief \ref error_guessing
267478de5b1Stbbdev TEST_CASE("parallel_for_each constraints") {
268478de5b1Stbbdev     test_pfor_each_iterator_constraints();
269478de5b1Stbbdev     test_pfor_each_container_based_sequence_constraints();
270478de5b1Stbbdev     test_pfor_each_body_constraints();
271478de5b1Stbbdev }
272478de5b1Stbbdev 
273478de5b1Stbbdev #endif // __TBB_CPP20_CONCEPTS_PRESENT
274de0a3a65SKonstantin Boyarinov 
275de0a3a65SKonstantin Boyarinov #if __TBB_CPP20_PRESENT
276de0a3a65SKonstantin Boyarinov 
277de0a3a65SKonstantin Boyarinov struct no_copy_move {
278de0a3a65SKonstantin Boyarinov     no_copy_move() = default;
279de0a3a65SKonstantin Boyarinov 
280de0a3a65SKonstantin Boyarinov     no_copy_move(const no_copy_move&) = delete;
281de0a3a65SKonstantin Boyarinov     no_copy_move(no_copy_move&&) = delete;
282de0a3a65SKonstantin Boyarinov 
283de0a3a65SKonstantin Boyarinov     no_copy_move& operator=(const no_copy_move&) = delete;
284de0a3a65SKonstantin Boyarinov     no_copy_move& operator=(no_copy_move&&) = delete;
285de0a3a65SKonstantin Boyarinov 
286de0a3a65SKonstantin Boyarinov     int item = 0;
287de0a3a65SKonstantin Boyarinov };
288de0a3a65SKonstantin Boyarinov 
289de0a3a65SKonstantin Boyarinov template <typename Category>
test_with_cpp20_iterator()290de0a3a65SKonstantin Boyarinov void test_with_cpp20_iterator() {
291de0a3a65SKonstantin Boyarinov     constexpr std::size_t n = 1'000'000;
292de0a3a65SKonstantin Boyarinov 
293*3b9f9baeSKonstantin Boyarinov     std::vector<no_copy_move> elements(n);
294de0a3a65SKonstantin Boyarinov 
295*3b9f9baeSKonstantin Boyarinov     cpp20_iterator<no_copy_move, Category> begin(elements.data());
296*3b9f9baeSKonstantin Boyarinov     cpp20_iterator<no_copy_move, Category> end(elements.data() + n);
297de0a3a65SKonstantin Boyarinov 
298de0a3a65SKonstantin Boyarinov     oneapi::tbb::parallel_for_each(begin, end, [](no_copy_move& element) {
299de0a3a65SKonstantin Boyarinov         element.item = 42;
300de0a3a65SKonstantin Boyarinov     });
301de0a3a65SKonstantin Boyarinov 
302de0a3a65SKonstantin Boyarinov     for (std::size_t index = 0; index < n; ++index) {
303de0a3a65SKonstantin Boyarinov         CHECK(elements[index].item == 42);
304de0a3a65SKonstantin Boyarinov     }
305de0a3a65SKonstantin Boyarinov }
306de0a3a65SKonstantin Boyarinov 
307de0a3a65SKonstantin Boyarinov //! \brief \ref error_guessing \ref regression
308de0a3a65SKonstantin Boyarinov TEST_CASE("parallel_for_each with cpp20 iterator") {
309de0a3a65SKonstantin Boyarinov     // Test that parallel_for_each threats ignores iterator_category type
310de0a3a65SKonstantin Boyarinov     // if iterator_concept type is defined for iterator
311de0a3a65SKonstantin Boyarinov 
312de0a3a65SKonstantin Boyarinov     // For input iterators parallel_for_each requires element to be
313de0a3a65SKonstantin Boyarinov     // copyable or movable so since cpp20_iterator is at least forward
314de0a3a65SKonstantin Boyarinov     // parallel_for_each should work with cpp20_iterator
315de0a3a65SKonstantin Boyarinov     // on non-copyable and non-movable type
316de0a3a65SKonstantin Boyarinov 
317de0a3a65SKonstantin Boyarinov     // test cpp20_iterator implementation
318de0a3a65SKonstantin Boyarinov     using cpp20_forward_iterator = cpp20_iterator<int, std::forward_iterator_tag>;
319de0a3a65SKonstantin Boyarinov     using cpp20_bidirectional_iterator = cpp20_iterator<int, std::bidirectional_iterator_tag>;
320de0a3a65SKonstantin Boyarinov     using cpp20_random_access_iterator = cpp20_iterator<int, std::random_access_iterator_tag>;
321de0a3a65SKonstantin Boyarinov 
322de0a3a65SKonstantin Boyarinov     static_assert(std::forward_iterator<cpp20_forward_iterator>);
323de0a3a65SKonstantin Boyarinov     static_assert(!std::bidirectional_iterator<cpp20_forward_iterator>);
324de0a3a65SKonstantin Boyarinov 
325de0a3a65SKonstantin Boyarinov     static_assert(std::bidirectional_iterator<cpp20_bidirectional_iterator>);
326de0a3a65SKonstantin Boyarinov     static_assert(!std::random_access_iterator<cpp20_bidirectional_iterator>);
327de0a3a65SKonstantin Boyarinov 
328de0a3a65SKonstantin Boyarinov     static_assert(std::random_access_iterator<cpp20_random_access_iterator>);
329de0a3a65SKonstantin Boyarinov 
330de0a3a65SKonstantin Boyarinov     test_with_cpp20_iterator<std::forward_iterator_tag>();
331de0a3a65SKonstantin Boyarinov     test_with_cpp20_iterator<std::bidirectional_iterator_tag>();
332de0a3a65SKonstantin Boyarinov     test_with_cpp20_iterator<std::random_access_iterator_tag>();
333de0a3a65SKonstantin Boyarinov }
334de0a3a65SKonstantin Boyarinov 
335de0a3a65SKonstantin Boyarinov #endif // __TBB_CPP20_PRESENT
336