151c0b2f7Stbbdev /*
2*13f9f32bSSergey 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 
17b15aabb3Stbbdev #if __INTEL_COMPILER && _MSC_VER
18b15aabb3Stbbdev #pragma warning(disable : 2586) // decorated name length exceeded, name was truncated
19b15aabb3Stbbdev #endif
20b15aabb3Stbbdev 
2151c0b2f7Stbbdev #include <common/test.h>
2251c0b2f7Stbbdev #include <common/spin_barrier.h>
2351c0b2f7Stbbdev #include <common/state_trackable.h>
2451c0b2f7Stbbdev #include <common/container_move_support.h>
2551c0b2f7Stbbdev #include <common/range_based_for_support.h>
2651c0b2f7Stbbdev #include <common/utils.h>
2751c0b2f7Stbbdev #include <common/utils_concurrency_limit.h>
2851c0b2f7Stbbdev #include <common/vector_types.h>
29478de5b1Stbbdev #include <common/concepts_common.h>
3051c0b2f7Stbbdev #include <tbb/concurrent_vector.h>
3151c0b2f7Stbbdev #include <tbb/tick_count.h>
3251c0b2f7Stbbdev #include <tbb/parallel_reduce.h>
3351c0b2f7Stbbdev #include <tbb/parallel_for.h>
3451c0b2f7Stbbdev #include <algorithm>
3551c0b2f7Stbbdev #include <cmath>
3651c0b2f7Stbbdev 
3751c0b2f7Stbbdev //! \file test_concurrent_vector.cpp
3851c0b2f7Stbbdev //! \brief Test for [containers.concurrent_vector] specification
3951c0b2f7Stbbdev 
TestSort()4051c0b2f7Stbbdev void TestSort() {
4151c0b2f7Stbbdev     for( int n=0; n<100; n=n*3+1 ) {
4251c0b2f7Stbbdev         tbb::concurrent_vector<int> array(n);
4351c0b2f7Stbbdev         for( int i=0; i<n; ++i ){
4451c0b2f7Stbbdev             array.at(i) = (i*7)%n;
4551c0b2f7Stbbdev         }
4651c0b2f7Stbbdev         std::sort( array.begin(), array.end() );
4751c0b2f7Stbbdev         for( int i=0; i<n; ++i ){
4851c0b2f7Stbbdev             REQUIRE( array[i]==i );
4951c0b2f7Stbbdev         }
5051c0b2f7Stbbdev     }
5151c0b2f7Stbbdev }
5251c0b2f7Stbbdev 
TestRangeBasedFor()5351c0b2f7Stbbdev void TestRangeBasedFor(){
5451c0b2f7Stbbdev     using namespace range_based_for_support_tests;
5551c0b2f7Stbbdev 
5651c0b2f7Stbbdev     using c_vector = tbb::concurrent_vector<int>;
5751c0b2f7Stbbdev     c_vector a_c_vector;
5851c0b2f7Stbbdev 
5951c0b2f7Stbbdev     const int sequence_length = 10;
6051c0b2f7Stbbdev     for (int i = 1; i<= sequence_length; ++i){
6151c0b2f7Stbbdev         a_c_vector.push_back(i);
6251c0b2f7Stbbdev     }
6351c0b2f7Stbbdev 
6451c0b2f7Stbbdev     REQUIRE_MESSAGE( range_based_for_accumulate(a_c_vector, std::plus<int>(), 0) == gauss_summ_of_int_sequence(sequence_length), "incorrect accumulated value generated via range based for ?");
6551c0b2f7Stbbdev }
6651c0b2f7Stbbdev 
6751c0b2f7Stbbdev struct default_container_traits {
6851c0b2f7Stbbdev     template <typename container_type, typename iterator_type>
construct_containerdefault_container_traits6951c0b2f7Stbbdev     static container_type& construct_container(typename std::aligned_storage<sizeof(container_type)>::type& storage, iterator_type begin, iterator_type end){
7051c0b2f7Stbbdev         container_type* ptr = reinterpret_cast<container_type*>(&storage);
7151c0b2f7Stbbdev         new (ptr) container_type(begin, end);
7251c0b2f7Stbbdev         return *ptr;
7351c0b2f7Stbbdev     }
7451c0b2f7Stbbdev 
7551c0b2f7Stbbdev     template <typename container_type, typename iterator_type, typename allocator_type>
construct_containerdefault_container_traits7651c0b2f7Stbbdev     static container_type& construct_container(typename std::aligned_storage<sizeof(container_type)>::type& storage, iterator_type begin, iterator_type end, allocator_type const& a){
7751c0b2f7Stbbdev         container_type* ptr = reinterpret_cast<container_type*>(&storage);
7851c0b2f7Stbbdev         new (ptr) container_type(begin, end, a);
7951c0b2f7Stbbdev         return *ptr;
8051c0b2f7Stbbdev     }
8151c0b2f7Stbbdev };
8251c0b2f7Stbbdev 
8351c0b2f7Stbbdev struct c_vector_type : default_container_traits {
8451c0b2f7Stbbdev     template <typename T, typename Allocator>
8551c0b2f7Stbbdev     using container_type = tbb::concurrent_vector<T, Allocator>;
8651c0b2f7Stbbdev 
8751c0b2f7Stbbdev     template <typename T>
8851c0b2f7Stbbdev     using container_value_type = T;
8951c0b2f7Stbbdev 
9051c0b2f7Stbbdev     using init_iterator_type = move_support_tests::FooIterator;
9151c0b2f7Stbbdev     template<typename element_type, typename allocator_type>
9251c0b2f7Stbbdev     struct apply{
9351c0b2f7Stbbdev         using type = tbb::concurrent_vector<element_type,  allocator_type >;
9451c0b2f7Stbbdev     };
9551c0b2f7Stbbdev 
9651c0b2f7Stbbdev     enum{ expected_number_of_items_to_allocate_for_steal_move = 0 };
9751c0b2f7Stbbdev 
9851c0b2f7Stbbdev     template<typename element_type, typename allocator_type, typename iterator>
equalc_vector_type9951c0b2f7Stbbdev     static bool equal(tbb::concurrent_vector<element_type, allocator_type > const& c, iterator begin, iterator end){
10051c0b2f7Stbbdev         bool equal_sizes = (size_t)std::distance(begin, end) == c.size();
10151c0b2f7Stbbdev         return  equal_sizes && std::equal(c.begin(), c.end(), begin);
10251c0b2f7Stbbdev     }
10351c0b2f7Stbbdev };
10451c0b2f7Stbbdev 
TestSerialGrowByWithMoveIterators()10551c0b2f7Stbbdev void TestSerialGrowByWithMoveIterators(){
10651c0b2f7Stbbdev     using fixture_t = move_support_tests::DefaultStatefulFixtureHelper<c_vector_type>::type;
10751c0b2f7Stbbdev     using vector_t = fixture_t::container_type;
10851c0b2f7Stbbdev 
10951c0b2f7Stbbdev     fixture_t fixture;
11051c0b2f7Stbbdev 
11151c0b2f7Stbbdev     vector_t dst(fixture.dst_allocator);
11251c0b2f7Stbbdev     dst.grow_by(std::make_move_iterator(fixture.source.begin()), std::make_move_iterator(fixture.source.end()));
11351c0b2f7Stbbdev 
11451c0b2f7Stbbdev     fixture.verify_content_deep_moved(dst);
11551c0b2f7Stbbdev }
11651c0b2f7Stbbdev 
11751c0b2f7Stbbdev #if HAVE_m128 || HAVE_m256
11851c0b2f7Stbbdev 
11951c0b2f7Stbbdev template<typename ClassWithVectorType>
TestVectorTypes()12051c0b2f7Stbbdev void TestVectorTypes() {
12151c0b2f7Stbbdev     tbb::concurrent_vector<ClassWithVectorType> v;
12251c0b2f7Stbbdev     for( int i = 0; i < 100; ++i ) {
12351c0b2f7Stbbdev         // VC8 does not properly align a temporary value; to work around, use explicit variable
12451c0b2f7Stbbdev         ClassWithVectorType foo(i);
12551c0b2f7Stbbdev         v.push_back(foo);
12651c0b2f7Stbbdev         for( int j=0; j<i; ++j ) {
12751c0b2f7Stbbdev             ClassWithVectorType bar(j);
12851c0b2f7Stbbdev             REQUIRE( v[j]==bar );
12951c0b2f7Stbbdev         }
13051c0b2f7Stbbdev     }
13151c0b2f7Stbbdev }
13251c0b2f7Stbbdev #endif /* HAVE_m128 | HAVE_m256 */
13351c0b2f7Stbbdev 
13451c0b2f7Stbbdev 
13551c0b2f7Stbbdev static tbb::concurrent_vector<std::size_t> Primes;
13651c0b2f7Stbbdev 
13751c0b2f7Stbbdev class FindPrimes {
is_prime(std::size_t val) const13851c0b2f7Stbbdev     bool is_prime( std::size_t val ) const {
13951c0b2f7Stbbdev         int limit, factor = 3;
14051c0b2f7Stbbdev         if( val<5u )
14151c0b2f7Stbbdev             return val==2;
14251c0b2f7Stbbdev         else {
14351c0b2f7Stbbdev             limit = long(sqrtf(float(val))+0.5f);
14451c0b2f7Stbbdev             while( factor<=limit && val % factor )
14551c0b2f7Stbbdev                 ++factor;
14651c0b2f7Stbbdev             return factor>limit;
14751c0b2f7Stbbdev         }
14851c0b2f7Stbbdev     }
14951c0b2f7Stbbdev public:
operator ()(const std::size_t idx) const15051c0b2f7Stbbdev     void operator()( const std::size_t idx ) const {
15151c0b2f7Stbbdev         if( idx % 2 && is_prime(idx) ) {
15251c0b2f7Stbbdev             Primes.push_back( idx );
15351c0b2f7Stbbdev         }
15451c0b2f7Stbbdev     }
15551c0b2f7Stbbdev };
15651c0b2f7Stbbdev 
TimeFindPrimes(std::size_t nthread)15751c0b2f7Stbbdev double TimeFindPrimes( std::size_t nthread ) {
15851c0b2f7Stbbdev     Primes.clear();
15951c0b2f7Stbbdev     const std::size_t count = 1048576;
16051c0b2f7Stbbdev     Primes.reserve(count);// TODO: or compact()?
16151c0b2f7Stbbdev     tbb::tick_count t0 = tbb::tick_count::now();
16251c0b2f7Stbbdev     std::size_t block_size = count / nthread;
16351c0b2f7Stbbdev     utils::NativeParallelFor(count, block_size, FindPrimes() );
16451c0b2f7Stbbdev     tbb::tick_count t1 = tbb::tick_count::now();
16551c0b2f7Stbbdev     return (t1-t0).seconds();
16651c0b2f7Stbbdev }
16751c0b2f7Stbbdev 
TestFindPrimes()16851c0b2f7Stbbdev void TestFindPrimes() {
16951c0b2f7Stbbdev     // Time fully subscribed run.
17051c0b2f7Stbbdev 
17151c0b2f7Stbbdev     // TimeFindPrimes( tbb::task_scheduler_init::automatic );
17251c0b2f7Stbbdev     double t2 = TimeFindPrimes( utils::get_platform_max_threads() );
17351c0b2f7Stbbdev 
17451c0b2f7Stbbdev     // Time parallel run that is very likely oversubscribed.
17551c0b2f7Stbbdev #if TBB_TEST_LOW_WORKLOAD
17651c0b2f7Stbbdev     double tx = TimeFindPrimes(32);
17751c0b2f7Stbbdev #else
17851c0b2f7Stbbdev     double tx = TimeFindPrimes(128);
17951c0b2f7Stbbdev #endif
18051c0b2f7Stbbdev     INFO("TestFindPrimes: t2 == " << t2 << " tx == " << tx << "k == " << tx / t2);
18151c0b2f7Stbbdev 
18251c0b2f7Stbbdev     // We allow the X-thread run a little extra time to allow for thread overhead.
18351c0b2f7Stbbdev     // Theoretically, following test will fail on machine with >X processors.
18451c0b2f7Stbbdev     // But that situation is not going to come up in the near future,
18551c0b2f7Stbbdev     // and the generalization to fix the issue is not worth the trouble.
18651c0b2f7Stbbdev     WARN_MESSAGE( tx <= 1.3*t2, "Warning: grow_by is pathetically slow");
18751c0b2f7Stbbdev     INFO("t2 == " << t2 << " tx == " << tx << "k == " << tx / t2);
18851c0b2f7Stbbdev }
18951c0b2f7Stbbdev 
19051c0b2f7Stbbdev template <typename Type, typename Allocator>
19151c0b2f7Stbbdev class test_grow_by_and_resize {
19251c0b2f7Stbbdev     tbb::concurrent_vector<Type, Allocator> &my_c;
19351c0b2f7Stbbdev public:
test_grow_by_and_resize(tbb::concurrent_vector<Type,Allocator> & c)19451c0b2f7Stbbdev     test_grow_by_and_resize( tbb::concurrent_vector<Type, Allocator> &c ) : my_c(c) {}
operator ()() const19551c0b2f7Stbbdev     void operator()() const {
19651c0b2f7Stbbdev         const typename tbb::concurrent_vector<Type, Allocator>::size_type sz = my_c.size();
19751c0b2f7Stbbdev         my_c.grow_by( 5 );
19851c0b2f7Stbbdev         REQUIRE( my_c.size() == sz + 5 );
19951c0b2f7Stbbdev         my_c.resize( sz );
20051c0b2f7Stbbdev         REQUIRE( my_c.size() == sz );
20151c0b2f7Stbbdev     }
20251c0b2f7Stbbdev };
20351c0b2f7Stbbdev 
test_scoped_allocator()20451c0b2f7Stbbdev void test_scoped_allocator() {
20551c0b2f7Stbbdev     using allocator_data_type = AllocatorAwareData<std::scoped_allocator_adaptor<std::allocator<int>>>;
20651c0b2f7Stbbdev     using allocator_type = std::scoped_allocator_adaptor<std::allocator<allocator_data_type>>;
20751c0b2f7Stbbdev     using container_type = tbb::concurrent_vector<allocator_data_type, allocator_type>;
20851c0b2f7Stbbdev 
20951c0b2f7Stbbdev     allocator_type allocator;
21051c0b2f7Stbbdev     allocator_data_type data1(1, allocator);
21151c0b2f7Stbbdev     allocator_data_type data2(2, allocator);
21251c0b2f7Stbbdev 
21351c0b2f7Stbbdev     auto init_list = {data1, data2};
21451c0b2f7Stbbdev 
21551c0b2f7Stbbdev     container_type c1(allocator), c2(allocator);
21651c0b2f7Stbbdev 
21751c0b2f7Stbbdev     allocator_data_type::activate();
21851c0b2f7Stbbdev 
21951c0b2f7Stbbdev     c1.grow_by(100);
22051c0b2f7Stbbdev     c1.grow_by(10, data1);
22151c0b2f7Stbbdev     c1.grow_by(init_list.begin(), init_list.end());
22251c0b2f7Stbbdev     c1.grow_by(init_list);
22351c0b2f7Stbbdev 
22451c0b2f7Stbbdev     c1.clear();
22551c0b2f7Stbbdev 
22651c0b2f7Stbbdev     c1.grow_to_at_least(100);
22751c0b2f7Stbbdev     c1.grow_to_at_least(110, data1);
22851c0b2f7Stbbdev 
22951c0b2f7Stbbdev     c1.clear();
23051c0b2f7Stbbdev 
23151c0b2f7Stbbdev     c1.push_back(data1);
23251c0b2f7Stbbdev     c1.push_back(data2);
23351c0b2f7Stbbdev     c1.push_back(std::move(data1));
23451c0b2f7Stbbdev     c1.emplace_back(1);
23551c0b2f7Stbbdev 
23651c0b2f7Stbbdev     c1.clear();
23751c0b2f7Stbbdev 
23851c0b2f7Stbbdev     c1.reserve(100);
23951c0b2f7Stbbdev     c1.resize(110);
24051c0b2f7Stbbdev     c1.resize(100);
24151c0b2f7Stbbdev     c1.resize(110, data1);
24251c0b2f7Stbbdev     c1.resize(100, data1);
24351c0b2f7Stbbdev 
24451c0b2f7Stbbdev     c1.shrink_to_fit();
24551c0b2f7Stbbdev 
24651c0b2f7Stbbdev     c1.clear();
24751c0b2f7Stbbdev 
24851c0b2f7Stbbdev     c1.grow_by(10, data1);
24951c0b2f7Stbbdev     c2.grow_by(20, data2);
25051c0b2f7Stbbdev 
25151c0b2f7Stbbdev     c1 = c2;
25251c0b2f7Stbbdev     c2 = std::move(c1);
25351c0b2f7Stbbdev 
25451c0b2f7Stbbdev     allocator_data_type::deactivate();
25551c0b2f7Stbbdev }
25651c0b2f7Stbbdev 
25751c0b2f7Stbbdev template <bool default_construction_present> struct do_default_construction_test {
operator ()do_default_construction_test25851c0b2f7Stbbdev     template<typename FuncType> void operator() ( FuncType func ) const { func(); }
25951c0b2f7Stbbdev };
26051c0b2f7Stbbdev template <> struct do_default_construction_test<false> {
operator ()do_default_construction_test26151c0b2f7Stbbdev     template<typename FuncType> void operator()( FuncType ) const {}
26251c0b2f7Stbbdev };
26351c0b2f7Stbbdev 
26451c0b2f7Stbbdev template <typename Type, typename Allocator>
CompareVectors(const tbb::concurrent_vector<Type,Allocator> & c1,const tbb::concurrent_vector<Type,Allocator> & c2)26551c0b2f7Stbbdev void CompareVectors( const tbb::concurrent_vector<Type, Allocator> &c1, const tbb::concurrent_vector<Type, Allocator> &c2 ) {
26651c0b2f7Stbbdev     REQUIRE( (!(c1 == c2) && c1 != c2) );
26751c0b2f7Stbbdev     REQUIRE( (c1 <= c2 && c1 < c2 && c2 >= c1 && c2 > c1) );
26851c0b2f7Stbbdev }
26951c0b2f7Stbbdev 
27051c0b2f7Stbbdev template <typename Type, typename Allocator>
CompareVectors(const tbb::concurrent_vector<std::weak_ptr<Type>,Allocator> &,const tbb::concurrent_vector<std::weak_ptr<Type>,Allocator> &)27151c0b2f7Stbbdev void CompareVectors( const tbb::concurrent_vector<std::weak_ptr<Type>, Allocator> &, const tbb::concurrent_vector<std::weak_ptr<Type>, Allocator> & ) {
27251c0b2f7Stbbdev     /* do nothing for std::weak_ptr */
27351c0b2f7Stbbdev }
27451c0b2f7Stbbdev 
27551c0b2f7Stbbdev template <bool default_construction_present, typename Type, typename Allocator>
Examine(tbb::concurrent_vector<Type,Allocator> c,const std::vector<Type> & vec)27651c0b2f7Stbbdev void Examine( tbb::concurrent_vector<Type, Allocator> c, const std::vector<Type> &vec ) {
27751c0b2f7Stbbdev     using vector_t = tbb::concurrent_vector<Type, Allocator>;
27851c0b2f7Stbbdev     using size_type_t = typename vector_t::size_type;
27951c0b2f7Stbbdev 
28051c0b2f7Stbbdev 
28151c0b2f7Stbbdev     REQUIRE( c.size() == vec.size() );
28251c0b2f7Stbbdev     for ( size_type_t i=0; i<c.size(); ++i ) {
28351c0b2f7Stbbdev         REQUIRE( utils::IsEqual()(c[i], vec[i]) );
28451c0b2f7Stbbdev     }
28551c0b2f7Stbbdev     do_default_construction_test<default_construction_present>()(test_grow_by_and_resize<Type,Allocator>(c));
28651c0b2f7Stbbdev     c.grow_by( size_type_t(5), c[0] );
28751c0b2f7Stbbdev     c.grow_to_at_least( c.size()+5, c.at(0) );
28851c0b2f7Stbbdev     vector_t c2;
28951c0b2f7Stbbdev     c2.reserve( 5 );
29051c0b2f7Stbbdev     std::copy( c.begin(), c.begin() + 5, std::back_inserter( c2 ) );
29151c0b2f7Stbbdev 
29251c0b2f7Stbbdev     c.grow_by( c2.begin(), c2.end() );
29351c0b2f7Stbbdev     const vector_t& cvcr = c;
29451c0b2f7Stbbdev     REQUIRE( utils::IsEqual()(cvcr.front(), *(c2.rend()-1)) );
29551c0b2f7Stbbdev     REQUIRE( utils::IsEqual()(cvcr.back(), *c2.rbegin()));
29651c0b2f7Stbbdev     REQUIRE( utils::IsEqual()(*c.cbegin(), *(c.crend()-1)) );
29751c0b2f7Stbbdev     REQUIRE( utils::IsEqual()(*(c.cend()-1), *c.crbegin()) );
29851c0b2f7Stbbdev     c.swap( c2 );
29951c0b2f7Stbbdev     REQUIRE( c.size() == 5 );
30051c0b2f7Stbbdev     CompareVectors( c, c2 );
30151c0b2f7Stbbdev     c.swap( c2 );
30251c0b2f7Stbbdev     c2.clear();
30351c0b2f7Stbbdev     REQUIRE( c2.size() == 0 );
30451c0b2f7Stbbdev     c2.shrink_to_fit();
30551c0b2f7Stbbdev     Allocator a = c.get_allocator();
30651c0b2f7Stbbdev     a.deallocate( a.allocate(1), 1 );
30751c0b2f7Stbbdev }
30851c0b2f7Stbbdev 
30951c0b2f7Stbbdev template <typename Type>
31051c0b2f7Stbbdev class test_default_construction {
31151c0b2f7Stbbdev     const std::vector<Type> &my_vec;
31251c0b2f7Stbbdev public:
test_default_construction(const std::vector<Type> & vec)31351c0b2f7Stbbdev     test_default_construction( const std::vector<Type> &vec ) : my_vec(vec) {}
operator ()() const31451c0b2f7Stbbdev     void operator()() const {
31551c0b2f7Stbbdev         // Construction with initial size specified by argument n.
31651c0b2f7Stbbdev         tbb::concurrent_vector<Type> c7( my_vec.size() );
31751c0b2f7Stbbdev         std::copy( my_vec.begin(), my_vec.end(), c7.begin() );
31851c0b2f7Stbbdev         Examine</*default_construction_present = */true>( c7, my_vec );
31951c0b2f7Stbbdev         tbb::concurrent_vector< Type, std::allocator<Type> > c8( my_vec.size() );
32051c0b2f7Stbbdev         std::copy( c7.begin(), c7.end(), c8.begin() );
32151c0b2f7Stbbdev         Examine</*default_construction_present = */true>( c8, my_vec );
32251c0b2f7Stbbdev     }
32351c0b2f7Stbbdev };
32451c0b2f7Stbbdev 
32551c0b2f7Stbbdev template <bool default_construction_present, typename Type>
TypeTester(const std::vector<Type> & vec)32651c0b2f7Stbbdev void TypeTester( const std::vector<Type> &vec ) {
32751c0b2f7Stbbdev     __TBB_ASSERT( vec.size() >= 5, "Array should have at least 5 elements" );
32851c0b2f7Stbbdev     // Construct empty vector.
32951c0b2f7Stbbdev     tbb::concurrent_vector<Type> c1;
33051c0b2f7Stbbdev     std::copy( vec.begin(), vec.end(), std::back_inserter(c1) );
33151c0b2f7Stbbdev     Examine<default_construction_present>( c1, vec );
33251c0b2f7Stbbdev     // Constructor from initializer_list.
33351c0b2f7Stbbdev     tbb::concurrent_vector<Type> c2({vec[0],vec[1],vec[2]});
33451c0b2f7Stbbdev     std::copy( vec.begin()+3, vec.end(), std::back_inserter(c2) );
33551c0b2f7Stbbdev     Examine<default_construction_present>( c2, vec );
33651c0b2f7Stbbdev     // Copying constructor.
33751c0b2f7Stbbdev     tbb::concurrent_vector<Type> c3(c1);
33851c0b2f7Stbbdev     Examine<default_construction_present>( c3, vec );
33951c0b2f7Stbbdev     // Construct with non-default allocator
34051c0b2f7Stbbdev     tbb::concurrent_vector< Type, std::allocator<Type> > c4;
34151c0b2f7Stbbdev     std::copy( vec.begin(), vec.end(), std::back_inserter(c4) );
34251c0b2f7Stbbdev     Examine<default_construction_present>( c4, vec );
34351c0b2f7Stbbdev     // Construction with initial size specified by argument n.
34451c0b2f7Stbbdev     do_default_construction_test<default_construction_present>()(test_default_construction<Type>(vec));
34551c0b2f7Stbbdev     // Construction with initial size specified by argument n, initialization by copying of t, and given allocator instance.
34651c0b2f7Stbbdev     std::allocator<Type> allocator;
34751c0b2f7Stbbdev     tbb::concurrent_vector< Type, std::allocator<Type> > c9(vec.size(), vec[1], allocator);
34851c0b2f7Stbbdev     Examine<default_construction_present>( c9, std::vector<Type>(vec.size(), vec[1]) );
34951c0b2f7Stbbdev     // Construction with copying iteration range and given allocator instance.
35051c0b2f7Stbbdev     tbb::concurrent_vector< Type, std::allocator<Type> > c10(c1.begin(), c1.end(), allocator);
35151c0b2f7Stbbdev     Examine<default_construction_present>( c10, vec );
35251c0b2f7Stbbdev     tbb::concurrent_vector<Type> c11(vec.begin(), vec.end());
35351c0b2f7Stbbdev     Examine<default_construction_present>( c11, vec );
35451c0b2f7Stbbdev }
35551c0b2f7Stbbdev 
TestTypes()35651c0b2f7Stbbdev void TestTypes() {
35751c0b2f7Stbbdev     const int NUMBER = 100;
35851c0b2f7Stbbdev 
35951c0b2f7Stbbdev     std::vector<int> intArr;
36051c0b2f7Stbbdev     for ( int i=0; i<NUMBER; ++i ) intArr.push_back(i);
36151c0b2f7Stbbdev     TypeTester</*default_construction_present = */true>( intArr );
36251c0b2f7Stbbdev 
36351c0b2f7Stbbdev     std::vector< std::reference_wrapper<int> > refArr;
36451c0b2f7Stbbdev     // The constructor of std::reference_wrapper<T> from T& is explicit in some versions of libstdc++.
36551c0b2f7Stbbdev     for ( int i=0; i<NUMBER; ++i ) refArr.push_back( std::reference_wrapper<int>(intArr[i]) );
36651c0b2f7Stbbdev     TypeTester</*default_construction_present = */false>( refArr );
36751c0b2f7Stbbdev 
36851c0b2f7Stbbdev     // std::vector< std::atomic<int> > tbbIntArr( NUMBER ); //TODO compilation error
36951c0b2f7Stbbdev     // for ( int i=0; i<NUMBER; ++i ) tbbIntArr[i] = i;
37051c0b2f7Stbbdev     // TypeTester</*default_construction_present = */true>( tbbIntArr );
37151c0b2f7Stbbdev 
37251c0b2f7Stbbdev     std::vector< std::shared_ptr<int> > shrPtrArr;
37351c0b2f7Stbbdev     for ( int i=0; i<NUMBER; ++i ) shrPtrArr.push_back( std::make_shared<int>(i) );
37451c0b2f7Stbbdev     TypeTester</*default_construction_present = */true>( shrPtrArr );
37551c0b2f7Stbbdev 
37651c0b2f7Stbbdev     std::vector< std::weak_ptr<int> > wkPtrArr;
37751c0b2f7Stbbdev     std::copy( shrPtrArr.begin(), shrPtrArr.end(), std::back_inserter(wkPtrArr) );
37851c0b2f7Stbbdev     TypeTester</*default_construction_present = */true>( wkPtrArr );
37951c0b2f7Stbbdev }
38051c0b2f7Stbbdev 
38151c0b2f7Stbbdev template <typename Vector>
test_grow_by_empty_range(Vector & v,typename Vector::value_type * range_begin_end)38251c0b2f7Stbbdev void test_grow_by_empty_range( Vector &v, typename Vector::value_type* range_begin_end ) {
38351c0b2f7Stbbdev     const Vector v_copy = v;
38451c0b2f7Stbbdev     REQUIRE_MESSAGE( (v.grow_by( range_begin_end, range_begin_end ) == v.end()), "grow_by(empty_range) returned a wrong iterator." );
38551c0b2f7Stbbdev     REQUIRE_MESSAGE( v == v_copy, "grow_by(empty_range) has changed the vector." );
38651c0b2f7Stbbdev }
38751c0b2f7Stbbdev 
TestSerialGrowByRange(bool fragmented_vector)38851c0b2f7Stbbdev void TestSerialGrowByRange( bool fragmented_vector ) {
38951c0b2f7Stbbdev     tbb::concurrent_vector<int> v;
39051c0b2f7Stbbdev     if ( fragmented_vector ) {
39151c0b2f7Stbbdev         v.reserve( 1 );
39251c0b2f7Stbbdev     }
39351c0b2f7Stbbdev     int init_range[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
39451c0b2f7Stbbdev     REQUIRE_MESSAGE( (v.grow_by( init_range, init_range + (utils::array_length( init_range )) ) == v.begin()), "grow_by(I,I) returned a wrong iterator." );
39551c0b2f7Stbbdev     REQUIRE_MESSAGE( std::equal( v.begin(), v.end(), init_range ), "grow_by(I,I) did not properly copied all elements ?" );
39651c0b2f7Stbbdev     test_grow_by_empty_range( v, init_range );
39751c0b2f7Stbbdev     test_grow_by_empty_range( v, (int*)nullptr );
39851c0b2f7Stbbdev }
39951c0b2f7Stbbdev 
40051c0b2f7Stbbdev template <typename allocator_type>
TestConcurrentOperationsWithUnSafeOperations(std::size_t threads_number)40151c0b2f7Stbbdev void TestConcurrentOperationsWithUnSafeOperations(std::size_t threads_number) {
40251c0b2f7Stbbdev     using vector_type = tbb::concurrent_vector<move_support_tests::Foo, allocator_type>;
40351c0b2f7Stbbdev 
40451c0b2f7Stbbdev     vector_type vector;
40551c0b2f7Stbbdev 
40651c0b2f7Stbbdev     constexpr std::size_t max_operations = 1000;
40751c0b2f7Stbbdev     std::atomic<int> curr_unsafe_thread{-1};
40851c0b2f7Stbbdev     // 0 - is safe operations
40951c0b2f7Stbbdev     // 1 - is shrink_to_fit
41051c0b2f7Stbbdev     // 2 - is clear + shrink_to_fit
41151c0b2f7Stbbdev     // 3 - is resize
41251c0b2f7Stbbdev     std::vector<std::size_t> operations(std::size_t(max_operations * 0.95), 0);
41351c0b2f7Stbbdev     utils::FastRandom<> op_rand(42);
41451c0b2f7Stbbdev     for (std::size_t i = std::size_t(max_operations * 0.95); i < max_operations; ++i) {
41551c0b2f7Stbbdev         std::size_t random_operation = op_rand.get() % 3;
41651c0b2f7Stbbdev         operations.push_back(random_operation + 1);
41751c0b2f7Stbbdev     }
41851c0b2f7Stbbdev 
419b15aabb3Stbbdev     // Array of active threads
420b15aabb3Stbbdev     std::unique_ptr<std::atomic<int>[]> active_threads{ new std::atomic<int>[threads_number]() };
42151c0b2f7Stbbdev     // If thread still have i < max_operations than in array will be false
42251c0b2f7Stbbdev     // When some thread finish it operation, set true in active_thread on thread_id position and start executing only safe operations
42351c0b2f7Stbbdev     // Than wait all threads
42451c0b2f7Stbbdev     // When all threads is finish their operations, all thread exit from lambda
425b15aabb3Stbbdev     auto all_done = [&active_threads, threads_number] {
426b15aabb3Stbbdev         for (std::size_t i = 0; i < threads_number; ++i) {
427b15aabb3Stbbdev             if (active_threads[i].load(std::memory_order_relaxed) == 0) return false;
42851c0b2f7Stbbdev         }
42951c0b2f7Stbbdev         return true;
43051c0b2f7Stbbdev     };
43151c0b2f7Stbbdev 
43251c0b2f7Stbbdev     // Need double synchronization to correct work
433b15aabb3Stbbdev     std::unique_ptr<std::atomic<int>[]> ready_threads{ new std::atomic<int>[threads_number]() };
434b15aabb3Stbbdev     auto all_ready_leave = [&ready_threads, threads_number] {
435b15aabb3Stbbdev         for (std::size_t i = 0; i < threads_number; ++i) {
436b15aabb3Stbbdev             if (ready_threads[i].load(std::memory_order_relaxed) == 0) return false;
43751c0b2f7Stbbdev         }
43851c0b2f7Stbbdev         return true;
43951c0b2f7Stbbdev     };
44051c0b2f7Stbbdev 
44151c0b2f7Stbbdev     utils::SpinBarrier barrier(threads_number);
442b15aabb3Stbbdev     auto concurrent_func = [&operations, &vector, &curr_unsafe_thread, &barrier, &all_done, &active_threads,
44351c0b2f7Stbbdev                             &all_ready_leave, &ready_threads] (std::size_t thread_id)
44451c0b2f7Stbbdev     {
445b15aabb3Stbbdev         std::vector<std::size_t> local_operations(operations);
44651c0b2f7Stbbdev         utils::FastRandom<> rand(thread_id);
44751c0b2f7Stbbdev         // std::shuffle doesn't work with msvc2017 and FastRandom
448b15aabb3Stbbdev         for (std::size_t i = local_operations.size(); i > 1; --i) {
449b15aabb3Stbbdev             std::size_t j = rand.get() % i;
450b15aabb3Stbbdev             std::swap(local_operations[i - 1], local_operations[j]);
45151c0b2f7Stbbdev         }
45251c0b2f7Stbbdev 
45351c0b2f7Stbbdev         std::size_t i = 0;
45451c0b2f7Stbbdev         do {
45551c0b2f7Stbbdev             if (all_done()) ready_threads[thread_id] = 1;
45651c0b2f7Stbbdev             if (curr_unsafe_thread.load() != -1) {
45751c0b2f7Stbbdev                     // If lock taken, wait
45851c0b2f7Stbbdev                     // First wait unblock unsafe thread
45951c0b2f7Stbbdev                     // Second wait finish unsafe operations
46051c0b2f7Stbbdev                     barrier.wait();
46151c0b2f7Stbbdev                     barrier.wait();
46251c0b2f7Stbbdev             }
46351c0b2f7Stbbdev             // Is safe operation
464b15aabb3Stbbdev             if (active_threads[thread_id] == 1 || local_operations[i] == 0) {
46551c0b2f7Stbbdev                 // If lock is free, perform various operations
46651c0b2f7Stbbdev                 std::size_t random_operation = rand.get() % 3;
46751c0b2f7Stbbdev                 switch (random_operation) {
46851c0b2f7Stbbdev                     case 0:
46951c0b2f7Stbbdev                         {
47051c0b2f7Stbbdev                             vector.push_back(1);
47151c0b2f7Stbbdev                         }
47251c0b2f7Stbbdev                         break;
47351c0b2f7Stbbdev                     case 1:
47451c0b2f7Stbbdev                         {
47551c0b2f7Stbbdev                             std::size_t grow_size = rand.get() % 100;
47651c0b2f7Stbbdev                             vector.grow_by(grow_size, 1);
47751c0b2f7Stbbdev                         }
47851c0b2f7Stbbdev                         break;
47951c0b2f7Stbbdev                     case 2:
48051c0b2f7Stbbdev                         {
48151c0b2f7Stbbdev                             std::size_t grow_at_least_size = vector.size() + rand.get() % 100;
48251c0b2f7Stbbdev                             vector.grow_to_at_least(grow_at_least_size, 1);
48351c0b2f7Stbbdev                         }
48451c0b2f7Stbbdev                         break;
48551c0b2f7Stbbdev                 }
48651c0b2f7Stbbdev             } else {
48751c0b2f7Stbbdev                 int default_unsafe_thread = -1;
48851c0b2f7Stbbdev                 if (curr_unsafe_thread.compare_exchange_strong(default_unsafe_thread, int(thread_id))) {
48951c0b2f7Stbbdev                     barrier.wait();
49051c0b2f7Stbbdev                     // All threads are blocked we can execute our unsafe operation
491b15aabb3Stbbdev                     switch (local_operations[i]) {
49251c0b2f7Stbbdev                         case 1:
49351c0b2f7Stbbdev                             vector.shrink_to_fit();
49451c0b2f7Stbbdev                             break;
49551c0b2f7Stbbdev                         case 2:
49651c0b2f7Stbbdev                             {
49751c0b2f7Stbbdev                                 vector.clear();
49851c0b2f7Stbbdev                                 vector.shrink_to_fit();
49951c0b2f7Stbbdev                             }
50051c0b2f7Stbbdev                             break;
50151c0b2f7Stbbdev                         case 3:
50251c0b2f7Stbbdev                             {
50351c0b2f7Stbbdev                                 vector.resize(0);
50451c0b2f7Stbbdev                             }
50551c0b2f7Stbbdev                             break;
50651c0b2f7Stbbdev                     }
50751c0b2f7Stbbdev                     curr_unsafe_thread = -1;
50851c0b2f7Stbbdev                     barrier.wait();
50951c0b2f7Stbbdev                 }
51051c0b2f7Stbbdev             }
51151c0b2f7Stbbdev             ++i;
512b15aabb3Stbbdev             if (i >= local_operations.size()) active_threads[thread_id] = 1;
51351c0b2f7Stbbdev         } while (!all_ready_leave() || !all_done());
51451c0b2f7Stbbdev     };
51551c0b2f7Stbbdev 
51651c0b2f7Stbbdev     utils::NativeParallelFor(threads_number, concurrent_func);
51751c0b2f7Stbbdev 
51851c0b2f7Stbbdev     vector.clear();
51951c0b2f7Stbbdev     vector.shrink_to_fit();
52051c0b2f7Stbbdev }
52151c0b2f7Stbbdev 
52251c0b2f7Stbbdev template <typename RangeType>
reduce_vector(RangeType test_range)52351c0b2f7Stbbdev int reduce_vector(RangeType test_range) {
52451c0b2f7Stbbdev     return tbb::parallel_reduce(test_range, 0,
52551c0b2f7Stbbdev         [] ( const RangeType& range, int sum ) {
52651c0b2f7Stbbdev             for (auto it = range.begin(); it != range.end(); ++it) {
52751c0b2f7Stbbdev                 sum += *it;
52851c0b2f7Stbbdev             }
52951c0b2f7Stbbdev 
53051c0b2f7Stbbdev             return sum;
53151c0b2f7Stbbdev         },
53251c0b2f7Stbbdev         [] ( const int& lhs, const int& rhs) {
53351c0b2f7Stbbdev             return lhs + rhs;
53451c0b2f7Stbbdev         }
53551c0b2f7Stbbdev     );
53651c0b2f7Stbbdev }
53751c0b2f7Stbbdev 
53851c0b2f7Stbbdev //! Test the grow_by on range
53951c0b2f7Stbbdev //! \brief \ref interface \ref requirement
54051c0b2f7Stbbdev TEST_CASE("testing serial grow_by range"){
54151c0b2f7Stbbdev     TestSerialGrowByRange(/*fragmented_vector = */false);
54251c0b2f7Stbbdev     TestSerialGrowByRange(/*fragmented_vector = */true);
54351c0b2f7Stbbdev }
54451c0b2f7Stbbdev 
54551c0b2f7Stbbdev //! Test of push_back operation
54651c0b2f7Stbbdev //! \brief \ref interface
54751c0b2f7Stbbdev TEST_CASE("testing range based for support"){
54851c0b2f7Stbbdev     TestRangeBasedFor();
54951c0b2f7Stbbdev }
55051c0b2f7Stbbdev 
55151c0b2f7Stbbdev //! Test of work STL algorithms  with concurrent_vector iterator.
55251c0b2f7Stbbdev //! \brief \ref interface
55351c0b2f7Stbbdev TEST_CASE("testing sort"){
55451c0b2f7Stbbdev     TestSort();
55551c0b2f7Stbbdev }
55651c0b2f7Stbbdev 
55751c0b2f7Stbbdev //! Test concurrent_vector with vector types
55851c0b2f7Stbbdev //! \brief \ref error_guessing
55951c0b2f7Stbbdev TEST_CASE("testing concurrent_vector with vector types"){
56051c0b2f7Stbbdev #if HAVE_m128
56151c0b2f7Stbbdev     TestVectorTypes<ClassWithSSE>();
56251c0b2f7Stbbdev #endif
56351c0b2f7Stbbdev #if HAVE_m256
56451c0b2f7Stbbdev     if (have_AVX()) TestVectorTypes<ClassWithAVX>();
56551c0b2f7Stbbdev #endif
56651c0b2f7Stbbdev }
56751c0b2f7Stbbdev 
56851c0b2f7Stbbdev //! Test concurrent push_back operation
56951c0b2f7Stbbdev //! \brief \ref error_guessing
57051c0b2f7Stbbdev TEST_CASE("testing find primes"){
57151c0b2f7Stbbdev     TestFindPrimes();
57251c0b2f7Stbbdev }
57351c0b2f7Stbbdev 
57451c0b2f7Stbbdev //! Test concurrent_vector with std::scoped_allocator_adaptor
57551c0b2f7Stbbdev //! \brief \ref error_guessing
57651c0b2f7Stbbdev TEST_CASE("test concurrent_vector with std::scoped_allocator_adaptor") {
57751c0b2f7Stbbdev     test_scoped_allocator();
57851c0b2f7Stbbdev }
57951c0b2f7Stbbdev 
58051c0b2f7Stbbdev //! Test type of vector
58151c0b2f7Stbbdev //! \brief \ref requirement
58251c0b2f7Stbbdev TEST_CASE("testing types"){
58351c0b2f7Stbbdev     TestTypes();
58451c0b2f7Stbbdev }
58551c0b2f7Stbbdev 
58651c0b2f7Stbbdev //! Test concurrent and unsafe operations
58751c0b2f7Stbbdev //! \brief \ref regression \ref error_guessing
58851c0b2f7Stbbdev TEST_CASE("Work without hang") {
58951c0b2f7Stbbdev     using allocator_type = StaticSharedCountingAllocator<std::allocator<move_support_tests::Foo>>;
59051c0b2f7Stbbdev     std::size_t max_threads = utils::get_platform_max_threads() - 1;
59151c0b2f7Stbbdev 
59251c0b2f7Stbbdev     for (std::size_t threads = 1; threads < max_threads; threads = std::size_t(double(threads) * 2.7)) {
59351c0b2f7Stbbdev         allocator_type::init_counters();
59451c0b2f7Stbbdev         TestConcurrentOperationsWithUnSafeOperations<allocator_type>(threads);
59551c0b2f7Stbbdev 
59651c0b2f7Stbbdev         REQUIRE( allocator_type::allocations == allocator_type::frees );
59751c0b2f7Stbbdev         REQUIRE( allocator_type::items_allocated == allocator_type::items_freed );
59851c0b2f7Stbbdev         REQUIRE( allocator_type::items_constructed == allocator_type::items_destroyed );
59951c0b2f7Stbbdev     }
60051c0b2f7Stbbdev }
60151c0b2f7Stbbdev 
60251c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
60351c0b2f7Stbbdev //! Whitebox test for segment table extension
60451c0b2f7Stbbdev //! \brief \ref regression \ref error_guessing
60551c0b2f7Stbbdev TEST_CASE("Whitebox test for segment table extension") {
60651c0b2f7Stbbdev     using allocator_type = StaticSharedCountingAllocator<std::allocator<move_support_tests::Foo>>;
60751c0b2f7Stbbdev     using vector_type = tbb::concurrent_vector<move_support_tests::Foo, allocator_type>;
60851c0b2f7Stbbdev 
60951c0b2f7Stbbdev     std::size_t max_number_of_elements_in_embedded = 12;
61051c0b2f7Stbbdev 
61151c0b2f7Stbbdev     for (std::size_t i = 3; i < max_number_of_elements_in_embedded; i += 3) {
61251c0b2f7Stbbdev         vector_type vector;
61351c0b2f7Stbbdev         allocator_type::init_counters();
61451c0b2f7Stbbdev         allocator_type::set_limits(std::size_t(1) << (i + 1));
61551c0b2f7Stbbdev 
61651c0b2f7Stbbdev         try {
61751c0b2f7Stbbdev             for (std::size_t j = 0; j < std::size_t(1) << i; ++j) {
61851c0b2f7Stbbdev                 vector.push_back(1);
61951c0b2f7Stbbdev             }
62051c0b2f7Stbbdev             vector.grow_by(1000);
62151c0b2f7Stbbdev         } catch (std::bad_alloc& ) {
62251c0b2f7Stbbdev             allocator_type::set_limits();
62351c0b2f7Stbbdev             vector_type copy_of_vector(vector);
62451c0b2f7Stbbdev             vector_type copy_of_copy(copy_of_vector);
62551c0b2f7Stbbdev             vector_type assigned_vector;
62651c0b2f7Stbbdev             assigned_vector = vector;
62751c0b2f7Stbbdev             REQUIRE(copy_of_vector == copy_of_copy);
62851c0b2f7Stbbdev             REQUIRE(assigned_vector == copy_of_copy);
62951c0b2f7Stbbdev         }
63051c0b2f7Stbbdev     }
63151c0b2f7Stbbdev }
63251c0b2f7Stbbdev 
63351c0b2f7Stbbdev //! Test exception in constructors
63451c0b2f7Stbbdev //! \brief \ref regression \ref error_guessing
63551c0b2f7Stbbdev TEST_CASE("Test exception in constructors") {
63651c0b2f7Stbbdev     using allocator_type = StaticSharedCountingAllocator<std::allocator<double>>;
63751c0b2f7Stbbdev     using vector_type = tbb::concurrent_vector<double, allocator_type>;
63851c0b2f7Stbbdev 
63951c0b2f7Stbbdev     allocator_type::set_limits(1);
64051c0b2f7Stbbdev 
__anon5f703c9f0702null64151c0b2f7Stbbdev     REQUIRE_THROWS_AS( [] {
64251c0b2f7Stbbdev         vector_type vec1(42, 42.);
64351c0b2f7Stbbdev         utils::suppress_unused_warning(vec1);
64451c0b2f7Stbbdev     }(), const std::bad_alloc);
64551c0b2f7Stbbdev 
64651c0b2f7Stbbdev     auto list = { 42., 42., 42., 42., 42., 42., 42., 42., 42., 42. };
__anon5f703c9f0802null64751c0b2f7Stbbdev     REQUIRE_THROWS_AS( [&] {
64851c0b2f7Stbbdev         vector_type vec2(list.begin(), list.end());
64951c0b2f7Stbbdev         utils::suppress_unused_warning(vec2);
65051c0b2f7Stbbdev     }(), const std::bad_alloc);
65151c0b2f7Stbbdev 
65251c0b2f7Stbbdev     allocator_type::init_counters();
65351c0b2f7Stbbdev     allocator_type::set_limits(0);
65451c0b2f7Stbbdev     vector_type src_vec(42, 42.);
65551c0b2f7Stbbdev     allocator_type::set_limits(1);
65651c0b2f7Stbbdev 
__anon5f703c9f0902null65751c0b2f7Stbbdev     REQUIRE_THROWS_AS( [&] {
65851c0b2f7Stbbdev         vector_type vec3(src_vec, allocator_type{});
65951c0b2f7Stbbdev         utils::suppress_unused_warning(vec3);
66051c0b2f7Stbbdev     }(), const std::bad_alloc);
66151c0b2f7Stbbdev }
66251c0b2f7Stbbdev #endif // TBB_USE_EXCEPTIONS
66351c0b2f7Stbbdev 
66451c0b2f7Stbbdev //! \brief \ref regression \ref error_guessing
66551c0b2f7Stbbdev TEST_CASE("Reducing concurrent_vector") {
66651c0b2f7Stbbdev     constexpr int final_sum = 100000;
66751c0b2f7Stbbdev     tbb::concurrent_vector<int> vec(final_sum, 1);
66851c0b2f7Stbbdev     const tbb::concurrent_vector<int> cvec(vec);
66951c0b2f7Stbbdev 
67051c0b2f7Stbbdev     CHECK(reduce_vector(vec.range()) == final_sum);
67151c0b2f7Stbbdev     CHECK(reduce_vector(cvec.range()) == final_sum);
67251c0b2f7Stbbdev }
67351c0b2f7Stbbdev 
67451c0b2f7Stbbdev 
67551c0b2f7Stbbdev //! \brief \ref error_guessing
67651c0b2f7Stbbdev TEST_CASE("swap with not always equal allocators"){
67751c0b2f7Stbbdev     using allocator_type = NotAlwaysEqualAllocator<int>;
67851c0b2f7Stbbdev     using vector_type = tbb::concurrent_vector<int, allocator_type>;
67951c0b2f7Stbbdev 
68051c0b2f7Stbbdev     vector_type vec1{};
68151c0b2f7Stbbdev     vector_type vec2(42, 42);
68251c0b2f7Stbbdev 
68351c0b2f7Stbbdev     swap(vec1, vec2);
68451c0b2f7Stbbdev 
68551c0b2f7Stbbdev     CHECK(vec2.empty());
68651c0b2f7Stbbdev }
68751c0b2f7Stbbdev 
68851c0b2f7Stbbdev // The problem was that after allocating first_block,
68951c0b2f7Stbbdev // no write was made to the embedded table.
69051c0b2f7Stbbdev // Also, two threads could be in the table extension section at once.
69151c0b2f7Stbbdev // NOTE: If the implementation of the vector has an issue, this test will either hang
69251c0b2f7Stbbdev // or fail with the assertion in debug mode.
69351c0b2f7Stbbdev //! \brief \ref regression
69451c0b2f7Stbbdev TEST_CASE("Testing vector in a highly concurrent environment") {
69551c0b2f7Stbbdev     for (std::size_t i = 0; i < 10000; ++i) {
69651c0b2f7Stbbdev         tbb::concurrent_vector<int> test_vec;
69751c0b2f7Stbbdev 
__anon5f703c9f0a02(const tbb::blocked_range<std::size_t>&) 69851c0b2f7Stbbdev         tbb::parallel_for(tbb::blocked_range<std::size_t>(0, 10000), [&] (const tbb::blocked_range<std::size_t>&) {
69951c0b2f7Stbbdev             test_vec.grow_by(1);
70051c0b2f7Stbbdev         }, tbb::static_partitioner{});
70151c0b2f7Stbbdev 
70251c0b2f7Stbbdev         REQUIRE(test_vec.size() == utils::get_platform_max_threads());
70351c0b2f7Stbbdev     }
70451c0b2f7Stbbdev }
705478de5b1Stbbdev 
706478de5b1Stbbdev #if __TBB_CPP20_CONCEPTS_PRESENT
707478de5b1Stbbdev //! \brief \ref error_guessing
708478de5b1Stbbdev TEST_CASE("container_range concept for concurrent_vector ranges") {
709478de5b1Stbbdev     static_assert(test_concepts::container_range<typename tbb::concurrent_vector<int>::range_type>);
710478de5b1Stbbdev     static_assert(test_concepts::container_range<typename tbb::concurrent_vector<int>::const_range_type>);
711478de5b1Stbbdev }
712478de5b1Stbbdev #endif // __TBB_CPP20_CONCEPTS_PRESENT
713a36bf9cbSPavel Kumbrasev 
714a36bf9cbSPavel Kumbrasev // There was a bug in concurrent_vector that was reproduced when resize marked
715a36bf9cbSPavel Kumbrasev // segment (that owned by my_first_block) as deleted and
716a36bf9cbSPavel Kumbrasev // on segment allocation thread is stuck waiting this segment to be published by other thread that allocated first block.
717a36bf9cbSPavel Kumbrasev //! Testing resize behavior for case when new size lesser than old size.
718a36bf9cbSPavel Kumbrasev //! \brief \ref regression
719a36bf9cbSPavel Kumbrasev TEST_CASE("testing resize on sequantual mode") {
720a36bf9cbSPavel Kumbrasev     tbb::concurrent_vector<int> v;
721a36bf9cbSPavel Kumbrasev 
722a36bf9cbSPavel Kumbrasev     v.resize(382);
723a36bf9cbSPavel Kumbrasev     CHECK(v.size() == 382);
724a36bf9cbSPavel Kumbrasev     while (v.size() < 737) {
725a36bf9cbSPavel Kumbrasev         v.emplace_back();
726a36bf9cbSPavel Kumbrasev     }
727a36bf9cbSPavel Kumbrasev     CHECK(v.size() == 737);
728a36bf9cbSPavel Kumbrasev 
729a36bf9cbSPavel Kumbrasev     v.resize(27);
730a36bf9cbSPavel Kumbrasev     CHECK(v.size() == 27);
731a36bf9cbSPavel Kumbrasev     while (v.size() < 737) {
732a36bf9cbSPavel Kumbrasev         v.emplace_back();
733a36bf9cbSPavel Kumbrasev     }
734a36bf9cbSPavel Kumbrasev     CHECK(v.size() == 737);
735a36bf9cbSPavel Kumbrasev 
736a36bf9cbSPavel Kumbrasev     v.resize(1);
737a36bf9cbSPavel Kumbrasev     CHECK(v.size() == 1);
738a36bf9cbSPavel Kumbrasev     while (v.size() < 40) {
739a36bf9cbSPavel Kumbrasev         v.emplace_back();
740a36bf9cbSPavel Kumbrasev     }
741a36bf9cbSPavel Kumbrasev     CHECK(v.size() == 40);
742a36bf9cbSPavel Kumbrasev 
743a36bf9cbSPavel Kumbrasev     v.resize(2222);
744a36bf9cbSPavel Kumbrasev     CHECK(v.size() == 2222);
745a36bf9cbSPavel Kumbrasev     while (v.size() < 4444) {
746a36bf9cbSPavel Kumbrasev         v.emplace_back();
747a36bf9cbSPavel Kumbrasev     }
748a36bf9cbSPavel Kumbrasev     CHECK(v.size() == 4444);
749a36bf9cbSPavel Kumbrasev 
750a36bf9cbSPavel Kumbrasev     v.clear();
751a36bf9cbSPavel Kumbrasev     CHECK(v.size() == 0);
752a36bf9cbSPavel Kumbrasev }
753