151c0b2f7Stbbdev /*
2b15aabb3Stbbdev     Copyright (c) 2005-2021 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 
17b15aabb3Stbbdev #if _MSC_VER
18b15aabb3Stbbdev #if __INTEL_COMPILER
19b15aabb3Stbbdev     #pragma warning(disable : 2586) // decorated name length exceeded, name was truncated
20b15aabb3Stbbdev #else
2151c0b2f7Stbbdev     // Workaround for vs2015 and warning name was longer than the compiler limit (4096).
2251c0b2f7Stbbdev     #pragma warning (disable: 4503)
2351c0b2f7Stbbdev #endif
24b15aabb3Stbbdev #endif
2551c0b2f7Stbbdev 
2651c0b2f7Stbbdev #include "common/test.h"
2751c0b2f7Stbbdev #include "common/utils.h"
2851c0b2f7Stbbdev #include "common/utils_report.h"
2951c0b2f7Stbbdev #include "common/utils_concurrency_limit.h"
3051c0b2f7Stbbdev #include "common/spin_barrier.h"
3151c0b2f7Stbbdev #include "common/checktype.h"
32b15aabb3Stbbdev #include "common/test_comparisons.h"
3351c0b2f7Stbbdev 
3449e08aacStbbdev #include "oneapi/tbb/detail/_utils.h"
3549e08aacStbbdev #include "oneapi/tbb/enumerable_thread_specific.h"
3649e08aacStbbdev #include "oneapi/tbb/parallel_for.h"
3749e08aacStbbdev #include "oneapi/tbb/parallel_reduce.h"
3849e08aacStbbdev #include "oneapi/tbb/parallel_invoke.h"
3949e08aacStbbdev #include "oneapi/tbb/blocked_range.h"
4049e08aacStbbdev #include "oneapi/tbb/tbb_allocator.h"
4149e08aacStbbdev #include "oneapi/tbb/global_control.h"
4249e08aacStbbdev #include "oneapi/tbb/cache_aligned_allocator.h"
4351c0b2f7Stbbdev 
4451c0b2f7Stbbdev #include <cstring>
4551c0b2f7Stbbdev #include <cstdio>
4651c0b2f7Stbbdev #include <vector>
4751c0b2f7Stbbdev #include <numeric>
4851c0b2f7Stbbdev #include <utility>
4951c0b2f7Stbbdev #include <atomic>
5051c0b2f7Stbbdev 
5151c0b2f7Stbbdev //! \file conformance_enumerable_thread_specific.cpp
5251c0b2f7Stbbdev //! \brief Test for [tls.enumerable_thread_specific tls.flattened2d] specification
5351c0b2f7Stbbdev 
5451c0b2f7Stbbdev //------------------------------------------------------------------------------------------------------
5551c0b2f7Stbbdev // Utility types/classes/functions
5651c0b2f7Stbbdev //------------------------------------------------------------------------------------------------------
5751c0b2f7Stbbdev 
5851c0b2f7Stbbdev //! Minimum number of threads
5951c0b2f7Stbbdev static int MinThread = 1;
6051c0b2f7Stbbdev 
6151c0b2f7Stbbdev //! Maximum number of threads
6251c0b2f7Stbbdev static int MaxThread = 4;
6351c0b2f7Stbbdev 
6451c0b2f7Stbbdev static std::atomic<int> construction_counter;
6551c0b2f7Stbbdev static std::atomic<int> destruction_counter;
6651c0b2f7Stbbdev 
67b15aabb3Stbbdev const int REPETITIONS = 5;
68b15aabb3Stbbdev const int N = 25000;
69b15aabb3Stbbdev const int RANGE_MIN = 5000;
7051c0b2f7Stbbdev const double EXPECTED_SUM = (REPETITIONS + 1) * N;
7151c0b2f7Stbbdev 
7251c0b2f7Stbbdev //! A minimal class that occupies N bytes.
7351c0b2f7Stbbdev /** Defines default and copy constructor, and allows implicit operator&. Hides operator=. */
7449e08aacStbbdev template<size_t N = oneapi::tbb::detail::max_nfs_size>
75*90b0671bSAlex class minimalNComparable: utils::NoAssign {
7651c0b2f7Stbbdev private:
7751c0b2f7Stbbdev     int my_value;
7851c0b2f7Stbbdev     bool is_constructed;
7951c0b2f7Stbbdev     char pad[N-sizeof(int) - sizeof(bool)];
8051c0b2f7Stbbdev public:
minimalNComparable()81*90b0671bSAlex     minimalNComparable() : utils::NoAssign(), my_value(0) { ++construction_counter; is_constructed = true; }
minimalNComparable(const minimalNComparable & m)82*90b0671bSAlex     minimalNComparable( const minimalNComparable &m ) : utils::NoAssign(), my_value(m.my_value) { ++construction_counter; is_constructed = true; }
~minimalNComparable()83*90b0671bSAlex     ~minimalNComparable() { ++destruction_counter; CHECK_FAST(is_constructed); is_constructed = false; }
set_value(const int i)84b15aabb3Stbbdev     void set_value( const int i ) { CHECK_FAST(is_constructed); my_value = i; }
value() const85b15aabb3Stbbdev     int value( ) const { CHECK_FAST(is_constructed); return my_value; }
8651c0b2f7Stbbdev 
operator ==(const minimalNComparable & other) const87*90b0671bSAlex     bool operator==( const minimalNComparable& other ) const { return my_value == other.my_value; }
8851c0b2f7Stbbdev };
8951c0b2f7Stbbdev 
9051c0b2f7Stbbdev static size_t AlignMask = 0;  // set to cache-line-size - 1
9151c0b2f7Stbbdev 
9251c0b2f7Stbbdev template<typename T>
check_alignment(T & t,const char * aname)9351c0b2f7Stbbdev T& check_alignment(T& t, const char *aname) {
9449e08aacStbbdev     if( !oneapi::tbb::detail::is_aligned(&t, AlignMask)) {
9551c0b2f7Stbbdev         // TBB_REVAMP_TODO: previously was REPORT_ONCE
9651c0b2f7Stbbdev         REPORT("alignment error with %s allocator (%x)\n", aname, (int)size_t(&t) & (AlignMask-1));
9751c0b2f7Stbbdev     }
9851c0b2f7Stbbdev     return t;
9951c0b2f7Stbbdev }
10051c0b2f7Stbbdev 
10151c0b2f7Stbbdev template<typename T>
check_alignment(const T & t,const char * aname)10251c0b2f7Stbbdev const T& check_alignment(const T& t, const char *aname) {
10349e08aacStbbdev     if( !oneapi::tbb::detail::is_aligned(&t, AlignMask)) {
10451c0b2f7Stbbdev         // TBB_REVAMP_TODO: previously was REPORT_ONCE
10551c0b2f7Stbbdev         REPORT("alignment error with %s allocator (%x)\n", aname, (int)size_t(&t) & (AlignMask-1));
10651c0b2f7Stbbdev     }
10751c0b2f7Stbbdev     return t;
10851c0b2f7Stbbdev }
10951c0b2f7Stbbdev 
11051c0b2f7Stbbdev // Test constructors which throw.  If an ETS constructor throws before completion,
11151c0b2f7Stbbdev // the already-built objects are un-constructed.  Do not call the destructor if
11251c0b2f7Stbbdev // this occurs.
11351c0b2f7Stbbdev 
11451c0b2f7Stbbdev static std::atomic<int> gThrowValue;
11551c0b2f7Stbbdev static int targetThrowValue = 3;
11651c0b2f7Stbbdev 
11751c0b2f7Stbbdev class Thrower {
11851c0b2f7Stbbdev public:
Thrower()11951c0b2f7Stbbdev     Thrower() {
12051c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
12151c0b2f7Stbbdev         if(++gThrowValue == targetThrowValue) {
12251c0b2f7Stbbdev             throw std::bad_alloc();
12351c0b2f7Stbbdev         }
12451c0b2f7Stbbdev #endif
12551c0b2f7Stbbdev     }
12651c0b2f7Stbbdev };
12751c0b2f7Stbbdev 
12851c0b2f7Stbbdev // MyThrower field of ThrowingConstructor will throw after a certain number of
12951c0b2f7Stbbdev // construction calls.  The constructor unwinder wshould unconstruct the instance
13051c0b2f7Stbbdev // of check_type<int> that was constructed just before.
13151c0b2f7Stbbdev class ThrowingConstructor {
13251c0b2f7Stbbdev     CheckType<int> m_checktype;
13351c0b2f7Stbbdev     Thrower m_throwing_field;
13451c0b2f7Stbbdev public:
13551c0b2f7Stbbdev     int m_cnt;
ThrowingConstructor()13651c0b2f7Stbbdev     ThrowingConstructor() : m_checktype(), m_throwing_field() { m_cnt = 0;}
13751c0b2f7Stbbdev 
operator ==(const ThrowingConstructor & other) const138b15aabb3Stbbdev     bool operator==( const ThrowingConstructor& other ) const { return m_cnt == other.m_cnt; }
13951c0b2f7Stbbdev private:
14051c0b2f7Stbbdev };
14151c0b2f7Stbbdev 
14251c0b2f7Stbbdev //
143*90b0671bSAlex // A helper class that simplifies writing the tests since minimalNComparable does not
14451c0b2f7Stbbdev // define = or + operators.
14551c0b2f7Stbbdev //
14651c0b2f7Stbbdev 
14751c0b2f7Stbbdev template< typename T >
14851c0b2f7Stbbdev struct test_helper {
inittest_helper14951c0b2f7Stbbdev    static inline void init(T &e) { e = static_cast<T>(0); }
sumtest_helper15051c0b2f7Stbbdev    static inline void sum(T &e, const int addend ) { e += static_cast<T>(addend); }
sumtest_helper15151c0b2f7Stbbdev    static inline void sum(T &e, const double addend ) { e += static_cast<T>(addend); }
settest_helper15251c0b2f7Stbbdev    static inline void set(T &e, const int value ) { e = static_cast<T>(value); }
gettest_helper15351c0b2f7Stbbdev    static inline double get(const T &e ) { return static_cast<double>(e); }
15451c0b2f7Stbbdev };
15551c0b2f7Stbbdev 
15651c0b2f7Stbbdev template<size_t N>
157*90b0671bSAlex struct test_helper<minimalNComparable<N> > {
inittest_helper158*90b0671bSAlex    static inline void init(minimalNComparable<N> &sum) { sum.set_value( 0 ); }
sumtest_helper159*90b0671bSAlex    static inline void sum(minimalNComparable<N> &sum, const int addend ) { sum.set_value( sum.value() + addend); }
sumtest_helper160*90b0671bSAlex    static inline void sum(minimalNComparable<N> &sum, const double addend ) { sum.set_value( sum.value() + static_cast<int>(addend)); }
sumtest_helper161*90b0671bSAlex    static inline void sum(minimalNComparable<N> &sum, const minimalNComparable<N> &addend ) { sum.set_value( sum.value() + addend.value()); }
settest_helper162*90b0671bSAlex    static inline void set(minimalNComparable<N> &v, const int value ) { v.set_value( static_cast<int>(value) ); }
gettest_helper163*90b0671bSAlex    static inline double get(const minimalNComparable<N> &sum ) { return static_cast<double>(sum.value()); }
16451c0b2f7Stbbdev };
16551c0b2f7Stbbdev 
16651c0b2f7Stbbdev template<>
16751c0b2f7Stbbdev struct test_helper<ThrowingConstructor> {
inittest_helper16851c0b2f7Stbbdev    static inline void init(ThrowingConstructor &sum) { sum.m_cnt = 0; }
sumtest_helper16951c0b2f7Stbbdev    static inline void sum(ThrowingConstructor &sum, const int addend ) { sum.m_cnt += addend; }
sumtest_helper17051c0b2f7Stbbdev    static inline void sum(ThrowingConstructor &sum, const double addend ) { sum.m_cnt += static_cast<int>(addend); }
sumtest_helper17151c0b2f7Stbbdev    static inline void sum(ThrowingConstructor &sum, const ThrowingConstructor &addend ) { sum.m_cnt += addend.m_cnt; }
settest_helper17251c0b2f7Stbbdev    static inline void set(ThrowingConstructor &v, const int value ) { v.m_cnt = static_cast<int>(value); }
gettest_helper17351c0b2f7Stbbdev    static inline double get(const ThrowingConstructor &sum ) { return static_cast<double>(sum.m_cnt); }
17451c0b2f7Stbbdev };
17551c0b2f7Stbbdev 
17651c0b2f7Stbbdev //! Tag class used to make certain constructors hard to invoke accidentally.
17751c0b2f7Stbbdev struct SecretTagType {} SecretTag;
17851c0b2f7Stbbdev 
17951c0b2f7Stbbdev //// functors and routines for initialization and combine
18051c0b2f7Stbbdev 
18151c0b2f7Stbbdev //! Counts instances of FunctorFinit
18251c0b2f7Stbbdev static std::atomic<int> FinitCounter;
18351c0b2f7Stbbdev 
18451c0b2f7Stbbdev template <typename T, int Value>
18551c0b2f7Stbbdev struct FunctorFinit {
FunctorFinitFunctorFinit18651c0b2f7Stbbdev     FunctorFinit( const FunctorFinit& ) {++FinitCounter;}
FunctorFinitFunctorFinit18751c0b2f7Stbbdev     FunctorFinit( SecretTagType ) {++FinitCounter;}
~FunctorFinitFunctorFinit18851c0b2f7Stbbdev     ~FunctorFinit() {--FinitCounter;}
operator ()FunctorFinit18951c0b2f7Stbbdev     T operator()() { return Value; }
19051c0b2f7Stbbdev };
19151c0b2f7Stbbdev 
19251c0b2f7Stbbdev template <int Value>
19351c0b2f7Stbbdev struct FunctorFinit<ThrowingConstructor,Value> {
FunctorFinitFunctorFinit19451c0b2f7Stbbdev     FunctorFinit( const FunctorFinit& ) {++FinitCounter;}
FunctorFinitFunctorFinit19551c0b2f7Stbbdev     FunctorFinit( SecretTagType ) {++FinitCounter;}
~FunctorFinitFunctorFinit19651c0b2f7Stbbdev     ~FunctorFinit() {--FinitCounter;}
operator ()FunctorFinit19751c0b2f7Stbbdev     ThrowingConstructor operator()() { ThrowingConstructor temp; temp.m_cnt = Value; return temp; }
19851c0b2f7Stbbdev };
19951c0b2f7Stbbdev 
20051c0b2f7Stbbdev template <size_t N, int Value>
201*90b0671bSAlex struct FunctorFinit<minimalNComparable<N>,Value> {
FunctorFinitFunctorFinit20251c0b2f7Stbbdev     FunctorFinit( const FunctorFinit& ) {++FinitCounter;}
FunctorFinitFunctorFinit20351c0b2f7Stbbdev     FunctorFinit( SecretTagType ) {++FinitCounter;}
~FunctorFinitFunctorFinit20451c0b2f7Stbbdev     ~FunctorFinit() {--FinitCounter;}
operator ()FunctorFinit205*90b0671bSAlex     minimalNComparable<N> operator()() {
206*90b0671bSAlex         minimalNComparable<N> result;
20751c0b2f7Stbbdev         result.set_value( Value );
20851c0b2f7Stbbdev         return result;
20951c0b2f7Stbbdev     }
21051c0b2f7Stbbdev };
21151c0b2f7Stbbdev 
21251c0b2f7Stbbdev // Addition
21351c0b2f7Stbbdev 
21451c0b2f7Stbbdev template <typename T>
21551c0b2f7Stbbdev struct FunctorAddCombineRef {
operator ()FunctorAddCombineRef21651c0b2f7Stbbdev     T operator()(const T& left, const T& right) const {
21751c0b2f7Stbbdev         return left+right;
21851c0b2f7Stbbdev     }
21951c0b2f7Stbbdev };
22051c0b2f7Stbbdev 
22151c0b2f7Stbbdev template <size_t N>
222*90b0671bSAlex struct FunctorAddCombineRef<minimalNComparable<N> > {
operator ()FunctorAddCombineRef223*90b0671bSAlex     minimalNComparable<N> operator()(const minimalNComparable<N>& left, const minimalNComparable<N>& right) const {
224*90b0671bSAlex         minimalNComparable<N> result;
22551c0b2f7Stbbdev         result.set_value( left.value() + right.value() );
22651c0b2f7Stbbdev         return result;
22751c0b2f7Stbbdev     }
22851c0b2f7Stbbdev };
22951c0b2f7Stbbdev 
23051c0b2f7Stbbdev template <>
23151c0b2f7Stbbdev struct FunctorAddCombineRef<ThrowingConstructor> {
operator ()FunctorAddCombineRef23251c0b2f7Stbbdev     ThrowingConstructor operator()(const ThrowingConstructor& left, const ThrowingConstructor& right) const {
23351c0b2f7Stbbdev         ThrowingConstructor result;
23451c0b2f7Stbbdev         result.m_cnt = ( left.m_cnt + right.m_cnt );
23551c0b2f7Stbbdev         return result;
23651c0b2f7Stbbdev     }
23751c0b2f7Stbbdev };
23851c0b2f7Stbbdev 
23951c0b2f7Stbbdev template <typename T>
24051c0b2f7Stbbdev struct FunctorAddCombine {
operator ()FunctorAddCombine24151c0b2f7Stbbdev     T operator()(T left, T right ) const {
24251c0b2f7Stbbdev         return FunctorAddCombineRef<T>()( left, right );
24351c0b2f7Stbbdev     }
24451c0b2f7Stbbdev };
24551c0b2f7Stbbdev 
24651c0b2f7Stbbdev template <typename T>
FunctionAddByRef(const T & left,const T & right)24751c0b2f7Stbbdev T FunctionAddByRef( const T &left, const T &right) {
24851c0b2f7Stbbdev     return FunctorAddCombineRef<T>()( left, right );
24951c0b2f7Stbbdev }
25051c0b2f7Stbbdev 
25151c0b2f7Stbbdev template <typename T>
FunctionAdd(T left,T right)25251c0b2f7Stbbdev T FunctionAdd( T left, T right) { return FunctionAddByRef(left,right); }
25351c0b2f7Stbbdev 
25451c0b2f7Stbbdev template <typename T>
25551c0b2f7Stbbdev class Accumulator {
25651c0b2f7Stbbdev public:
Accumulator(T & result)25751c0b2f7Stbbdev     Accumulator(T& result) : my_result(result) {}
Accumulator(const Accumulator & other)25851c0b2f7Stbbdev     Accumulator(const Accumulator& other) : my_result(other.my_result) {}
operator =(const Accumulator & other)25951c0b2f7Stbbdev     Accumulator& operator=(const Accumulator& other) {
26051c0b2f7Stbbdev         test_helper<T>::set(my_result, test_helper<T>::get(other));
26151c0b2f7Stbbdev         return *this;
26251c0b2f7Stbbdev     }
operator ()(const T & new_bit)26351c0b2f7Stbbdev     void operator()(const T& new_bit) { test_helper<T>::sum(my_result, new_bit); }
26451c0b2f7Stbbdev private:
26551c0b2f7Stbbdev     T& my_result;
26651c0b2f7Stbbdev };
26751c0b2f7Stbbdev 
26851c0b2f7Stbbdev template <typename T>
26951c0b2f7Stbbdev class ClearingAccumulator {
27051c0b2f7Stbbdev public:
ClearingAccumulator(T & result)27151c0b2f7Stbbdev     ClearingAccumulator(T& result) : my_result(result) {}
ClearingAccumulator(const ClearingAccumulator & other)27251c0b2f7Stbbdev     ClearingAccumulator(const ClearingAccumulator& other) : my_result(other.my_result) {}
operator =(const ClearingAccumulator & other)27351c0b2f7Stbbdev     ClearingAccumulator& operator=(const ClearingAccumulator& other) {
27451c0b2f7Stbbdev         test_helper<T>::set(my_result, test_helper<T>::get(other));
27551c0b2f7Stbbdev         return *this;
27651c0b2f7Stbbdev     }
operator ()(T & new_bit)27751c0b2f7Stbbdev     void operator()(T& new_bit) {
27851c0b2f7Stbbdev         test_helper<T>::sum(my_result, new_bit);
27951c0b2f7Stbbdev         test_helper<T>::init(new_bit);
28051c0b2f7Stbbdev     }
AssertClean(const T & thread_local_value)28151c0b2f7Stbbdev     static void AssertClean(const T& thread_local_value) {
28251c0b2f7Stbbdev         T zero;
28351c0b2f7Stbbdev         test_helper<T>::init(zero);
28451c0b2f7Stbbdev         REQUIRE_MESSAGE(test_helper<T>::get(thread_local_value)==test_helper<T>::get(zero),
28551c0b2f7Stbbdev                "combine_each does not allow to modify thread local values?");
28651c0b2f7Stbbdev     }
28751c0b2f7Stbbdev private:
28851c0b2f7Stbbdev     T& my_result;
28951c0b2f7Stbbdev };
29051c0b2f7Stbbdev 
29151c0b2f7Stbbdev //// end functors and routines
29251c0b2f7Stbbdev 
29351c0b2f7Stbbdev //------------------------------------------------------------------------------------------------------
29451c0b2f7Stbbdev // Tests for tests cases
29551c0b2f7Stbbdev //------------------------------------------------------------------------------------------------------
29651c0b2f7Stbbdev 
29751c0b2f7Stbbdev template <typename T, template<class> class Allocator>
29851c0b2f7Stbbdev class parallel_scalar_body: utils::NoAssign {
29949e08aacStbbdev     typedef oneapi::tbb::enumerable_thread_specific<T, Allocator<T> > ets_type;
30051c0b2f7Stbbdev     ets_type &sums;
30151c0b2f7Stbbdev     const char* allocator_name;
30251c0b2f7Stbbdev 
30351c0b2f7Stbbdev public:
30451c0b2f7Stbbdev 
parallel_scalar_body(ets_type & _sums,const char * alloc_name)30551c0b2f7Stbbdev     parallel_scalar_body ( ets_type &_sums, const char *alloc_name ) : sums(_sums), allocator_name(alloc_name) { }
30651c0b2f7Stbbdev 
operator ()(const oneapi::tbb::blocked_range<int> & r) const30749e08aacStbbdev     void operator()( const oneapi::tbb::blocked_range<int> &r ) const {
30851c0b2f7Stbbdev         for (int i = r.begin(); i != r.end(); ++i)
30951c0b2f7Stbbdev             test_helper<T>::sum( check_alignment(sums.local(),allocator_name), 1 );
31051c0b2f7Stbbdev     }
31151c0b2f7Stbbdev 
31251c0b2f7Stbbdev };
31351c0b2f7Stbbdev 
31451c0b2f7Stbbdev template< typename T, template<class> class Allocator>
run_parallel_scalar_tests_nocombine(const char *,const char * allocator_name)31551c0b2f7Stbbdev void run_parallel_scalar_tests_nocombine(const char* /* test_name */, const char *allocator_name) {
31651c0b2f7Stbbdev 
31749e08aacStbbdev     typedef oneapi::tbb::enumerable_thread_specific<T, Allocator<T> > ets_type;
31851c0b2f7Stbbdev 
31951c0b2f7Stbbdev     Checker<T> my_check;
320b15aabb3Stbbdev 
321b15aabb3Stbbdev     gThrowValue = 0;
322b15aabb3Stbbdev     struct fail_on_exception_guard {
323b15aabb3Stbbdev         bool dismiss = false;
324b15aabb3Stbbdev         ~fail_on_exception_guard() {
325b15aabb3Stbbdev             if (!dismiss) {
326b15aabb3Stbbdev                 FAIL("The exception is not expected");
327b15aabb3Stbbdev             }
328b15aabb3Stbbdev         }
329b15aabb3Stbbdev     } guard;
330b15aabb3Stbbdev     T default_value{};
331b15aabb3Stbbdev     guard.dismiss = true;
332b15aabb3Stbbdev 
33351c0b2f7Stbbdev     gThrowValue = 0;
33451c0b2f7Stbbdev     {
33551c0b2f7Stbbdev         // We assume that static_sums zero-initialized or has a default constructor that zeros it.
33651c0b2f7Stbbdev         ets_type static_sums = ets_type( T() );
33751c0b2f7Stbbdev 
33851c0b2f7Stbbdev         T exemplar;
33951c0b2f7Stbbdev         test_helper<T>::init(exemplar);
34051c0b2f7Stbbdev 
341b15aabb3Stbbdev         for (int p = std::max(MinThread, 2); p <= MaxThread; ++p) {
34249e08aacStbbdev             oneapi::tbb::global_control gc(oneapi::tbb::global_control::max_allowed_parallelism, p);
34351c0b2f7Stbbdev 
34451c0b2f7Stbbdev             T iterator_sum;
34551c0b2f7Stbbdev             test_helper<T>::init(iterator_sum);
34651c0b2f7Stbbdev 
34751c0b2f7Stbbdev             T finit_ets_sum;
34851c0b2f7Stbbdev             test_helper<T>::init(finit_ets_sum);
34951c0b2f7Stbbdev 
35051c0b2f7Stbbdev             T const_iterator_sum;
35151c0b2f7Stbbdev             test_helper<T>::init(const_iterator_sum);
35251c0b2f7Stbbdev 
35351c0b2f7Stbbdev             T range_sum;
35451c0b2f7Stbbdev             test_helper<T>::init(range_sum);
35551c0b2f7Stbbdev 
35651c0b2f7Stbbdev             T const_range_sum;
35751c0b2f7Stbbdev             test_helper<T>::init(const_range_sum);
35851c0b2f7Stbbdev 
35951c0b2f7Stbbdev             T cconst_sum;
36051c0b2f7Stbbdev             test_helper<T>::init(cconst_sum);
36151c0b2f7Stbbdev 
36251c0b2f7Stbbdev             T assign_sum;
36351c0b2f7Stbbdev             test_helper<T>::init(assign_sum);
36451c0b2f7Stbbdev 
36551c0b2f7Stbbdev             T cassgn_sum;
36651c0b2f7Stbbdev             test_helper<T>::init(cassgn_sum);
36751c0b2f7Stbbdev             T non_cassgn_sum;
36851c0b2f7Stbbdev             test_helper<T>::init(non_cassgn_sum);
36951c0b2f7Stbbdev 
37051c0b2f7Stbbdev             T static_sum;
37151c0b2f7Stbbdev             test_helper<T>::init(static_sum);
37251c0b2f7Stbbdev 
37351c0b2f7Stbbdev             for (int t = -1; t < REPETITIONS; ++t) {
37451c0b2f7Stbbdev                 static_sums.clear();
37551c0b2f7Stbbdev 
37651c0b2f7Stbbdev                 ets_type sums(exemplar);
37751c0b2f7Stbbdev                 FunctorFinit<T,0> my_finit(SecretTag);
37851c0b2f7Stbbdev                 ets_type finit_ets(my_finit);
37951c0b2f7Stbbdev 
38051c0b2f7Stbbdev                 REQUIRE( sums.empty());
381b15aabb3Stbbdev                 oneapi::tbb::parallel_for( oneapi::tbb::blocked_range<int>( 0, N*p, RANGE_MIN ), parallel_scalar_body<T,Allocator>( sums, allocator_name ) );
38251c0b2f7Stbbdev                 REQUIRE( !sums.empty());
38351c0b2f7Stbbdev 
38451c0b2f7Stbbdev                 REQUIRE( finit_ets.empty());
385b15aabb3Stbbdev                 oneapi::tbb::parallel_for( oneapi::tbb::blocked_range<int>( 0, N*p, RANGE_MIN ), parallel_scalar_body<T,Allocator>( finit_ets, allocator_name ) );
38651c0b2f7Stbbdev                 REQUIRE( !finit_ets.empty());
38751c0b2f7Stbbdev 
38851c0b2f7Stbbdev                 REQUIRE(static_sums.empty());
389b15aabb3Stbbdev                 oneapi::tbb::parallel_for( oneapi::tbb::blocked_range<int>( 0, N*p, RANGE_MIN ), parallel_scalar_body<T,Allocator>( static_sums, allocator_name ) );
39051c0b2f7Stbbdev                 REQUIRE( !static_sums.empty());
39151c0b2f7Stbbdev 
39251c0b2f7Stbbdev                 // use iterator
39351c0b2f7Stbbdev                 typename ets_type::size_type size = 0;
39451c0b2f7Stbbdev                 for ( typename ets_type::iterator i = sums.begin(); i != sums.end(); ++i ) {
39551c0b2f7Stbbdev                      ++size;
39651c0b2f7Stbbdev                      test_helper<T>::sum(iterator_sum, *i);
39751c0b2f7Stbbdev                 }
39851c0b2f7Stbbdev                 REQUIRE( sums.size() == size);
39951c0b2f7Stbbdev 
40051c0b2f7Stbbdev                 // use const_iterator
40151c0b2f7Stbbdev                 for ( typename ets_type::const_iterator i = sums.begin(); i != sums.end(); ++i ) {
40251c0b2f7Stbbdev                      test_helper<T>::sum(const_iterator_sum, *i);
40351c0b2f7Stbbdev                 }
40451c0b2f7Stbbdev 
40551c0b2f7Stbbdev                 // use range_type
40651c0b2f7Stbbdev                 typename ets_type::range_type r = sums.range();
40751c0b2f7Stbbdev                 for ( typename ets_type::range_type::const_iterator i = r.begin(); i != r.end(); ++i ) {
40851c0b2f7Stbbdev                      test_helper<T>::sum(range_sum, *i);
40951c0b2f7Stbbdev                 }
41051c0b2f7Stbbdev 
41151c0b2f7Stbbdev                 // use const_range_type
41251c0b2f7Stbbdev                 const ets_type& csums = sums;
41351c0b2f7Stbbdev                 typename ets_type::const_range_type cr = csums.range();
41451c0b2f7Stbbdev                 for ( typename ets_type::const_range_type::iterator i = cr.begin(); i != cr.end(); ++i ) {
41551c0b2f7Stbbdev                      test_helper<T>::sum(const_range_sum, *i);
41651c0b2f7Stbbdev                 }
41751c0b2f7Stbbdev 
41851c0b2f7Stbbdev                 // test copy constructor, with TLS-cached locals
41949e08aacStbbdev                 typedef typename oneapi::tbb::enumerable_thread_specific<T, Allocator<T>, oneapi::tbb::ets_key_per_instance> cached_ets_type;
42051c0b2f7Stbbdev 
42151c0b2f7Stbbdev                 cached_ets_type cconst(sums);
422b15aabb3Stbbdev                 oneapi::tbb::parallel_for( oneapi::tbb::blocked_range<int>(0, N*p, RANGE_MIN), [&]( const oneapi::tbb::blocked_range<int>& ) {
42351c0b2f7Stbbdev                     bool exists = false;
42451c0b2f7Stbbdev                     T& ref = cconst.local(exists);
425b15aabb3Stbbdev                     CHECK( (exists || ref == default_value) );
42651c0b2f7Stbbdev                 } );
42751c0b2f7Stbbdev                 cached_ets_type cconst_to_assign1 = cconst;
42851c0b2f7Stbbdev                 cached_ets_type cconst_to_assign2;
42951c0b2f7Stbbdev                 cconst_to_assign2 = std::move(cconst_to_assign1);
43051c0b2f7Stbbdev                 REQUIRE(cconst_to_assign2.size() == cconst.size());
43151c0b2f7Stbbdev 
43251c0b2f7Stbbdev                 for ( typename cached_ets_type::const_iterator i = cconst.begin(); i != cconst.end(); ++i ) {
43351c0b2f7Stbbdev                      test_helper<T>::sum(cconst_sum, *i);
43451c0b2f7Stbbdev                 }
43551c0b2f7Stbbdev 
43651c0b2f7Stbbdev                 // test assignment
43751c0b2f7Stbbdev                 ets_type assigned;
43851c0b2f7Stbbdev                 assigned = sums;
43951c0b2f7Stbbdev 
44051c0b2f7Stbbdev                 for ( typename ets_type::const_iterator i = assigned.begin(); i != assigned.end(); ++i ) {
44151c0b2f7Stbbdev                      test_helper<T>::sum(assign_sum, *i);
44251c0b2f7Stbbdev                 }
44351c0b2f7Stbbdev 
44451c0b2f7Stbbdev                 // test assign to and from cached locals
44551c0b2f7Stbbdev                 cached_ets_type cassgn;
44651c0b2f7Stbbdev                 cassgn = sums;
44751c0b2f7Stbbdev                 for ( typename cached_ets_type::const_iterator i = cassgn.begin(); i != cassgn.end(); ++i ) {
44851c0b2f7Stbbdev                      test_helper<T>::sum(cassgn_sum, *i);
44951c0b2f7Stbbdev                 }
45051c0b2f7Stbbdev 
45151c0b2f7Stbbdev                 ets_type non_cassgn;
45251c0b2f7Stbbdev                 non_cassgn = cassgn;
45351c0b2f7Stbbdev                 for ( typename ets_type::const_iterator i = non_cassgn.begin(); i != non_cassgn.end(); ++i ) {
45451c0b2f7Stbbdev                      test_helper<T>::sum(non_cassgn_sum, *i);
45551c0b2f7Stbbdev                 }
45651c0b2f7Stbbdev 
45751c0b2f7Stbbdev                 // test finit-initialized ets
45851c0b2f7Stbbdev                 for(typename ets_type::const_iterator i = finit_ets.begin(); i != finit_ets.end(); ++i) {
45951c0b2f7Stbbdev                     test_helper<T>::sum(finit_ets_sum, *i);
46051c0b2f7Stbbdev                 }
46151c0b2f7Stbbdev 
46251c0b2f7Stbbdev                 // test static ets
46351c0b2f7Stbbdev                 for(typename ets_type::const_iterator i = static_sums.begin(); i != static_sums.end(); ++i) {
46451c0b2f7Stbbdev                     test_helper<T>::sum(static_sum, *i);
46551c0b2f7Stbbdev                 }
46651c0b2f7Stbbdev 
46751c0b2f7Stbbdev             }
46851c0b2f7Stbbdev 
469b15aabb3Stbbdev             REQUIRE(EXPECTED_SUM*p == test_helper<T>::get(iterator_sum));
470b15aabb3Stbbdev             REQUIRE(EXPECTED_SUM*p == test_helper<T>::get(const_iterator_sum));
471b15aabb3Stbbdev             REQUIRE(EXPECTED_SUM*p == test_helper<T>::get(range_sum));
472b15aabb3Stbbdev             REQUIRE(EXPECTED_SUM*p == test_helper<T>::get(const_range_sum));
47351c0b2f7Stbbdev 
474b15aabb3Stbbdev             REQUIRE(EXPECTED_SUM*p == test_helper<T>::get(cconst_sum));
475b15aabb3Stbbdev             REQUIRE(EXPECTED_SUM*p == test_helper<T>::get(assign_sum));
476b15aabb3Stbbdev             REQUIRE(EXPECTED_SUM*p == test_helper<T>::get(cassgn_sum));
477b15aabb3Stbbdev             REQUIRE(EXPECTED_SUM*p == test_helper<T>::get(non_cassgn_sum));
478b15aabb3Stbbdev             REQUIRE(EXPECTED_SUM*p == test_helper<T>::get(finit_ets_sum));
479b15aabb3Stbbdev             REQUIRE(EXPECTED_SUM*p == test_helper<T>::get(static_sum));
48051c0b2f7Stbbdev         }
48151c0b2f7Stbbdev     }  // Checker block
48251c0b2f7Stbbdev }
48351c0b2f7Stbbdev 
48451c0b2f7Stbbdev template< typename T, template<class> class Allocator>
run_parallel_scalar_tests(const char * test_name,const char * allocator_name)48551c0b2f7Stbbdev void run_parallel_scalar_tests(const char* test_name, const char* allocator_name) {
48651c0b2f7Stbbdev 
48749e08aacStbbdev     typedef oneapi::tbb::enumerable_thread_specific<T, Allocator<T> > ets_type;
48851c0b2f7Stbbdev     bool exception_caught = false;
48951c0b2f7Stbbdev 
49051c0b2f7Stbbdev     // We assume that static_sums zero-initialized or has a default constructor that zeros it.
49151c0b2f7Stbbdev     ets_type static_sums = ets_type( T() );
49251c0b2f7Stbbdev 
49351c0b2f7Stbbdev     T exemplar;
49451c0b2f7Stbbdev     test_helper<T>::init(exemplar);
49551c0b2f7Stbbdev 
49651c0b2f7Stbbdev     int test_throw_count = 10;
49751c0b2f7Stbbdev     // the test will be performed repeatedly until it does not throw.  For non-throwing types
49851c0b2f7Stbbdev     // this means once; for the throwing type test it may loop two or three times.  The
49951c0b2f7Stbbdev     // value of targetThrowValue will determine when and if the test will throw.
50051c0b2f7Stbbdev     do {
50151c0b2f7Stbbdev         targetThrowValue = test_throw_count;  // keep testing until we get no exception
50251c0b2f7Stbbdev         exception_caught = false;
50351c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
50451c0b2f7Stbbdev         try {
50551c0b2f7Stbbdev #endif
50651c0b2f7Stbbdev             run_parallel_scalar_tests_nocombine<T,Allocator>(test_name, allocator_name);
50751c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
50851c0b2f7Stbbdev         }
509b15aabb3Stbbdev         catch(...) {}
51051c0b2f7Stbbdev #endif
511b15aabb3Stbbdev         for (int p = std::max(MinThread, 2); p <= MaxThread; ++p) {
51249e08aacStbbdev             oneapi::tbb::global_control gc(oneapi::tbb::global_control::max_allowed_parallelism, p);
51351c0b2f7Stbbdev 
51451c0b2f7Stbbdev             gThrowValue = 0;
51551c0b2f7Stbbdev 
51651c0b2f7Stbbdev             T combine_sum;
51751c0b2f7Stbbdev             test_helper<T>::init(combine_sum);
51851c0b2f7Stbbdev 
51951c0b2f7Stbbdev             T combine_ref_sum;
52051c0b2f7Stbbdev             test_helper<T>::init(combine_ref_sum);
52151c0b2f7Stbbdev 
52251c0b2f7Stbbdev             T accumulator_sum;
52351c0b2f7Stbbdev             test_helper<T>::init(accumulator_sum);
52451c0b2f7Stbbdev 
52551c0b2f7Stbbdev             T static_sum;
52651c0b2f7Stbbdev             test_helper<T>::init(static_sum);
52751c0b2f7Stbbdev 
52851c0b2f7Stbbdev             T clearing_accumulator_sum;
52951c0b2f7Stbbdev             test_helper<T>::init(clearing_accumulator_sum);
53051c0b2f7Stbbdev 
53151c0b2f7Stbbdev             {
53251c0b2f7Stbbdev                 Checker<T> my_check;
53351c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
53451c0b2f7Stbbdev                 try
53551c0b2f7Stbbdev #endif
53651c0b2f7Stbbdev                 {
53751c0b2f7Stbbdev                     for (int t = -1; t < REPETITIONS; ++t) {
53851c0b2f7Stbbdev                         static_sums.clear();
53951c0b2f7Stbbdev 
54051c0b2f7Stbbdev                         ets_type sums(exemplar);
54151c0b2f7Stbbdev 
54251c0b2f7Stbbdev                         REQUIRE(sums.empty());
543b15aabb3Stbbdev                         oneapi::tbb::parallel_for(oneapi::tbb::blocked_range<int>(0, N * p, RANGE_MIN),
54451c0b2f7Stbbdev                             parallel_scalar_body<T, Allocator>(sums, allocator_name));
54551c0b2f7Stbbdev                         REQUIRE(!sums.empty());
54651c0b2f7Stbbdev 
54751c0b2f7Stbbdev                         REQUIRE(static_sums.empty());
548b15aabb3Stbbdev                         oneapi::tbb::parallel_for(oneapi::tbb::blocked_range<int>(0, N * p, RANGE_MIN),
54951c0b2f7Stbbdev                             parallel_scalar_body<T, Allocator>(static_sums, allocator_name));
55051c0b2f7Stbbdev                         REQUIRE(!static_sums.empty());
55151c0b2f7Stbbdev 
55251c0b2f7Stbbdev                         // Use combine
55351c0b2f7Stbbdev                         test_helper<T>::sum(combine_sum, sums.combine(FunctionAdd<T>));
55451c0b2f7Stbbdev                         test_helper<T>::sum(combine_ref_sum, sums.combine(FunctionAddByRef<T>));
55551c0b2f7Stbbdev                         test_helper<T>::sum(static_sum, static_sums.combine(FunctionAdd<T>));
55651c0b2f7Stbbdev 
55751c0b2f7Stbbdev                         // Accumulate with combine_each
55851c0b2f7Stbbdev                         sums.combine_each(Accumulator<T>(accumulator_sum));
55951c0b2f7Stbbdev                         // Accumulate and clear thread-local values
56051c0b2f7Stbbdev                         sums.combine_each(ClearingAccumulator<T>(clearing_accumulator_sum));
56151c0b2f7Stbbdev                         // Check that the values were cleared
56251c0b2f7Stbbdev                         sums.combine_each(ClearingAccumulator<T>::AssertClean);
56351c0b2f7Stbbdev                     }
56451c0b2f7Stbbdev                 }
56551c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
56651c0b2f7Stbbdev                 catch (...) {
56751c0b2f7Stbbdev                     exception_caught = true;
56851c0b2f7Stbbdev                 }
56951c0b2f7Stbbdev #endif
57051c0b2f7Stbbdev             }
57151c0b2f7Stbbdev 
572b15aabb3Stbbdev             if (!exception_caught) {
573b15aabb3Stbbdev                 REQUIRE(EXPECTED_SUM * p == test_helper<T>::get(combine_sum));
574b15aabb3Stbbdev                 REQUIRE(EXPECTED_SUM * p == test_helper<T>::get(combine_ref_sum));
575b15aabb3Stbbdev                 REQUIRE(EXPECTED_SUM * p == test_helper<T>::get(static_sum));
576b15aabb3Stbbdev                 REQUIRE(EXPECTED_SUM * p == test_helper<T>::get(accumulator_sum));
577b15aabb3Stbbdev                 REQUIRE(EXPECTED_SUM * p == test_helper<T>::get(clearing_accumulator_sum));
578b15aabb3Stbbdev             }
57951c0b2f7Stbbdev 
58051c0b2f7Stbbdev         }  // MinThread .. MaxThread
58151c0b2f7Stbbdev         test_throw_count += 10;  // keep testing until we don't get an exception
58251c0b2f7Stbbdev     } while (exception_caught && test_throw_count < 200);
58351c0b2f7Stbbdev     REQUIRE_MESSAGE(!exception_caught, "No non-exception test completed");
58451c0b2f7Stbbdev }
58551c0b2f7Stbbdev 
58651c0b2f7Stbbdev template <typename T, template<class> class Allocator>
58751c0b2f7Stbbdev class parallel_vector_for_body: utils::NoAssign {
58849e08aacStbbdev     typedef std::vector<T, oneapi::tbb::tbb_allocator<T> > container_type;
58949e08aacStbbdev     typedef oneapi::tbb::enumerable_thread_specific< container_type, Allocator<container_type> > ets_type;
59051c0b2f7Stbbdev     ets_type &locals;
59151c0b2f7Stbbdev     const char *allocator_name;
59251c0b2f7Stbbdev 
59351c0b2f7Stbbdev public:
59451c0b2f7Stbbdev 
parallel_vector_for_body(ets_type & _locals,const char * aname)59551c0b2f7Stbbdev     parallel_vector_for_body ( ets_type &_locals, const char *aname ) : locals(_locals), allocator_name(aname) { }
59651c0b2f7Stbbdev 
operator ()(const oneapi::tbb::blocked_range<int> & r) const59749e08aacStbbdev     void operator()( const oneapi::tbb::blocked_range<int> &r ) const {
59851c0b2f7Stbbdev         T one;
59951c0b2f7Stbbdev         test_helper<T>::set(one, 1);
60051c0b2f7Stbbdev 
60151c0b2f7Stbbdev         for (int i = r.begin(); i < r.end(); ++i) {
60251c0b2f7Stbbdev             check_alignment(locals.local(),allocator_name).push_back( one );
60351c0b2f7Stbbdev         }
60451c0b2f7Stbbdev     }
60551c0b2f7Stbbdev 
60651c0b2f7Stbbdev };
60751c0b2f7Stbbdev 
60851c0b2f7Stbbdev template <typename R, typename T>
60951c0b2f7Stbbdev struct parallel_vector_reduce_body {
61051c0b2f7Stbbdev 
61151c0b2f7Stbbdev     T sum;
61251c0b2f7Stbbdev     size_t count;
61349e08aacStbbdev     typedef std::vector<T, oneapi::tbb::tbb_allocator<T> > container_type;
61451c0b2f7Stbbdev 
parallel_vector_reduce_bodyparallel_vector_reduce_body61551c0b2f7Stbbdev     parallel_vector_reduce_body ( ) : count(0) { test_helper<T>::init(sum); }
parallel_vector_reduce_bodyparallel_vector_reduce_body61649e08aacStbbdev     parallel_vector_reduce_body ( parallel_vector_reduce_body<R, T> &, oneapi::tbb::split ) : count(0) {  test_helper<T>::init(sum); }
61751c0b2f7Stbbdev 
operator ()parallel_vector_reduce_body61851c0b2f7Stbbdev     void operator()( const R &r ) {
61951c0b2f7Stbbdev         for (typename R::iterator ri = r.begin(); ri != r.end(); ++ri) {
62051c0b2f7Stbbdev             const container_type &v = *ri;
62151c0b2f7Stbbdev             ++count;
62251c0b2f7Stbbdev             for (typename container_type::const_iterator vi = v.begin(); vi != v.end(); ++vi) {
62351c0b2f7Stbbdev                 test_helper<T>::sum(sum, *vi);
62451c0b2f7Stbbdev             }
62551c0b2f7Stbbdev         }
62651c0b2f7Stbbdev     }
62751c0b2f7Stbbdev 
joinparallel_vector_reduce_body62851c0b2f7Stbbdev     void join( const parallel_vector_reduce_body &b ) {
62951c0b2f7Stbbdev         test_helper<T>::sum(sum,b.sum);
63051c0b2f7Stbbdev         count += b.count;
63151c0b2f7Stbbdev     }
63251c0b2f7Stbbdev 
63351c0b2f7Stbbdev };
63451c0b2f7Stbbdev 
63551c0b2f7Stbbdev template< typename T, template<class> class Allocator>
run_parallel_vector_tests(const char *,const char * allocator_name)63651c0b2f7Stbbdev void run_parallel_vector_tests(const char* /* test_name */, const char *allocator_name) {
63749e08aacStbbdev     typedef std::vector<T, oneapi::tbb::tbb_allocator<T> > container_type;
63849e08aacStbbdev     typedef oneapi::tbb::enumerable_thread_specific< container_type, Allocator<container_type> > ets_type;
63951c0b2f7Stbbdev 
640b15aabb3Stbbdev     for (int p = std::max(MinThread, 2); p <= MaxThread; ++p) {
64149e08aacStbbdev         oneapi::tbb::global_control gc(oneapi::tbb::global_control::max_allowed_parallelism, p);
64251c0b2f7Stbbdev 
64351c0b2f7Stbbdev         T sum;
64451c0b2f7Stbbdev         test_helper<T>::init(sum);
64551c0b2f7Stbbdev 
64651c0b2f7Stbbdev         for (int t = -1; t < REPETITIONS; ++t) {
64751c0b2f7Stbbdev             ets_type vs;
64851c0b2f7Stbbdev 
64951c0b2f7Stbbdev             REQUIRE( vs.empty() );
650b15aabb3Stbbdev             oneapi::tbb::parallel_for( oneapi::tbb::blocked_range<int> (0, N*p, RANGE_MIN),
65151c0b2f7Stbbdev                                parallel_vector_for_body<T,Allocator>( vs, allocator_name ) );
65251c0b2f7Stbbdev             REQUIRE( !vs.empty() );
65351c0b2f7Stbbdev 
65451c0b2f7Stbbdev             // copy construct
65551c0b2f7Stbbdev             ets_type vs2(vs); // this causes an assertion failure, related to allocators...
65651c0b2f7Stbbdev 
65751c0b2f7Stbbdev             // assign
65851c0b2f7Stbbdev             ets_type vs3;
65951c0b2f7Stbbdev             vs3 = vs;
66051c0b2f7Stbbdev 
66151c0b2f7Stbbdev             parallel_vector_reduce_body< typename ets_type::const_range_type, T > pvrb;
66249e08aacStbbdev             oneapi::tbb::parallel_reduce ( vs.range(1), pvrb );
66351c0b2f7Stbbdev 
66451c0b2f7Stbbdev             test_helper<T>::sum(sum, pvrb.sum);
66551c0b2f7Stbbdev 
66651c0b2f7Stbbdev             REQUIRE( vs.size() == pvrb.count );
66751c0b2f7Stbbdev             REQUIRE( vs2.size() == pvrb.count );
66851c0b2f7Stbbdev             REQUIRE( vs3.size() == pvrb.count );
66951c0b2f7Stbbdev 
67049e08aacStbbdev             oneapi::tbb::flattened2d<ets_type> fvs = flatten2d(vs);
67151c0b2f7Stbbdev             size_t ccount = fvs.size();
672b15aabb3Stbbdev             REQUIRE( ccount == size_t(N*p) );
67351c0b2f7Stbbdev             size_t elem_cnt = 0;
67449e08aacStbbdev             typename oneapi::tbb::flattened2d<ets_type>::iterator it;
67551c0b2f7Stbbdev             auto it2(it);
67651c0b2f7Stbbdev             it = fvs.begin();
67751c0b2f7Stbbdev             REQUIRE(it != it2);
67849e08aacStbbdev             typename oneapi::tbb::flattened2d<ets_type>::iterator it3;
67949e08aacStbbdev             typename oneapi::tbb::flattened2d<ets_type>::const_iterator cit = fvs.begin();
68051c0b2f7Stbbdev             it3 = cit;
68151c0b2f7Stbbdev             REQUIRE(it3 == cit);
68251c0b2f7Stbbdev             REQUIRE(it3.operator->() == &(*it3));
68351c0b2f7Stbbdev 
68449e08aacStbbdev             for(typename oneapi::tbb::flattened2d<ets_type>::const_iterator i = fvs.begin(); i != fvs.end(); ++i) {
68551c0b2f7Stbbdev                 ++elem_cnt;
68651c0b2f7Stbbdev             };
68751c0b2f7Stbbdev             REQUIRE( ccount == elem_cnt );
68851c0b2f7Stbbdev 
68951c0b2f7Stbbdev             elem_cnt = 0;
69049e08aacStbbdev             for(typename oneapi::tbb::flattened2d<ets_type>::iterator i = fvs.begin(); i != fvs.end(); i++) {
69151c0b2f7Stbbdev                 ++elem_cnt;
69251c0b2f7Stbbdev             };
69351c0b2f7Stbbdev             REQUIRE( ccount == elem_cnt );
69451c0b2f7Stbbdev 
69551c0b2f7Stbbdev             // Test the ETS constructor with multiple args
69651c0b2f7Stbbdev             T minus_one;
69751c0b2f7Stbbdev             test_helper<T>::set(minus_one, -1);
69851c0b2f7Stbbdev             // Set ETS to construct "local" vectors pre-occupied with 25 "minus_one"s
69951c0b2f7Stbbdev             // Cast 25 to size_type to prevent Intel Compiler SFINAE compilation issues with gcc 5.
70049e08aacStbbdev             ets_type vvs( typename container_type::size_type(25), minus_one, oneapi::tbb::tbb_allocator<T>() );
70151c0b2f7Stbbdev             REQUIRE( vvs.empty() );
702b15aabb3Stbbdev             oneapi::tbb::parallel_for ( oneapi::tbb::blocked_range<int> (0, N*p, RANGE_MIN), parallel_vector_for_body<T,Allocator>( vvs, allocator_name ) );
70351c0b2f7Stbbdev             REQUIRE( !vvs.empty() );
70451c0b2f7Stbbdev 
70551c0b2f7Stbbdev             parallel_vector_reduce_body< typename ets_type::const_range_type, T > pvrb2;
70649e08aacStbbdev             oneapi::tbb::parallel_reduce ( vvs.range(1), pvrb2 );
70751c0b2f7Stbbdev             REQUIRE( pvrb2.count == vvs.size() );
708b15aabb3Stbbdev             REQUIRE( test_helper<T>::get(pvrb2.sum) == N*p-pvrb2.count*25 );
70951c0b2f7Stbbdev 
71049e08aacStbbdev             oneapi::tbb::flattened2d<ets_type> fvvs = flatten2d(vvs);
71151c0b2f7Stbbdev             ccount = fvvs.size();
712b15aabb3Stbbdev             REQUIRE( ccount == N*p+pvrb2.count*25 );
71351c0b2f7Stbbdev         }
71451c0b2f7Stbbdev 
71551c0b2f7Stbbdev         double result_value = test_helper<T>::get(sum);
716b15aabb3Stbbdev         REQUIRE( EXPECTED_SUM*p == result_value);
71751c0b2f7Stbbdev     }
71851c0b2f7Stbbdev }
71951c0b2f7Stbbdev 
72051c0b2f7Stbbdev template<typename T, template<class> class Allocator>
run_cross_type_vector_tests(const char *)72151c0b2f7Stbbdev void run_cross_type_vector_tests(const char* /* test_name */) {
72251c0b2f7Stbbdev     const char* allocator_name = "default";
72349e08aacStbbdev     typedef std::vector<T, oneapi::tbb::tbb_allocator<T> > container_type;
72451c0b2f7Stbbdev 
725b15aabb3Stbbdev     for (int p = std::max(MinThread, 2); p <= MaxThread; ++p) {
72649e08aacStbbdev         oneapi::tbb::global_control gc(oneapi::tbb::global_control::max_allowed_parallelism, p);
72751c0b2f7Stbbdev 
72851c0b2f7Stbbdev         T sum;
72951c0b2f7Stbbdev         test_helper<T>::init(sum);
73051c0b2f7Stbbdev 
73151c0b2f7Stbbdev         for (int t = -1; t < REPETITIONS; ++t) {
73249e08aacStbbdev             typedef typename oneapi::tbb::enumerable_thread_specific< container_type, Allocator<container_type>, oneapi::tbb::ets_no_key > ets_nokey_type;
73349e08aacStbbdev             typedef typename oneapi::tbb::enumerable_thread_specific< container_type, Allocator<container_type>, oneapi::tbb::ets_key_per_instance > ets_tlskey_type;
73451c0b2f7Stbbdev             ets_nokey_type vs;
73551c0b2f7Stbbdev 
73651c0b2f7Stbbdev             REQUIRE( vs.empty());
737b15aabb3Stbbdev             oneapi::tbb::parallel_for ( oneapi::tbb::blocked_range<int> (0, N*p, RANGE_MIN), parallel_vector_for_body<T, Allocator>( vs, allocator_name ) );
73851c0b2f7Stbbdev             REQUIRE( !vs.empty());
73951c0b2f7Stbbdev 
74051c0b2f7Stbbdev             // copy construct
74151c0b2f7Stbbdev             ets_tlskey_type vs2(vs);
74251c0b2f7Stbbdev 
74351c0b2f7Stbbdev             // assign
74451c0b2f7Stbbdev             ets_nokey_type vs3;
74551c0b2f7Stbbdev             vs3 = vs2;
74651c0b2f7Stbbdev 
74751c0b2f7Stbbdev             parallel_vector_reduce_body< typename ets_nokey_type::const_range_type, T > pvrb;
74849e08aacStbbdev             oneapi::tbb::parallel_reduce ( vs3.range(1), pvrb );
74951c0b2f7Stbbdev 
75051c0b2f7Stbbdev             test_helper<T>::sum(sum, pvrb.sum);
75151c0b2f7Stbbdev 
75251c0b2f7Stbbdev             REQUIRE( vs3.size() == pvrb.count);
75351c0b2f7Stbbdev 
75449e08aacStbbdev             oneapi::tbb::flattened2d<ets_nokey_type> fvs = flatten2d(vs3);
75551c0b2f7Stbbdev             size_t ccount = fvs.size();
75651c0b2f7Stbbdev             size_t elem_cnt = 0;
75749e08aacStbbdev             for(typename oneapi::tbb::flattened2d<ets_nokey_type>::const_iterator i = fvs.begin(); i != fvs.end(); ++i) {
75851c0b2f7Stbbdev                 ++elem_cnt;
75951c0b2f7Stbbdev             };
76051c0b2f7Stbbdev             REQUIRE(ccount == elem_cnt);
76151c0b2f7Stbbdev 
76251c0b2f7Stbbdev             elem_cnt = 0;
76349e08aacStbbdev             for(typename oneapi::tbb::flattened2d<ets_nokey_type>::iterator i = fvs.begin(); i != fvs.end(); ++i) {
76451c0b2f7Stbbdev                 ++elem_cnt;
76551c0b2f7Stbbdev             };
76651c0b2f7Stbbdev             REQUIRE(ccount == elem_cnt);
76751c0b2f7Stbbdev 
76849e08aacStbbdev             oneapi::tbb::flattened2d<ets_nokey_type> fvs2 = flatten2d(vs3, vs3.begin(), std::next(vs3.begin()));
76951c0b2f7Stbbdev             REQUIRE(std::distance(fvs2.begin(), fvs2.end()) == vs3.begin()->size());
77049e08aacStbbdev             const oneapi::tbb::flattened2d<ets_nokey_type>& cfvs2(fvs2);
77151c0b2f7Stbbdev             REQUIRE(std::distance(cfvs2.begin(), cfvs2.end()) == vs3.begin()->size());
77251c0b2f7Stbbdev         }
77351c0b2f7Stbbdev 
77451c0b2f7Stbbdev         double result_value = test_helper<T>::get(sum);
775b15aabb3Stbbdev         REQUIRE( EXPECTED_SUM*p == result_value);
77651c0b2f7Stbbdev     }
77751c0b2f7Stbbdev }
77851c0b2f7Stbbdev 
77951c0b2f7Stbbdev template< typename T >
run_serial_scalar_tests(const char *)78051c0b2f7Stbbdev void run_serial_scalar_tests(const char* /* test_name */) {
78151c0b2f7Stbbdev     T sum;
78251c0b2f7Stbbdev     test_helper<T>::init(sum);
78351c0b2f7Stbbdev 
78451c0b2f7Stbbdev     for (int t = -1; t < REPETITIONS; ++t) {
78551c0b2f7Stbbdev         for (int i = 0; i < N; ++i) {
78651c0b2f7Stbbdev             test_helper<T>::sum(sum,1);
78751c0b2f7Stbbdev         }
78851c0b2f7Stbbdev     }
78951c0b2f7Stbbdev 
79051c0b2f7Stbbdev     double result_value = test_helper<T>::get(sum);
79151c0b2f7Stbbdev     REQUIRE( EXPECTED_SUM == result_value);
79251c0b2f7Stbbdev }
79351c0b2f7Stbbdev 
79451c0b2f7Stbbdev template< typename T >
run_serial_vector_tests(const char *)79551c0b2f7Stbbdev void run_serial_vector_tests(const char* /* test_name */) {
79651c0b2f7Stbbdev     T sum;
79751c0b2f7Stbbdev     test_helper<T>::init(sum);
79851c0b2f7Stbbdev     T one;
79951c0b2f7Stbbdev     test_helper<T>::set(one, 1);
80051c0b2f7Stbbdev 
80151c0b2f7Stbbdev     for (int t = -1; t < REPETITIONS; ++t) {
80249e08aacStbbdev         std::vector<T, oneapi::tbb::tbb_allocator<T> > v;
80351c0b2f7Stbbdev         for (int i = 0; i < N; ++i) {
80451c0b2f7Stbbdev             v.push_back( one );
80551c0b2f7Stbbdev         }
80649e08aacStbbdev         for (typename std::vector<T, oneapi::tbb::tbb_allocator<T> >::const_iterator i = v.begin(); i != v.end(); ++i)
80751c0b2f7Stbbdev             test_helper<T>::sum(sum, *i);
80851c0b2f7Stbbdev     }
80951c0b2f7Stbbdev 
81051c0b2f7Stbbdev     double result_value = test_helper<T>::get(sum);
81151c0b2f7Stbbdev     REQUIRE( EXPECTED_SUM == result_value);
81251c0b2f7Stbbdev }
81351c0b2f7Stbbdev 
81449e08aacStbbdev const size_t line_size = oneapi::tbb::detail::max_nfs_size;
81551c0b2f7Stbbdev 
run_reference_check()81651c0b2f7Stbbdev void run_reference_check() {
81751c0b2f7Stbbdev     run_serial_scalar_tests<int>("int");
81851c0b2f7Stbbdev     run_serial_scalar_tests<double>("double");
819*90b0671bSAlex     run_serial_scalar_tests<minimalNComparable<> >("minimalNComparable<>");
82049e08aacStbbdev     run_serial_vector_tests<int>("std::vector<int, oneapi::tbb::tbb_allocator<int> >");
82149e08aacStbbdev     run_serial_vector_tests<double>("std::vector<double, oneapi::tbb::tbb_allocator<double> >");
82251c0b2f7Stbbdev }
82351c0b2f7Stbbdev 
82451c0b2f7Stbbdev template<template<class>class Allocator>
run_parallel_tests(const char * allocator_name)82551c0b2f7Stbbdev void run_parallel_tests(const char *allocator_name) {
82651c0b2f7Stbbdev     run_parallel_scalar_tests<int, Allocator>("int",allocator_name);
82751c0b2f7Stbbdev     run_parallel_scalar_tests<double, Allocator>("double",allocator_name);
828*90b0671bSAlex     run_parallel_scalar_tests_nocombine<minimalNComparable<>,Allocator>("minimalNComparable<>",allocator_name);
82951c0b2f7Stbbdev     run_parallel_scalar_tests<ThrowingConstructor, Allocator>("ThrowingConstructor", allocator_name);
83049e08aacStbbdev     run_parallel_vector_tests<int, Allocator>("std::vector<int, oneapi::tbb::tbb_allocator<int> >",allocator_name);
83149e08aacStbbdev     run_parallel_vector_tests<double, Allocator>("std::vector<double, oneapi::tbb::tbb_allocator<double> >",allocator_name);
83251c0b2f7Stbbdev }
83351c0b2f7Stbbdev 
run_cross_type_tests()83451c0b2f7Stbbdev void run_cross_type_tests() {
83551c0b2f7Stbbdev     // cross-type scalar tests are part of run_parallel_scalar_tests_nocombine
83649e08aacStbbdev     run_cross_type_vector_tests<int, oneapi::tbb::tbb_allocator>("std::vector<int, oneapi::tbb::tbb_allocator<int> >");
83749e08aacStbbdev     run_cross_type_vector_tests<double, oneapi::tbb::tbb_allocator>("std::vector<double, oneapi::tbb::tbb_allocator<double> >");
83851c0b2f7Stbbdev }
83951c0b2f7Stbbdev 
84051c0b2f7Stbbdev template<typename T, template<class> class Allocator, typename Init>
MakeETS(Init init)84149e08aacStbbdev oneapi::tbb::enumerable_thread_specific<T,Allocator<T> > MakeETS( Init init ) {
84249e08aacStbbdev     return oneapi::tbb::enumerable_thread_specific<T,Allocator<T> >(init);
84351c0b2f7Stbbdev }
84451c0b2f7Stbbdev // In some GCC versions, parameter packs in lambdas might cause compile errors
84551c0b2f7Stbbdev template<typename ETS, typename... P>
84651c0b2f7Stbbdev struct MakeETS_Functor {
operator ()MakeETS_Functor84751c0b2f7Stbbdev     ETS operator()( typename std::decay<P>::type&&... params ) {
84851c0b2f7Stbbdev         return ETS(std::move(params)...);
84951c0b2f7Stbbdev     }
85051c0b2f7Stbbdev };
85151c0b2f7Stbbdev template<typename T, template<class> class Allocator, typename... P>
MakeETS(oneapi::tbb::detail::stored_pack<P...> pack)85249e08aacStbbdev oneapi::tbb::enumerable_thread_specific<T,Allocator<T> > MakeETS( oneapi::tbb::detail::stored_pack<P...> pack ) {
85349e08aacStbbdev     typedef oneapi::tbb::enumerable_thread_specific<T,Allocator<T> > result_type;
85449e08aacStbbdev     return oneapi::tbb::detail::call_and_return< result_type >(
85551c0b2f7Stbbdev         MakeETS_Functor<result_type,P...>(), std::move(pack)
85651c0b2f7Stbbdev     );
85751c0b2f7Stbbdev }
85851c0b2f7Stbbdev 
85951c0b2f7Stbbdev template<typename T, template<class> class Allocator, typename InitSrc, typename InitDst, typename Validator>
ets_copy_assign_test(InitSrc init1,InitDst init2,Validator check,const char * allocator_name)86051c0b2f7Stbbdev void ets_copy_assign_test( InitSrc init1, InitDst init2, Validator check, const char *allocator_name ) {
86149e08aacStbbdev     typedef oneapi::tbb::enumerable_thread_specific<T, Allocator<T> > ets_type;
86251c0b2f7Stbbdev 
86351c0b2f7Stbbdev     // Create the source instance
86451c0b2f7Stbbdev     const ets_type& cref_binder = MakeETS<T, Allocator>(init1);
86551c0b2f7Stbbdev     ets_type& source = const_cast<ets_type&>(cref_binder);
86651c0b2f7Stbbdev     check(check_alignment(source.local(),allocator_name));
86751c0b2f7Stbbdev 
86851c0b2f7Stbbdev     // Test copy construction
86951c0b2f7Stbbdev     bool existed = false;
87051c0b2f7Stbbdev     ets_type copy(source);
87151c0b2f7Stbbdev     check(check_alignment(copy.local(existed),allocator_name));
87251c0b2f7Stbbdev     REQUIRE_MESSAGE(existed, "Local data not created by ETS copy constructor");
87351c0b2f7Stbbdev     copy.clear();
87451c0b2f7Stbbdev     check(check_alignment(copy.local(),allocator_name));
87551c0b2f7Stbbdev 
87651c0b2f7Stbbdev     // Test assignment
87751c0b2f7Stbbdev     existed = false;
87851c0b2f7Stbbdev     ets_type assign(init2);
87951c0b2f7Stbbdev     assign = source;
88051c0b2f7Stbbdev     check(check_alignment(assign.local(existed),allocator_name));
88151c0b2f7Stbbdev     REQUIRE_MESSAGE(existed, "Local data not created by ETS assignment");
88251c0b2f7Stbbdev     assign.clear();
88351c0b2f7Stbbdev     check(check_alignment(assign.local(),allocator_name));
88451c0b2f7Stbbdev 
88551c0b2f7Stbbdev     // Create the source instance
88651c0b2f7Stbbdev     ets_type&& rvref_binder = MakeETS<T, Allocator>(init1);
88751c0b2f7Stbbdev     check(check_alignment(rvref_binder.local(),allocator_name));
88851c0b2f7Stbbdev 
88951c0b2f7Stbbdev     // Test move construction
89051c0b2f7Stbbdev     existed = false;
89151c0b2f7Stbbdev     ets_type moved(rvref_binder);
89251c0b2f7Stbbdev     check(check_alignment(moved.local(existed),allocator_name));
89351c0b2f7Stbbdev     REQUIRE_MESSAGE(existed, "Local data not created by ETS move constructor");
89451c0b2f7Stbbdev     moved.clear();
89551c0b2f7Stbbdev     check(check_alignment(moved.local(),allocator_name));
89651c0b2f7Stbbdev 
89751c0b2f7Stbbdev     // Test assignment
89851c0b2f7Stbbdev     existed = false;
89951c0b2f7Stbbdev     ets_type move_assign(init2);
90051c0b2f7Stbbdev     move_assign = std::move(moved);
90151c0b2f7Stbbdev     check(check_alignment(move_assign.local(existed),allocator_name));
90251c0b2f7Stbbdev     REQUIRE_MESSAGE(existed, "Local data not created by ETS move assignment");
90351c0b2f7Stbbdev     move_assign.clear();
90451c0b2f7Stbbdev     check(check_alignment(move_assign.local(),allocator_name));
90551c0b2f7Stbbdev }
90651c0b2f7Stbbdev 
90751c0b2f7Stbbdev template<typename T, int Expected>
90851c0b2f7Stbbdev struct Validator {
operator ()Validator90951c0b2f7Stbbdev     void operator()( const T& value ) {
91051c0b2f7Stbbdev         REQUIRE(test_helper<T>::get(value) == Expected);
91151c0b2f7Stbbdev     }
operator ()Validator91251c0b2f7Stbbdev     void operator()( const std::pair<int,T>& value ) {
91351c0b2f7Stbbdev         REQUIRE(value.first > 0);
91451c0b2f7Stbbdev         REQUIRE(test_helper<T>::get(value.second) == Expected*value.first);
91551c0b2f7Stbbdev     }
91651c0b2f7Stbbdev };
91751c0b2f7Stbbdev 
91851c0b2f7Stbbdev template <typename T, template<class> class Allocator>
run_assign_and_copy_constructor_test(const char *,const char * allocator_name)91951c0b2f7Stbbdev void run_assign_and_copy_constructor_test(const char* /* test_name */, const char *allocator_name) {
92051c0b2f7Stbbdev     #define EXPECTED 3142
92151c0b2f7Stbbdev 
92251c0b2f7Stbbdev     // test with exemplar initializer
92351c0b2f7Stbbdev     T src_init;
92451c0b2f7Stbbdev     test_helper<T>::set(src_init,EXPECTED);
92551c0b2f7Stbbdev     T other_init;
92651c0b2f7Stbbdev     test_helper<T>::init(other_init);
92751c0b2f7Stbbdev     ets_copy_assign_test<T, Allocator>(src_init, other_init, Validator<T,EXPECTED>(), allocator_name);
92851c0b2f7Stbbdev 
92951c0b2f7Stbbdev     // test with function initializer
93051c0b2f7Stbbdev     FunctorFinit<T,EXPECTED> src_finit(SecretTag);
93151c0b2f7Stbbdev     FunctorFinit<T,0> other_finit(SecretTag);
93251c0b2f7Stbbdev     ets_copy_assign_test<T, Allocator>(src_finit, other_finit, Validator<T,EXPECTED>(), allocator_name);
93351c0b2f7Stbbdev 
93451c0b2f7Stbbdev     // test with multi-argument "emplace" initializer
93549e08aacStbbdev     // The arguments are wrapped into oneapi::tbb::internal::stored_pack to avoid variadic templates in ets_copy_assign_test.
93651c0b2f7Stbbdev     test_helper<T>::set(src_init,EXPECTED*17);
93749e08aacStbbdev     ets_copy_assign_test< std::pair<int,T>, Allocator>(oneapi::tbb::detail::save_pack(17,src_init), std::make_pair(-1,T()), Validator<T,EXPECTED>(), allocator_name);
93851c0b2f7Stbbdev     #undef EXPECTED
93951c0b2f7Stbbdev }
94051c0b2f7Stbbdev 
94151c0b2f7Stbbdev template< template<class> class Allocator>
run_assignment_and_copy_constructor_tests(const char * allocator_name)94251c0b2f7Stbbdev void run_assignment_and_copy_constructor_tests(const char* allocator_name) {
94351c0b2f7Stbbdev     run_assign_and_copy_constructor_test<int, Allocator>("int", allocator_name);
94451c0b2f7Stbbdev     run_assign_and_copy_constructor_test<double, Allocator>("double", allocator_name);
94551c0b2f7Stbbdev     // Try class sizes that are close to a cache line in size, in order to check padding calculations.
946*90b0671bSAlex     run_assign_and_copy_constructor_test<minimalNComparable<line_size-1>, Allocator >("minimalNComparable<line_size-1>", allocator_name);
947*90b0671bSAlex     run_assign_and_copy_constructor_test<minimalNComparable<line_size>, Allocator >("minimalNComparable<line_size>", allocator_name);
948*90b0671bSAlex     run_assign_and_copy_constructor_test<minimalNComparable<line_size+1>, Allocator >("minimalNComparable<line_size+1>", allocator_name);
94951c0b2f7Stbbdev     REQUIRE(FinitCounter==0);
95051c0b2f7Stbbdev }
95151c0b2f7Stbbdev 
95251c0b2f7Stbbdev // Class with no default constructor
95351c0b2f7Stbbdev class HasNoDefaultConstructor {
95451c0b2f7Stbbdev     HasNoDefaultConstructor();
95551c0b2f7Stbbdev public:
HasNoDefaultConstructor(SecretTagType)95651c0b2f7Stbbdev     HasNoDefaultConstructor( SecretTagType ) {}
95751c0b2f7Stbbdev };
95851c0b2f7Stbbdev // Initialization functor for HasNoDefaultConstructor
95951c0b2f7Stbbdev struct HasNoDefaultConstructorFinit {
operator ()HasNoDefaultConstructorFinit96051c0b2f7Stbbdev     HasNoDefaultConstructor operator()() {
96151c0b2f7Stbbdev         return HasNoDefaultConstructor(SecretTag);
96251c0b2f7Stbbdev     }
96351c0b2f7Stbbdev };
96451c0b2f7Stbbdev // Combine functor for HasNoDefaultConstructor
96551c0b2f7Stbbdev struct HasNoDefaultConstructorCombine {
operator ()HasNoDefaultConstructorCombine96651c0b2f7Stbbdev     HasNoDefaultConstructor operator()( HasNoDefaultConstructor, HasNoDefaultConstructor ) {
96751c0b2f7Stbbdev         return HasNoDefaultConstructor(SecretTag);
96851c0b2f7Stbbdev     }
96951c0b2f7Stbbdev };
97051c0b2f7Stbbdev 
97151c0b2f7Stbbdev // Class that only has a constructor with multiple parameters and a move constructor
97251c0b2f7Stbbdev class HasSpecialAndMoveCtor : utils::NoCopy {
97351c0b2f7Stbbdev     HasSpecialAndMoveCtor();
97451c0b2f7Stbbdev public:
HasSpecialAndMoveCtor(SecretTagType,size_t=size_t (0),const char * ="")97551c0b2f7Stbbdev     HasSpecialAndMoveCtor( SecretTagType, size_t = size_t(0), const char* = "" ) {}
HasSpecialAndMoveCtor(HasSpecialAndMoveCtor &&)97651c0b2f7Stbbdev     HasSpecialAndMoveCtor( HasSpecialAndMoveCtor&& ) {}
97751c0b2f7Stbbdev };
97851c0b2f7Stbbdev 
97951c0b2f7Stbbdev // No-op combine-each functor
98051c0b2f7Stbbdev template<typename V>
98151c0b2f7Stbbdev struct EmptyCombineEach {
operator ()EmptyCombineEach98251c0b2f7Stbbdev     void operator()( const V& ) { }
98351c0b2f7Stbbdev };
98451c0b2f7Stbbdev 
98551c0b2f7Stbbdev //! Test situations where only default constructor or copy constructor is required.
98651c0b2f7Stbbdev template<template<class> class Allocator>
TestInstantiation(const char *)98751c0b2f7Stbbdev void TestInstantiation(const char* /* allocator_name */) {
98851c0b2f7Stbbdev     // Test instantiation is possible when copy constructor is not required.
98949e08aacStbbdev     oneapi::tbb::enumerable_thread_specific<utils::NoCopy, Allocator<utils::NoCopy> > ets1;
99051c0b2f7Stbbdev     ets1.local();
99151c0b2f7Stbbdev     ets1.combine_each(EmptyCombineEach<utils::NoCopy>());
99251c0b2f7Stbbdev 
99351c0b2f7Stbbdev     // Test instantiation when default constructor is not required, because exemplar is provided.
99451c0b2f7Stbbdev     HasNoDefaultConstructor x(SecretTag);
99549e08aacStbbdev     oneapi::tbb::enumerable_thread_specific<HasNoDefaultConstructor, Allocator<HasNoDefaultConstructor> > ets2(x);
99651c0b2f7Stbbdev     ets2.local();
99751c0b2f7Stbbdev     ets2.combine(HasNoDefaultConstructorCombine());
99851c0b2f7Stbbdev 
99951c0b2f7Stbbdev     // Test instantiation when default constructor is not required, because init function is provided.
100051c0b2f7Stbbdev     HasNoDefaultConstructorFinit f;
100149e08aacStbbdev     oneapi::tbb::enumerable_thread_specific<HasNoDefaultConstructor, Allocator<HasNoDefaultConstructor> > ets3(f);
100251c0b2f7Stbbdev     ets3.local();
100351c0b2f7Stbbdev     ets3.combine(HasNoDefaultConstructorCombine());
100451c0b2f7Stbbdev 
100551c0b2f7Stbbdev     // Test instantiation with multiple arguments
100649e08aacStbbdev     oneapi::tbb::enumerable_thread_specific<HasSpecialAndMoveCtor, Allocator<HasSpecialAndMoveCtor> > ets4(SecretTag, 0x42, "meaningless");
100751c0b2f7Stbbdev     ets4.local();
100851c0b2f7Stbbdev     ets4.combine_each(EmptyCombineEach<HasSpecialAndMoveCtor>());
100951c0b2f7Stbbdev     // Test instantiation with one argument that should however use the variadic constructor
101049e08aacStbbdev     oneapi::tbb::enumerable_thread_specific<HasSpecialAndMoveCtor, Allocator<HasSpecialAndMoveCtor> > ets5(SecretTag);
101151c0b2f7Stbbdev     ets5.local();
101251c0b2f7Stbbdev     ets5.combine_each(EmptyCombineEach<HasSpecialAndMoveCtor>());
101351c0b2f7Stbbdev     // Test that move operations do not impose extra requirements
101451c0b2f7Stbbdev     // Default allocator is used. If it does not match Allocator, there will be elementwise move
101549e08aacStbbdev     oneapi::tbb::enumerable_thread_specific<HasSpecialAndMoveCtor> ets6( std::move(ets4) );
101651c0b2f7Stbbdev     ets6.combine_each(EmptyCombineEach<HasSpecialAndMoveCtor>());
101751c0b2f7Stbbdev     ets6 = std::move(ets5);
101851c0b2f7Stbbdev }
101951c0b2f7Stbbdev 
TestMemberTypes()102051c0b2f7Stbbdev void TestMemberTypes() {
102149e08aacStbbdev     using default_container_type = oneapi::tbb::enumerable_thread_specific<int>;
102249e08aacStbbdev     static_assert(std::is_same<typename default_container_type::allocator_type, oneapi::tbb::cache_aligned_allocator<int>>::value,
102351c0b2f7Stbbdev             "Incorrect default template allocator");
102451c0b2f7Stbbdev 
102551c0b2f7Stbbdev     using test_allocator_type = std::allocator<int>;
102649e08aacStbbdev     using ets_container_type = oneapi::tbb::enumerable_thread_specific<int, test_allocator_type>;
102751c0b2f7Stbbdev 
102851c0b2f7Stbbdev     static_assert(std::is_same<typename ets_container_type::allocator_type, test_allocator_type>::value,
102951c0b2f7Stbbdev                   "Incorrect container allocator_type member type");
103051c0b2f7Stbbdev 
103151c0b2f7Stbbdev     using value_type = typename ets_container_type::value_type;
103251c0b2f7Stbbdev 
103351c0b2f7Stbbdev     static_assert(std::is_same<typename ets_container_type::value_type, int>::value,
103451c0b2f7Stbbdev                   "Incorrect container value_type member type");
103551c0b2f7Stbbdev     static_assert(std::is_same<typename ets_container_type::reference, value_type&>::value,
103651c0b2f7Stbbdev                   "Incorrect container reference member type");
103751c0b2f7Stbbdev     static_assert(std::is_same<typename ets_container_type::const_reference, const value_type&>::value,
103851c0b2f7Stbbdev                   "Incorrect container const_reference member type");
103951c0b2f7Stbbdev 
104051c0b2f7Stbbdev     using allocator_type = typename ets_container_type::allocator_type;
104151c0b2f7Stbbdev     static_assert(std::is_same<typename ets_container_type::pointer, typename std::allocator_traits<allocator_type>::pointer>::value,
104251c0b2f7Stbbdev                   "Incorrect container pointer member type");
104351c0b2f7Stbbdev     static_assert(std::is_same<typename ets_container_type::const_pointer, typename std::allocator_traits<allocator_type>::const_pointer>::value,
104451c0b2f7Stbbdev                   "Incorrect container const_pointer member type");
104551c0b2f7Stbbdev 
104651c0b2f7Stbbdev     static_assert(std::is_unsigned<typename ets_container_type::size_type>::value,
104751c0b2f7Stbbdev                   "Incorrect container size_type member type");
104851c0b2f7Stbbdev     static_assert(std::is_signed<typename ets_container_type::difference_type>::value,
104951c0b2f7Stbbdev                   "Incorrect container difference_type member type");
105051c0b2f7Stbbdev 
105151c0b2f7Stbbdev     static_assert(utils::is_random_access_iterator<typename ets_container_type::iterator>::value,
105251c0b2f7Stbbdev                   "Incorrect container iterator member type");
105351c0b2f7Stbbdev     static_assert(!std::is_const<typename ets_container_type::iterator::value_type>::value,
105451c0b2f7Stbbdev                   "Incorrect container iterator member type");
105551c0b2f7Stbbdev     static_assert(utils::is_random_access_iterator<typename ets_container_type::const_iterator>::value,
105651c0b2f7Stbbdev                   "Incorrect container const_iterator member type");
105751c0b2f7Stbbdev     static_assert(std::is_const<typename ets_container_type::const_iterator::value_type>::value,
105851c0b2f7Stbbdev                   "Incorrect container iterator member type");
105951c0b2f7Stbbdev }
106051c0b2f7Stbbdev 
init_tbb_alloc_mask()106151c0b2f7Stbbdev size_t init_tbb_alloc_mask() {
106251c0b2f7Stbbdev     // TODO: use __TBB_alignof(T) to check for local() results instead of using internal knowledges of ets element padding
106349e08aacStbbdev     if(oneapi::tbb::tbb_allocator<int>::allocator_type() == oneapi::tbb::tbb_allocator<int>::standard) {
106451c0b2f7Stbbdev         // scalable allocator is not available.
106551c0b2f7Stbbdev         return 1;
106651c0b2f7Stbbdev     }
106751c0b2f7Stbbdev     else {
106851c0b2f7Stbbdev         // this value is for large objects, but will be correct for small.
106951c0b2f7Stbbdev         return 64; // TBB_REVAMP_TODO: enable as estimatedCacheLineSize when tbbmalloc is available;
107051c0b2f7Stbbdev     }
107151c0b2f7Stbbdev }
107251c0b2f7Stbbdev 
107351c0b2f7Stbbdev // TODO: rework the test not to depend on oneTBB internals
107449e08aacStbbdev static const size_t cache_allocator_mask = oneapi::tbb::detail::r1::cache_line_size();
107551c0b2f7Stbbdev static const size_t tbb_allocator_mask = init_tbb_alloc_mask();
107651c0b2f7Stbbdev 
TestETSIterator()107751c0b2f7Stbbdev void TestETSIterator() {
107849e08aacStbbdev     using ets_type = oneapi::tbb::enumerable_thread_specific<int>;
107951c0b2f7Stbbdev     if (utils::get_platform_max_threads() == 1) {
108051c0b2f7Stbbdev         ets_type ets;
108151c0b2f7Stbbdev         ets.local() = 1;
108251c0b2f7Stbbdev         REQUIRE_MESSAGE(std::next(ets.begin()) == ets.end(), "Incorrect begin or end of the ETS");
108351c0b2f7Stbbdev         REQUIRE_MESSAGE(std::prev(ets.end()) == ets.begin(), "Incorrect begin or end of the ETS");
108451c0b2f7Stbbdev     } else {
108551c0b2f7Stbbdev         std::atomic<std::size_t> sync_counter(0);
108651c0b2f7Stbbdev 
108751c0b2f7Stbbdev         const std::size_t expected_ets_size = 2;
108851c0b2f7Stbbdev         ets_type ets;
108951c0b2f7Stbbdev         const ets_type& cets(ets);
109051c0b2f7Stbbdev 
109151c0b2f7Stbbdev         auto fill_ets_body = [&](){
109251c0b2f7Stbbdev             ets.local() = 42;
109351c0b2f7Stbbdev             ++sync_counter;
109451c0b2f7Stbbdev             while(sync_counter != expected_ets_size)
1095b15aabb3Stbbdev                 utils::yield();
109651c0b2f7Stbbdev         };
109751c0b2f7Stbbdev 
109849e08aacStbbdev         oneapi::tbb::parallel_invoke(fill_ets_body, fill_ets_body);
109951c0b2f7Stbbdev         REQUIRE_MESSAGE(ets.size() == expected_ets_size, "Incorrect ETS size");
110051c0b2f7Stbbdev 
110151c0b2f7Stbbdev         std::size_t counter = 0;
110251c0b2f7Stbbdev         auto iter = ets.begin();
110351c0b2f7Stbbdev         while(iter != ets.end()) {
110451c0b2f7Stbbdev             ++counter % 2 == 0 ? ++iter : iter++;
110551c0b2f7Stbbdev         }
110651c0b2f7Stbbdev         REQUIRE(counter == expected_ets_size);
110751c0b2f7Stbbdev         while(iter != ets.begin()) {
110851c0b2f7Stbbdev             --counter % 2 == 0 ? --iter : iter--;
110951c0b2f7Stbbdev         }
111051c0b2f7Stbbdev         REQUIRE(counter == 0);
111151c0b2f7Stbbdev         auto citer = cets.begin();
111251c0b2f7Stbbdev         while(citer != cets.end()) {
111351c0b2f7Stbbdev             ++counter % 2 == 0 ? ++citer : citer++;
111451c0b2f7Stbbdev         }
111551c0b2f7Stbbdev         REQUIRE(counter == expected_ets_size);
111651c0b2f7Stbbdev         while(citer != cets.begin()) {
111751c0b2f7Stbbdev             --counter % 2 == 0 ? --citer : citer--;
111851c0b2f7Stbbdev         }
111951c0b2f7Stbbdev         REQUIRE(counter == 0);
112051c0b2f7Stbbdev         REQUIRE(ets.begin() + expected_ets_size == ets.end());
112151c0b2f7Stbbdev         REQUIRE(expected_ets_size + ets.begin() == ets.end());
112251c0b2f7Stbbdev         REQUIRE(ets.end() - expected_ets_size == ets.begin());
112351c0b2f7Stbbdev 
112451c0b2f7Stbbdev         typename ets_type::iterator it;
112551c0b2f7Stbbdev         it = ets.begin();
112651c0b2f7Stbbdev 
112751c0b2f7Stbbdev         auto it_bkp = it;
112851c0b2f7Stbbdev         auto it2 = it++;
112951c0b2f7Stbbdev         REQUIRE(it2 == it_bkp);
113051c0b2f7Stbbdev 
113151c0b2f7Stbbdev         it = ets.begin();
113251c0b2f7Stbbdev         it += expected_ets_size;
113351c0b2f7Stbbdev         REQUIRE(it == ets.end());
113451c0b2f7Stbbdev         it -= expected_ets_size;
113551c0b2f7Stbbdev         REQUIRE(it == ets.begin());
113651c0b2f7Stbbdev 
113751c0b2f7Stbbdev         for (int i = 0; i < int(expected_ets_size - 1); ++i) {
113851c0b2f7Stbbdev             REQUIRE(ets.begin()[i] == 42);
113951c0b2f7Stbbdev             REQUIRE(std::prev(ets.end())[-i] == 42);
114051c0b2f7Stbbdev         }
114151c0b2f7Stbbdev 
114251c0b2f7Stbbdev         auto iter1 = ets.begin();
114351c0b2f7Stbbdev         auto iter2 = ets.end();
114451c0b2f7Stbbdev         REQUIRE(iter1 < iter2);
114551c0b2f7Stbbdev         REQUIRE(iter1 <= iter2);
114651c0b2f7Stbbdev         REQUIRE(!(iter1 > iter2));
114751c0b2f7Stbbdev         REQUIRE(!(iter1 >= iter2));
114851c0b2f7Stbbdev     }
114951c0b2f7Stbbdev }
115051c0b2f7Stbbdev 
1151b15aabb3Stbbdev template <bool ExpectEqual, bool ExpectLess, typename Iterator>
DoETSIteratorComparisons(const Iterator & lhs,const Iterator & rhs)1152b15aabb3Stbbdev void DoETSIteratorComparisons( const Iterator& lhs, const Iterator& rhs ) {
1153b15aabb3Stbbdev     // TODO: replace with testEqualityAndLessComparisons after adding <=> operator for ETS iterator
1154b15aabb3Stbbdev     using namespace comparisons_testing;
1155b15aabb3Stbbdev     testEqualityComparisons<ExpectEqual>(lhs, rhs);
1156b15aabb3Stbbdev     testTwoWayComparisons<ExpectEqual, ExpectLess>(lhs, rhs);
1157b15aabb3Stbbdev }
1158b15aabb3Stbbdev 
1159b15aabb3Stbbdev template <typename Iterator, typename ETS>
TestETSIteratorComparisonsBasic(ETS & ets)1160b15aabb3Stbbdev void TestETSIteratorComparisonsBasic( ETS& ets ) {
1161b15aabb3Stbbdev     REQUIRE_MESSAGE(!ets.empty(), "Incorrect test setup");
1162b15aabb3Stbbdev     Iterator it1, it2;
1163b15aabb3Stbbdev     DoETSIteratorComparisons</*ExpectEqual = */true, /*ExpectLess = */false>(it1, it2);
1164b15aabb3Stbbdev     it1 = ets.begin();
1165b15aabb3Stbbdev     it2 = ets.begin();
1166b15aabb3Stbbdev     DoETSIteratorComparisons</*ExpectEqual = */true, /*ExpectLess = */false>(it1, it2);
1167b15aabb3Stbbdev     it2 = std::prev(ets.end());
1168b15aabb3Stbbdev     DoETSIteratorComparisons</*ExpectEqual = */false, /*ExpectLess = */true>(it1, it2);
1169b15aabb3Stbbdev }
1170b15aabb3Stbbdev 
TestETSIteratorComparisons()1171b15aabb3Stbbdev void TestETSIteratorComparisons() {
1172b15aabb3Stbbdev     using ets_type = oneapi::tbb::enumerable_thread_specific<int>;
1173b15aabb3Stbbdev     ets_type ets;
1174b15aabb3Stbbdev 
1175b15aabb3Stbbdev     // Fill the ets
1176b15aabb3Stbbdev     const std::size_t expected_ets_size = 2;
1177b15aabb3Stbbdev     std::atomic<std::size_t> sync_counter(0);
11788b6f831cStbbdev     auto fill_ets_body = [&](int){
1179b15aabb3Stbbdev             ets.local() = 42;
1180b15aabb3Stbbdev             ++sync_counter;
1181b15aabb3Stbbdev             while(sync_counter != expected_ets_size)
1182b15aabb3Stbbdev                 std::this_thread::yield();
1183b15aabb3Stbbdev         };
1184b15aabb3Stbbdev 
11858b6f831cStbbdev     utils::NativeParallelFor(2, fill_ets_body);
1186b15aabb3Stbbdev 
1187b15aabb3Stbbdev     TestETSIteratorComparisonsBasic<typename ets_type::iterator>(ets);
1188b15aabb3Stbbdev     const ets_type& cets = ets;
1189b15aabb3Stbbdev     TestETSIteratorComparisonsBasic<typename ets_type::const_iterator>(cets);
1190b15aabb3Stbbdev }
1191b15aabb3Stbbdev 
119251c0b2f7Stbbdev //! Test container instantiation
119351c0b2f7Stbbdev //! \brief \ref interface \ref requirement
119451c0b2f7Stbbdev TEST_CASE("Instantiation") {
119551c0b2f7Stbbdev     AlignMask = cache_allocator_mask;
119649e08aacStbbdev     TestInstantiation<oneapi::tbb::cache_aligned_allocator>("oneapi::tbb::cache_aligned_allocator");
119751c0b2f7Stbbdev     AlignMask = tbb_allocator_mask;
119849e08aacStbbdev     TestInstantiation<oneapi::tbb::tbb_allocator>("oneapi::tbb::tbb_allocator");
119951c0b2f7Stbbdev }
120051c0b2f7Stbbdev 
120151c0b2f7Stbbdev //! Test assignment and copy constructor
120251c0b2f7Stbbdev //! \brief \ref interface \ref requirement
120351c0b2f7Stbbdev TEST_CASE("Assignment and copy constructor") {
120451c0b2f7Stbbdev     AlignMask = cache_allocator_mask;
120549e08aacStbbdev     run_assignment_and_copy_constructor_tests<oneapi::tbb::cache_aligned_allocator>("oneapi::tbb::cache_aligned_allocator");
120651c0b2f7Stbbdev     AlignMask = tbb_allocator_mask;
120749e08aacStbbdev     run_assignment_and_copy_constructor_tests<oneapi::tbb::tbb_allocator>("oneapi::tbb::tbb_allocator");
120851c0b2f7Stbbdev }
120951c0b2f7Stbbdev 
121051c0b2f7Stbbdev //! Test for basic ETS functionality and requirements
121151c0b2f7Stbbdev //! \brief \ref interface \ref requirement
121251c0b2f7Stbbdev TEST_CASE("Basic ETS functionality") {
121351c0b2f7Stbbdev     const int LOCALS = 10;
121451c0b2f7Stbbdev 
121549e08aacStbbdev     oneapi::tbb::enumerable_thread_specific<int> ets;
121651c0b2f7Stbbdev     ets.local() = 42;
121751c0b2f7Stbbdev 
121851c0b2f7Stbbdev     utils::SpinBarrier barrier(LOCALS);
__anonba7ca1840402(int i) 121951c0b2f7Stbbdev     utils::NativeParallelFor(LOCALS, [&](int i) {
122051c0b2f7Stbbdev         barrier.wait();
122151c0b2f7Stbbdev         ets.local() = i;
122251c0b2f7Stbbdev         CHECK(ets.local() == i);
122351c0b2f7Stbbdev     });
122451c0b2f7Stbbdev     CHECK(ets.local() == 42);
122551c0b2f7Stbbdev 
122651c0b2f7Stbbdev     int ref_combined{0};
122751c0b2f7Stbbdev     std::vector<int> sequence(LOCALS);
122851c0b2f7Stbbdev     std::iota(sequence.begin(), sequence.end(), 0);
122951c0b2f7Stbbdev     for (int i : sequence) {
123051c0b2f7Stbbdev         ref_combined += i;
123151c0b2f7Stbbdev     }
123251c0b2f7Stbbdev     ref_combined += 42;
__anonba7ca1840502(int x, int y) 123351c0b2f7Stbbdev     int ets_combined = ets.combine([](int x, int y) {
123451c0b2f7Stbbdev         return x + y;
123551c0b2f7Stbbdev     });
123651c0b2f7Stbbdev     CHECK(ref_combined == ets_combined);
123751c0b2f7Stbbdev }
123851c0b2f7Stbbdev 
123951c0b2f7Stbbdev //! Test ETS usage in parallel algorithms.
124051c0b2f7Stbbdev //! Also tests flattened2d and flattend2d
124151c0b2f7Stbbdev //! \brief \ref interface \ref requirement \ref stress
124251c0b2f7Stbbdev TEST_CASE("Parallel test") {
124351c0b2f7Stbbdev     run_reference_check();
124451c0b2f7Stbbdev     AlignMask = cache_allocator_mask;
124549e08aacStbbdev     run_parallel_tests<oneapi::tbb::cache_aligned_allocator>("oneapi::tbb::cache_aligned_allocator");
124651c0b2f7Stbbdev     AlignMask = tbb_allocator_mask;
124749e08aacStbbdev     run_parallel_tests<oneapi::tbb::tbb_allocator>("oneapi::tbb::tbb_allocator");
124851c0b2f7Stbbdev     run_cross_type_tests();
124951c0b2f7Stbbdev }
125051c0b2f7Stbbdev 
125151c0b2f7Stbbdev //! \brief \ref interface \ref requirement
125251c0b2f7Stbbdev TEST_CASE("Member types") {
125351c0b2f7Stbbdev     TestMemberTypes();
125451c0b2f7Stbbdev }
125551c0b2f7Stbbdev 
125651c0b2f7Stbbdev //! \brief \ref interface \ref requirement
125751c0b2f7Stbbdev TEST_CASE("enumerable_thread_specific iterator") {
125851c0b2f7Stbbdev     TestETSIterator();
125951c0b2f7Stbbdev }
126051c0b2f7Stbbdev 
1261b15aabb3Stbbdev //! \brief \ref interface \ref requirement
1262b15aabb3Stbbdev TEST_CASE("enumerable_thread_specific iterator comparisons") {
1263b15aabb3Stbbdev     TestETSIteratorComparisons();
1264b15aabb3Stbbdev }
1265