xref: /oneTBB/test/tbb/test_parallel_for.cpp (revision c21e688a)
151c0b2f7Stbbdev /*
2*c21e688aSSergey Zheltov     Copyright (c) 2005-2022 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/test.h"
18478de5b1Stbbdev 
19478de5b1Stbbdev #include "tbb/parallel_for.h"
20478de5b1Stbbdev 
2151c0b2f7Stbbdev #include "common/config.h"
2251c0b2f7Stbbdev #include "common/utils.h"
2351c0b2f7Stbbdev #include "common/utils_concurrency_limit.h"
2451c0b2f7Stbbdev #include "common/utils_report.h"
2551c0b2f7Stbbdev #include "common/vector_types.h"
2651c0b2f7Stbbdev #include "common/cpu_usertime.h"
2751c0b2f7Stbbdev #include "common/spin_barrier.h"
2851c0b2f7Stbbdev #include "common/exception_handling.h"
29478de5b1Stbbdev #include "common/concepts_common.h"
30478de5b1Stbbdev #include "test_partitioner.h"
3151c0b2f7Stbbdev 
32478de5b1Stbbdev #include <cstddef>
3351c0b2f7Stbbdev #include <vector>
3451c0b2f7Stbbdev 
3551c0b2f7Stbbdev //! \file test_parallel_for.cpp
3651c0b2f7Stbbdev //! \brief Test for [algorithms.parallel_for] specification
3751c0b2f7Stbbdev 
3851c0b2f7Stbbdev #if _MSC_VER
3951c0b2f7Stbbdev #pragma warning (push)
4055f9b178SIvan Kochin // Suppress conditional expression is constant
4155f9b178SIvan Kochin #pragma warning (disable: 4127)
4251c0b2f7Stbbdev #if __TBB_MSVC_UNREACHABLE_CODE_IGNORED
4351c0b2f7Stbbdev     // Suppress pointless "unreachable code" warning.
4451c0b2f7Stbbdev     #pragma warning (disable: 4702)
4551c0b2f7Stbbdev #endif
4651c0b2f7Stbbdev #if defined(_Wp64)
4751c0b2f7Stbbdev     // Workaround for overzealous compiler warnings in /Wp64 mode
4851c0b2f7Stbbdev     #pragma warning (disable: 4267)
4951c0b2f7Stbbdev #endif
5051c0b2f7Stbbdev #define _SCL_SECURE_NO_WARNINGS
5151c0b2f7Stbbdev #endif //#if _MSC_VER
5251c0b2f7Stbbdev 
5351c0b2f7Stbbdev 
5451c0b2f7Stbbdev #if (HAVE_m128 || HAVE_m256)
5551c0b2f7Stbbdev template<typename ClassWithVectorType>
5651c0b2f7Stbbdev struct SSE_Functor {
5751c0b2f7Stbbdev     ClassWithVectorType* Src, * Dst;
SSE_FunctorSSE_Functor5851c0b2f7Stbbdev     SSE_Functor( ClassWithVectorType* src, ClassWithVectorType* dst ) : Src(src), Dst(dst) {}
5951c0b2f7Stbbdev 
operator ()SSE_Functor6051c0b2f7Stbbdev     void operator()( tbb::blocked_range<int>& r ) const {
6151c0b2f7Stbbdev         for( int i=r.begin(); i!=r.end(); ++i )
6251c0b2f7Stbbdev             Dst[i] = Src[i];
6351c0b2f7Stbbdev     }
6451c0b2f7Stbbdev };
6551c0b2f7Stbbdev 
6651c0b2f7Stbbdev //! Test that parallel_for works with stack-allocated __m128
6751c0b2f7Stbbdev template<typename ClassWithVectorType>
TestVectorTypes()6851c0b2f7Stbbdev void TestVectorTypes() {
6951c0b2f7Stbbdev     const int aSize = 300;
7051c0b2f7Stbbdev     ClassWithVectorType Array1[aSize], Array2[aSize];
7151c0b2f7Stbbdev     for( int i=0; i<aSize; ++i ) {
7251c0b2f7Stbbdev         // VC8 does not properly align a temporary value; to work around, use explicit variable
7351c0b2f7Stbbdev         ClassWithVectorType foo(i);
7451c0b2f7Stbbdev         Array1[i] = foo;
7551c0b2f7Stbbdev     }
7651c0b2f7Stbbdev     tbb::parallel_for( tbb::blocked_range<int>(0,aSize), SSE_Functor<ClassWithVectorType>(Array1, Array2) );
7751c0b2f7Stbbdev     for( int i=0; i<aSize; ++i ) {
7851c0b2f7Stbbdev         ClassWithVectorType foo(i);
7951c0b2f7Stbbdev         CHECK( Array2[i]==foo ) ;
8051c0b2f7Stbbdev     }
8151c0b2f7Stbbdev }
8251c0b2f7Stbbdev #endif /* HAVE_m128 || HAVE_m256 */
8351c0b2f7Stbbdev 
8451c0b2f7Stbbdev struct TestSimplePartitionerStabilityFunctor {
8551c0b2f7Stbbdev   std::vector<int> & ranges;
TestSimplePartitionerStabilityFunctorTestSimplePartitionerStabilityFunctor8651c0b2f7Stbbdev   TestSimplePartitionerStabilityFunctor(std::vector<int> & theRanges):ranges(theRanges){}
operator ()TestSimplePartitionerStabilityFunctor8751c0b2f7Stbbdev   void operator()(tbb::blocked_range<size_t>& r)const{
8851c0b2f7Stbbdev       ranges.at(r.begin()) = 1;
8951c0b2f7Stbbdev   }
9051c0b2f7Stbbdev };
TestSimplePartitionerStability()9151c0b2f7Stbbdev void TestSimplePartitionerStability(){
9251c0b2f7Stbbdev     const std::size_t repeat_count= 10;
9351c0b2f7Stbbdev     const std::size_t rangeToSplitSize=1000000;
9451c0b2f7Stbbdev     const std::size_t grainsizeStep=rangeToSplitSize/repeat_count;
9551c0b2f7Stbbdev     typedef TestSimplePartitionerStabilityFunctor FunctorType;
9651c0b2f7Stbbdev 
9751c0b2f7Stbbdev     for (std::size_t i=0 , grainsize=grainsizeStep; i<repeat_count;i++, grainsize+=grainsizeStep){
9851c0b2f7Stbbdev         std::vector<int> firstSeries(rangeToSplitSize,0);
9951c0b2f7Stbbdev         std::vector<int> secondSeries(rangeToSplitSize,0);
10051c0b2f7Stbbdev 
10151c0b2f7Stbbdev         tbb::parallel_for(tbb::blocked_range<size_t>(0,rangeToSplitSize,grainsize),FunctorType(firstSeries),tbb::simple_partitioner());
10251c0b2f7Stbbdev         tbb::parallel_for(tbb::blocked_range<size_t>(0,rangeToSplitSize,grainsize),FunctorType(secondSeries),tbb::simple_partitioner());
103478de5b1Stbbdev 
104478de5b1Stbbdev         CHECK_MESSAGE(
105478de5b1Stbbdev             firstSeries == secondSeries,
106478de5b1Stbbdev             "Splitting range with tbb::simple_partitioner must be reproducible; i = " << i
107478de5b1Stbbdev         );
10851c0b2f7Stbbdev     }
10951c0b2f7Stbbdev }
11051c0b2f7Stbbdev 
11151c0b2f7Stbbdev namespace various_range_implementations {
11251c0b2f7Stbbdev 
11351c0b2f7Stbbdev using namespace test_partitioner_utils;
11451c0b2f7Stbbdev using namespace test_partitioner_utils::TestRanges;
11551c0b2f7Stbbdev 
11651c0b2f7Stbbdev // Body ensures that initial work distribution is done uniformly through affinity mechanism and not through work stealing
11751c0b2f7Stbbdev class Body {
11851c0b2f7Stbbdev     utils::SpinBarrier &m_sb;
11951c0b2f7Stbbdev public:
Body(utils::SpinBarrier & sb)12051c0b2f7Stbbdev     Body(utils::SpinBarrier& sb) : m_sb(sb) { }
Body(Body & b,tbb::split)12151c0b2f7Stbbdev     Body(Body& b, tbb::split) : m_sb(b.m_sb) { }
12251c0b2f7Stbbdev 
12351c0b2f7Stbbdev     template <typename Range>
operator ()(Range & r) const12451c0b2f7Stbbdev     void operator()(Range& r) const {
12551c0b2f7Stbbdev         INFO("Executing range [" << r.begin() << ", " << r.end() << "]");
126b15aabb3Stbbdev         m_sb.wait(); // waiting for all threads
12751c0b2f7Stbbdev     }
12851c0b2f7Stbbdev };
12951c0b2f7Stbbdev 
13051c0b2f7Stbbdev namespace correctness {
13151c0b2f7Stbbdev 
13251c0b2f7Stbbdev /* Testing only correctness (that is parallel_for does not hang) */
13351c0b2f7Stbbdev template <typename RangeType, bool /* feedback */, bool ensure_non_emptiness>
test()13451c0b2f7Stbbdev void test() {
13557f524caSIlya Isaev     RangeType range( 0, utils::get_platform_max_threads(), nullptr, false, ensure_non_emptiness );
13651c0b2f7Stbbdev     tbb::affinity_partitioner ap;
13751c0b2f7Stbbdev     tbb::parallel_for( range, SimpleBody(), ap );
13851c0b2f7Stbbdev }
13951c0b2f7Stbbdev 
14051c0b2f7Stbbdev } // namespace correctness
14151c0b2f7Stbbdev 
14251c0b2f7Stbbdev namespace uniform_distribution {
14351c0b2f7Stbbdev 
14451c0b2f7Stbbdev /* Body of parallel_for algorithm would hang if non-uniform work distribution happened  */
14551c0b2f7Stbbdev template <typename RangeType, bool feedback, bool ensure_non_emptiness>
test()14651c0b2f7Stbbdev void test() {
14751c0b2f7Stbbdev     static const std::size_t thread_num = utils::get_platform_max_threads();
14851c0b2f7Stbbdev     utils::SpinBarrier sb( thread_num );
14957f524caSIlya Isaev     RangeType range(0, thread_num, nullptr, feedback, ensure_non_emptiness);
15051c0b2f7Stbbdev     const Body sync_body( sb );
15151c0b2f7Stbbdev     tbb::affinity_partitioner ap;
15251c0b2f7Stbbdev     tbb::parallel_for( range, sync_body, ap );
15351c0b2f7Stbbdev     tbb::parallel_for( range, sync_body, tbb::static_partitioner() );
15451c0b2f7Stbbdev }
15551c0b2f7Stbbdev 
15651c0b2f7Stbbdev } // namespace uniform_distribution
15751c0b2f7Stbbdev 
test()15851c0b2f7Stbbdev void test() {
15951c0b2f7Stbbdev     const bool provide_feedback = false;
16051c0b2f7Stbbdev     const bool ensure_non_empty_range = true;
16151c0b2f7Stbbdev 
16251c0b2f7Stbbdev     // BlockedRange does not take into account feedback and non-emptiness settings but uses the
16351c0b2f7Stbbdev     // tbb::blocked_range implementation
16451c0b2f7Stbbdev     uniform_distribution::test<BlockedRange, !provide_feedback, !ensure_non_empty_range>();
16551c0b2f7Stbbdev     using correctness::test;
16651c0b2f7Stbbdev 
16751c0b2f7Stbbdev     {
16851c0b2f7Stbbdev         test<RoundedDownRange, provide_feedback, ensure_non_empty_range>();
16951c0b2f7Stbbdev         test<RoundedDownRange, provide_feedback, !ensure_non_empty_range>();
17051c0b2f7Stbbdev     }
17151c0b2f7Stbbdev 
17251c0b2f7Stbbdev     {
17351c0b2f7Stbbdev         test<RoundedUpRange, provide_feedback, ensure_non_empty_range>();
17451c0b2f7Stbbdev         test<RoundedUpRange, provide_feedback, !ensure_non_empty_range>();
17551c0b2f7Stbbdev     }
17651c0b2f7Stbbdev 
17751c0b2f7Stbbdev     // Testing that parallel_for algorithm works with such weird ranges
17851c0b2f7Stbbdev     correctness::test<Range1_2, /* provide_feedback= */ false, !ensure_non_empty_range>();
17951c0b2f7Stbbdev     correctness::test<Range1_999, /* provide_feedback= */ false, !ensure_non_empty_range>();
18051c0b2f7Stbbdev     correctness::test<Range999_1, /* provide_feedback= */ false, !ensure_non_empty_range>();
18151c0b2f7Stbbdev 
18251c0b2f7Stbbdev     // The following ranges do not comply with the proportion suggested by partitioner. Therefore
18351c0b2f7Stbbdev     // they have to provide the proportion in which they were actually split back to partitioner and
18451c0b2f7Stbbdev     // ensure theirs non-emptiness
18551c0b2f7Stbbdev     test<Range1_2, provide_feedback, ensure_non_empty_range>();
18651c0b2f7Stbbdev     test<Range1_999, provide_feedback, ensure_non_empty_range>();
18751c0b2f7Stbbdev     test<Range999_1, provide_feedback, ensure_non_empty_range>();
18851c0b2f7Stbbdev }
18951c0b2f7Stbbdev 
19051c0b2f7Stbbdev } // namespace various_range_implementations
19151c0b2f7Stbbdev 
19251c0b2f7Stbbdev namespace test_cancellation {
19351c0b2f7Stbbdev 
19451c0b2f7Stbbdev struct FunctorToCancel {
19551c0b2f7Stbbdev     static std::atomic<bool> need_to_wait;
19651c0b2f7Stbbdev 
operator ()test_cancellation::FunctorToCancel19751c0b2f7Stbbdev     void operator()( std::size_t ) const {
19851c0b2f7Stbbdev         ++g_CurExecuted;
19951c0b2f7Stbbdev         if (need_to_wait) {
20051c0b2f7Stbbdev             need_to_wait = Cancellator::WaitUntilReady();
20151c0b2f7Stbbdev         }
20251c0b2f7Stbbdev     }
20351c0b2f7Stbbdev 
operator ()test_cancellation::FunctorToCancel20451c0b2f7Stbbdev     void operator()( const tbb::blocked_range<std::size_t>& ) const {
20551c0b2f7Stbbdev         ++g_CurExecuted;
20651c0b2f7Stbbdev         Cancellator::WaitUntilReady();
20751c0b2f7Stbbdev     }
20851c0b2f7Stbbdev 
resettest_cancellation::FunctorToCancel20951c0b2f7Stbbdev     static void reset() { need_to_wait = true; }
21051c0b2f7Stbbdev }; // struct FunctorToCancel
21151c0b2f7Stbbdev 
21251c0b2f7Stbbdev std::atomic<bool> FunctorToCancel::need_to_wait(true);
21351c0b2f7Stbbdev 
21451c0b2f7Stbbdev static constexpr std::size_t buffer_test_size = 1024;
21551c0b2f7Stbbdev static constexpr std::size_t maxParallelForRunnerMode = 14;
21651c0b2f7Stbbdev 
21751c0b2f7Stbbdev template <std::size_t Mode>
21851c0b2f7Stbbdev class ParallelForRunner {
21951c0b2f7Stbbdev     tbb::task_group_context& my_ctx;
22051c0b2f7Stbbdev     const std::size_t worker_task_step = 1;
22151c0b2f7Stbbdev 
22251c0b2f7Stbbdev     static_assert(Mode >= 0 && Mode <= maxParallelForRunnerMode, "Incorrect mode for ParallelForRunner");
22351c0b2f7Stbbdev 
22451c0b2f7Stbbdev     template <typename Partitioner, typename... Args>
run_parallel_for(Args &&...args) const22551c0b2f7Stbbdev     void run_parallel_for( Args&&... args ) const {
22651c0b2f7Stbbdev         Partitioner part;
22751c0b2f7Stbbdev         tbb::parallel_for(std::forward<Args>(args)..., part, my_ctx);
22851c0b2f7Stbbdev     }
22951c0b2f7Stbbdev 
23051c0b2f7Stbbdev     template <typename... Args>
run_overload(Args &&...args) const23151c0b2f7Stbbdev     void run_overload( Args&&... args ) const {
23251c0b2f7Stbbdev 
23351c0b2f7Stbbdev         switch(Mode % 5) {
23451c0b2f7Stbbdev             case 0 : {
23551c0b2f7Stbbdev                 tbb::parallel_for(std::forward<Args>(args)..., my_ctx);
23651c0b2f7Stbbdev                 break;
23751c0b2f7Stbbdev             }
23851c0b2f7Stbbdev             case 1 : {
23951c0b2f7Stbbdev                 run_parallel_for<tbb::simple_partitioner>(std::forward<Args>(args)...);
24051c0b2f7Stbbdev                 break;
24151c0b2f7Stbbdev             }
24251c0b2f7Stbbdev             case 2 : {
24351c0b2f7Stbbdev                 run_parallel_for<tbb::auto_partitioner>(std::forward<Args>(args)...);
24451c0b2f7Stbbdev                 break;
24551c0b2f7Stbbdev             }
24651c0b2f7Stbbdev             case 3 : {
24751c0b2f7Stbbdev                 run_parallel_for<tbb::static_partitioner>(std::forward<Args>(args)...);
24851c0b2f7Stbbdev                 break;
24951c0b2f7Stbbdev             }
25051c0b2f7Stbbdev             case 4 : {
25151c0b2f7Stbbdev                 run_parallel_for<tbb::affinity_partitioner>(std::forward<Args>(args)...);
25251c0b2f7Stbbdev                 break;
25351c0b2f7Stbbdev             }
25451c0b2f7Stbbdev         }
25551c0b2f7Stbbdev     }
25651c0b2f7Stbbdev 
25751c0b2f7Stbbdev public:
ParallelForRunner(tbb::task_group_context & ctx)25851c0b2f7Stbbdev     ParallelForRunner( tbb::task_group_context& ctx )
25951c0b2f7Stbbdev         : my_ctx(ctx) {}
26051c0b2f7Stbbdev 
~ParallelForRunner()26151c0b2f7Stbbdev     ~ParallelForRunner() { FunctorToCancel::reset(); }
26251c0b2f7Stbbdev 
operator ()() const26351c0b2f7Stbbdev     void operator()() const {
26451c0b2f7Stbbdev         if (Mode < 5) {
26551c0b2f7Stbbdev             // Overload with blocked range
26651c0b2f7Stbbdev             tbb::blocked_range<std::size_t> br(0, buffer_test_size);
26751c0b2f7Stbbdev             run_overload(br, FunctorToCancel{});
26851c0b2f7Stbbdev         } else if (Mode < 10) {
26951c0b2f7Stbbdev             // Overload with two indexes
27051c0b2f7Stbbdev             run_overload(std::size_t(0), buffer_test_size, FunctorToCancel{});
27151c0b2f7Stbbdev         } else {
27251c0b2f7Stbbdev             // Overload with two indexes and step
27351c0b2f7Stbbdev             run_overload(std::size_t(0), buffer_test_size, worker_task_step, FunctorToCancel{});
27451c0b2f7Stbbdev         }
27551c0b2f7Stbbdev     }
27651c0b2f7Stbbdev }; // class ParallelForRunner
27751c0b2f7Stbbdev 
27851c0b2f7Stbbdev template <std::size_t Mode>
run_parallel_for_cancellation_test()27951c0b2f7Stbbdev void run_parallel_for_cancellation_test() {
28051c0b2f7Stbbdev     // TODO: enable concurrency_range
281478de5b1Stbbdev     if (utils::get_platform_max_threads() < 2) {
282478de5b1Stbbdev         // The test requires at least one worker thread to request cancellation
283478de5b1Stbbdev         return;
284478de5b1Stbbdev     }
28551c0b2f7Stbbdev     ResetEhGlobals();
28651c0b2f7Stbbdev     RunCancellationTest<ParallelForRunner<Mode>, Cancellator>();
28751c0b2f7Stbbdev }
28851c0b2f7Stbbdev 
28951c0b2f7Stbbdev template <std::size_t Mode>
29051c0b2f7Stbbdev struct ParallelForTestRunner {
runtest_cancellation::ParallelForTestRunner29151c0b2f7Stbbdev     static void run() {
29251c0b2f7Stbbdev         run_parallel_for_cancellation_test<Mode>();
29351c0b2f7Stbbdev         ParallelForTestRunner<Mode + 1>::run();
29451c0b2f7Stbbdev     }
29551c0b2f7Stbbdev }; // struct ParallelForTestRunner
29651c0b2f7Stbbdev 
29751c0b2f7Stbbdev template <>
29851c0b2f7Stbbdev struct ParallelForTestRunner<maxParallelForRunnerMode> {
runtest_cancellation::ParallelForTestRunner29951c0b2f7Stbbdev     static void run() {
30051c0b2f7Stbbdev         run_parallel_for_cancellation_test<maxParallelForRunnerMode>();
30151c0b2f7Stbbdev     }
30251c0b2f7Stbbdev }; // struct ParallelForTestRunner<maxParallelForRunnerMode>
30351c0b2f7Stbbdev 
30451c0b2f7Stbbdev } // namespace test_cancellation
30551c0b2f7Stbbdev 
306478de5b1Stbbdev #if __TBB_CPP20_CONCEPTS_PRESENT
307478de5b1Stbbdev template <typename... Args>
308478de5b1Stbbdev concept can_call_parallel_for_basic = requires( Args&&... args ) {
309478de5b1Stbbdev     tbb::parallel_for(std::forward<Args>(args)...);
310478de5b1Stbbdev };
311478de5b1Stbbdev 
312478de5b1Stbbdev template <typename... Args>
313478de5b1Stbbdev concept can_call_parallel_for_helper = can_call_parallel_for_basic<Args...> &&
314478de5b1Stbbdev                                        can_call_parallel_for_basic<Args..., tbb::task_group_context&>;
315478de5b1Stbbdev 
316478de5b1Stbbdev template <typename... Args>
317478de5b1Stbbdev concept can_call_parallel_for_with_partitioner = can_call_parallel_for_helper<Args...> &&
318478de5b1Stbbdev                                                  can_call_parallel_for_helper<Args..., const tbb::simple_partitioner&> &&
319478de5b1Stbbdev                                                  can_call_parallel_for_helper<Args..., const tbb::auto_partitioner&> &&
320478de5b1Stbbdev                                                  can_call_parallel_for_helper<Args..., const tbb::static_partitioner> &&
321478de5b1Stbbdev                                                  can_call_parallel_for_helper<Args..., tbb::affinity_partitioner&>;
322478de5b1Stbbdev 
323478de5b1Stbbdev template <typename Range, typename Body>
324478de5b1Stbbdev concept can_call_range_pfor = can_call_parallel_for_with_partitioner<const Range&, const Body&>;
325478de5b1Stbbdev 
326478de5b1Stbbdev template <typename Index, typename Function>
327478de5b1Stbbdev concept can_call_index_pfor = can_call_parallel_for_with_partitioner<Index, Index, const Function&> &&
328478de5b1Stbbdev                               can_call_parallel_for_with_partitioner<Index, Index, Index, const Function&>;
329478de5b1Stbbdev 
330478de5b1Stbbdev 
331478de5b1Stbbdev template <typename Range>
332478de5b1Stbbdev using CorrectBody = test_concepts::parallel_for_body::Correct<Range>;
333478de5b1Stbbdev template <typename Index>
334478de5b1Stbbdev using CorrectFunc = test_concepts::parallel_for_function::Correct<Index>;
335478de5b1Stbbdev 
test_pfor_range_constraints()336478de5b1Stbbdev void test_pfor_range_constraints() {
337478de5b1Stbbdev     using namespace test_concepts::range;
338478de5b1Stbbdev 
339478de5b1Stbbdev     static_assert(can_call_range_pfor<Correct, CorrectBody<Correct>>);
340478de5b1Stbbdev     static_assert(!can_call_range_pfor<NonCopyable, CorrectBody<NonCopyable>>);
341478de5b1Stbbdev     static_assert(!can_call_range_pfor<NonSplittable, CorrectBody<NonSplittable>>);
342478de5b1Stbbdev     static_assert(!can_call_range_pfor<NonDestructible, CorrectBody<NonDestructible>>);
343478de5b1Stbbdev     static_assert(!can_call_range_pfor<NoEmpty, CorrectBody<NoEmpty>>);
344478de5b1Stbbdev     static_assert(!can_call_range_pfor<EmptyNonConst, CorrectBody<EmptyNonConst>>);
345478de5b1Stbbdev     static_assert(!can_call_range_pfor<WrongReturnEmpty, CorrectBody<WrongReturnEmpty>>);
346478de5b1Stbbdev     static_assert(!can_call_range_pfor<NoIsDivisible, CorrectBody<NoIsDivisible>>);
347478de5b1Stbbdev     static_assert(!can_call_range_pfor<IsDivisibleNonConst, CorrectBody<IsDivisibleNonConst>>);
348478de5b1Stbbdev     static_assert(!can_call_range_pfor<WrongReturnIsDivisible, CorrectBody<WrongReturnIsDivisible>>);
349478de5b1Stbbdev }
350478de5b1Stbbdev 
test_pfor_body_constraints()351478de5b1Stbbdev void test_pfor_body_constraints() {
352478de5b1Stbbdev     using namespace test_concepts::parallel_for_body;
353478de5b1Stbbdev     using CorrectRange = test_concepts::range::Correct;
354478de5b1Stbbdev 
355478de5b1Stbbdev     static_assert(can_call_range_pfor<CorrectRange, Correct<CorrectRange>>);
356478de5b1Stbbdev     static_assert(!can_call_range_pfor<CorrectRange, NonCopyable<CorrectRange>>);
357478de5b1Stbbdev     static_assert(!can_call_range_pfor<CorrectRange, NonDestructible<CorrectRange>>);
358478de5b1Stbbdev     static_assert(!can_call_range_pfor<CorrectRange, NoOperatorRoundBrackets<CorrectRange>>);
359478de5b1Stbbdev     static_assert(!can_call_range_pfor<CorrectRange, OperatorRoundBracketsNonConst<CorrectRange>>);
360478de5b1Stbbdev     static_assert(!can_call_range_pfor<CorrectRange, WrongInputOperatorRoundBrackets<CorrectRange>>);
361478de5b1Stbbdev }
362478de5b1Stbbdev 
test_pfor_func_constraints()363478de5b1Stbbdev void test_pfor_func_constraints() {
364478de5b1Stbbdev     using namespace test_concepts::parallel_for_function;
365478de5b1Stbbdev     using CorrectIndex = test_concepts::parallel_for_index::Correct;
366478de5b1Stbbdev 
367478de5b1Stbbdev     static_assert(can_call_index_pfor<CorrectIndex, Correct<CorrectIndex>>);
368478de5b1Stbbdev     static_assert(!can_call_index_pfor<CorrectIndex, NoOperatorRoundBrackets<CorrectIndex>>);
369478de5b1Stbbdev     static_assert(!can_call_index_pfor<CorrectIndex, OperatorRoundBracketsNonConst<CorrectIndex>>);
370478de5b1Stbbdev     static_assert(!can_call_index_pfor<CorrectIndex, WrongInputOperatorRoundBrackets<CorrectIndex>>);
371478de5b1Stbbdev }
372478de5b1Stbbdev 
test_pfor_index_constraints()373478de5b1Stbbdev void test_pfor_index_constraints() {
374478de5b1Stbbdev     using namespace test_concepts::parallel_for_index;
375478de5b1Stbbdev     static_assert(can_call_index_pfor<Correct, CorrectFunc<Correct>>);
376478de5b1Stbbdev     static_assert(!can_call_index_pfor<NoIntCtor, CorrectFunc<NoIntCtor>>);
377478de5b1Stbbdev     static_assert(!can_call_index_pfor<NonCopyable, CorrectFunc<NonCopyable>>);
378478de5b1Stbbdev     static_assert(!can_call_index_pfor<NonCopyAssignable, CorrectFunc<NonCopyAssignable>>);
379478de5b1Stbbdev     static_assert(!can_call_index_pfor<NonDestructible, CorrectFunc<NonDestructible>>);
380478de5b1Stbbdev     static_assert(!can_call_index_pfor<NoOperatorLess, CorrectFunc<NoOperatorLess>>);
381478de5b1Stbbdev     static_assert(!can_call_index_pfor<OperatorLessNonConst, CorrectFunc<OperatorLessNonConst>>);
382478de5b1Stbbdev     static_assert(!can_call_index_pfor<WrongInputOperatorLess, CorrectFunc<WrongInputOperatorLess>>);
383478de5b1Stbbdev     static_assert(!can_call_index_pfor<WrongReturnOperatorLess, CorrectFunc<WrongReturnOperatorLess>>);
384478de5b1Stbbdev     static_assert(!can_call_index_pfor<NoOperatorMinus, CorrectFunc<NoOperatorMinus>>);
385478de5b1Stbbdev     static_assert(!can_call_index_pfor<OperatorMinusNonConst, CorrectFunc<OperatorMinusNonConst>>);
386478de5b1Stbbdev     static_assert(!can_call_index_pfor<WrongInputOperatorMinus, CorrectFunc<WrongInputOperatorMinus>>);
387478de5b1Stbbdev     static_assert(!can_call_index_pfor<WrongReturnOperatorMinus, CorrectFunc<WrongReturnOperatorMinus>>);
388478de5b1Stbbdev     static_assert(!can_call_index_pfor<NoOperatorPlus, CorrectFunc<NoOperatorPlus>>);
389478de5b1Stbbdev     static_assert(!can_call_index_pfor<OperatorPlusNonConst, CorrectFunc<OperatorPlusNonConst>>);
390478de5b1Stbbdev     static_assert(!can_call_index_pfor<WrongInputOperatorPlus, CorrectFunc<WrongInputOperatorPlus>>);
391478de5b1Stbbdev     static_assert(!can_call_index_pfor<WrongReturnOperatorPlus, CorrectFunc<WrongReturnOperatorPlus>>);
392478de5b1Stbbdev }
393478de5b1Stbbdev #endif // __TBB_CPP20_CONCEPTS_PRESENT
394478de5b1Stbbdev 
39551c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN && TBB_REVAMP_TODO
396478de5b1Stbbdev #include "tbb/global_control.h"
39751c0b2f7Stbbdev //! Testing exceptions
39851c0b2f7Stbbdev //! \brief \ref requirement
39951c0b2f7Stbbdev TEST_CASE("Exceptions support") {
40051c0b2f7Stbbdev     for ( int p = MinThread; p <= MaxThread; ++p ) {
40151c0b2f7Stbbdev         if ( p > 0 ) {
40251c0b2f7Stbbdev             tbb::global_control control(tbb::global_control::max_allowed_parallelism, p);
40351c0b2f7Stbbdev             TestExceptionsSupport();
40451c0b2f7Stbbdev         }
40551c0b2f7Stbbdev     }
40651c0b2f7Stbbdev }
40751c0b2f7Stbbdev #endif /* TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN */
40851c0b2f7Stbbdev 
40951c0b2f7Stbbdev //! Testing cancellation
41051c0b2f7Stbbdev //! \brief \ref error_guessing
41151c0b2f7Stbbdev TEST_CASE("Vector types") {
41251c0b2f7Stbbdev #if HAVE_m128
41351c0b2f7Stbbdev     TestVectorTypes<ClassWithSSE>();
41451c0b2f7Stbbdev #endif
41551c0b2f7Stbbdev #if HAVE_m256
41651c0b2f7Stbbdev     if (have_AVX()) TestVectorTypes<ClassWithAVX>();
41751c0b2f7Stbbdev #endif
41851c0b2f7Stbbdev }
41951c0b2f7Stbbdev 
42051c0b2f7Stbbdev //! Testing workers going to sleep
42151c0b2f7Stbbdev //! \brief \ref resource_usage
42251c0b2f7Stbbdev TEST_CASE("That all workers sleep when no work") {
42351c0b2f7Stbbdev     const std::size_t N = 100000;
42451c0b2f7Stbbdev     std::atomic<int> counter{};
42551c0b2f7Stbbdev 
__anon95d702fd0102(std::size_t) 42651c0b2f7Stbbdev     tbb::parallel_for(std::size_t(0), N, [&](std::size_t) {
427b15aabb3Stbbdev         for (int i = 0; i < 1000; ++i) {
42851c0b2f7Stbbdev             ++counter;
42951c0b2f7Stbbdev         }
43051c0b2f7Stbbdev     }, tbb::simple_partitioner());
43151c0b2f7Stbbdev     TestCPUUserTime(utils::get_platform_max_threads());
43251c0b2f7Stbbdev }
43351c0b2f7Stbbdev 
43451c0b2f7Stbbdev //! Testing simple partitioner stability
43551c0b2f7Stbbdev //! \brief \ref error_guessing
43651c0b2f7Stbbdev TEST_CASE("Simple partitioner stability") {
43751c0b2f7Stbbdev     TestSimplePartitionerStability();
43851c0b2f7Stbbdev }
43951c0b2f7Stbbdev 
44051c0b2f7Stbbdev //! Testing various range implementations
44151c0b2f7Stbbdev //! \brief \ref requirement
44251c0b2f7Stbbdev TEST_CASE("Various range implementations") {
44351c0b2f7Stbbdev     various_range_implementations::test();
44451c0b2f7Stbbdev }
44551c0b2f7Stbbdev 
44651c0b2f7Stbbdev //! Testing parallel_for with explicit task_group_context
44751c0b2f7Stbbdev //! \brief \ref interface \ref error_guessing
44851c0b2f7Stbbdev TEST_CASE("Сancellation test for tbb::parallel_for") {
44951c0b2f7Stbbdev     test_cancellation::ParallelForTestRunner</*FirstMode = */0>::run();
45051c0b2f7Stbbdev }
45151c0b2f7Stbbdev 
452478de5b1Stbbdev #if __TBB_CPP20_CONCEPTS_PRESENT
453478de5b1Stbbdev //! \brief \ref error_guessing
454478de5b1Stbbdev TEST_CASE("parallel_for constraints") {
455478de5b1Stbbdev     test_pfor_range_constraints();
456478de5b1Stbbdev     test_pfor_body_constraints();
457478de5b1Stbbdev     test_pfor_func_constraints();
458478de5b1Stbbdev     test_pfor_index_constraints();
459478de5b1Stbbdev }
460478de5b1Stbbdev #endif // __TBB_CPP20_CONCEPTS_PRESENT
461478de5b1Stbbdev 
46251c0b2f7Stbbdev #if _MSC_VER
46351c0b2f7Stbbdev #pragma warning (pop)
46451c0b2f7Stbbdev #endif
465