1 /*
2     Copyright (c) 2005-2022 Intel Corporation
3 
4     Licensed under the Apache License, Version 2.0 (the "License");
5     you may not use this file except in compliance with the License.
6     You may obtain a copy of the License at
7 
8         http://www.apache.org/licenses/LICENSE-2.0
9 
10     Unless required by applicable law or agreed to in writing, software
11     distributed under the License is distributed on an "AS IS" BASIS,
12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13     See the License for the specific language governing permissions and
14     limitations under the License.
15 */
16 
17 #include "common/test.h"
18 #include "common/utils.h"
19 #include "common/utils_report.h"
20 #include "common/state_trackable.h"
21 #include "common/container_move_support.h"
22 #include "common/custom_allocators.h"
23 #include "common/initializer_list_support.h"
24 #include "common/containers_common.h"
25 #define __TBB_TEST_CPP20_COMPARISONS __TBB_CPP20_COMPARISONS_PRESENT && __TBB_CPP20_CONCEPTS_PRESENT
26 #include "common/test_comparisons.h"
27 #include "oneapi/tbb/concurrent_vector.h"
28 #include "oneapi/tbb/parallel_for.h"
29 #include "oneapi/tbb/tick_count.h"
30 #include "oneapi/tbb/global_control.h"
31 #include <initializer_list>
32 #include <numeric>
33 
34 //! \file conformance_concurrent_vector.cpp
35 //! \brief Test for [containers.concurrent_vector] specification
36 
37 const size_t N = 8192;
38 
39 template<typename Vector, typename Iterator>
40 void CheckConstIterator( const Vector& u, int i, const Iterator& cp ) {
41     typename Vector::const_reference pref = *cp;
42     CHECK((pref.bar()==i));
43     typename Vector::difference_type delta = cp-u.begin();
44     REQUIRE( delta==i );
45     CHECK((u[i].bar()==i));
46     REQUIRE( u.begin()[i].bar()==i );
47 }
48 
49 template<typename Iterator1, typename Iterator2, typename V>
50 void CheckIteratorComparison( V& u ) {
51     V u2 = u;
52     Iterator1 i = u.begin();
53 
54     for( int i_count=0; i_count<100; ++i_count ) {
55         Iterator2 j = u.begin();
56         Iterator2 i2 = u2.begin();
57         for( int j_count=0; j_count<100; ++j_count ) {
58             REQUIRE( ((i==j)==(i_count==j_count)) );
59             REQUIRE( ((i!=j)==(i_count!=j_count)) );
60             REQUIRE( ((i-j)==(i_count-j_count)) );
61             REQUIRE( ((i<j)==(i_count<j_count)) );
62             REQUIRE( ((i>j)==(i_count>j_count)) );
63             REQUIRE( ((i<=j)==(i_count<=j_count)) );
64             REQUIRE( ((i>=j)==(i_count>=j_count)) );
65             REQUIRE( (!(i==i2)) );
66             REQUIRE( i!=i2 );
67             ++j;
68             ++i2;
69         }
70         ++i;
71     }
72 }
73 
74 template<typename Iterator1, typename Iterator2>
75 void TestIteratorAssignment( Iterator2 j ) {
76     Iterator1 i(j);
77     REQUIRE( i==j );
78     REQUIRE( !(i!=j) );
79     Iterator1 k;
80     k = j;
81     REQUIRE( k==j );
82     REQUIRE( !(k!=j) );
83 }
84 
85 template<typename Range1, typename Range2>
86 void TestRangeAssignment( Range2 r2 ) {
87     Range1 r1(r2); r1 = r2;
88 }
89 
90 template<typename T>
91 void TestSequentialFor() {
92     using V = oneapi::tbb::concurrent_vector<move_support_tests::FooWithAssign>;
93     V v(N);
94     REQUIRE(v.grow_by(0) == v.grow_by(0, move_support_tests::FooWithAssign()));
95 
96     // Check iterator
97     typename V::iterator p = v.begin();
98     REQUIRE( !(*p).is_const() );
99     REQUIRE( !p->is_const() );
100     for( int i=0; std::size_t(i)<v.size(); ++i, ++p ) {
101         CHECK( ((*p).state==move_support_tests::Foo::DefaultInitialized) );
102         typename V::reference pref = *p;
103         pref.bar() = i;
104         typename V::difference_type delta = p-v.begin();
105         REQUIRE( delta==i );
106         REQUIRE_MESSAGE( (-delta<=0), "difference type not signed?" );
107     }
108 
109     // Check const_iterator going forwards
110     const V& u = v;
111     typename V::const_iterator cp = u.begin();
112     REQUIRE( cp == v.cbegin() );
113     REQUIRE( (*cp).is_const() );
114     REQUIRE( (cp->is_const()) );
115     REQUIRE( (*cp == v.front()) );
116     for( int i=0; std::size_t(i)<u.size(); ++i ) {
117         CheckConstIterator(u,i,cp);
118         V::const_iterator &cpr = ++cp;
119         REQUIRE_MESSAGE( (&cpr == &cp), "pre-increment not returning a reference?");
120     }
121 
122     // Now go backwards
123     cp = u.end();
124     REQUIRE( cp == v.cend() );
125     for( int i=int(u.size()); i>0; ) {
126         --i;
127         V::const_iterator &cpr = --cp;
128         REQUIRE_MESSAGE( &cpr == &cp, "pre-decrement not returning a reference?");
129         if( i>0 ) {
130             typename V::const_iterator cp_old = cp--;
131             intptr_t here = (*cp_old).bar();
132             REQUIRE( here==u[i].bar() );
133             typename V::const_iterator cp_new = cp++;
134             intptr_t prev = (*cp_new).bar();
135             REQUIRE( prev==u[i-1].bar() );
136         }
137         CheckConstIterator(u,i,cp);
138     }
139 
140     // Now go forwards and backwards
141     std::ptrdiff_t k = 0;
142     cp = u.begin();
143     for( std::size_t i=0; i<u.size(); ++i ) {
144         CheckConstIterator(u,int(k),cp);
145         typename V::difference_type delta = i*3 % u.size();
146         if( 0<=k+delta && std::size_t(k+delta)<u.size() ) {
147             V::const_iterator &cpr = (cp += delta);
148             REQUIRE_MESSAGE( (&cpr == &cp), "+= not returning a reference?");
149             k += delta;
150         }
151         delta = i*7 % u.size();
152         if( 0<=k-delta && std::size_t(k-delta)<u.size() ) {
153             if( i&1 ) {
154                 V::const_iterator &cpr = (cp -= delta);
155                 REQUIRE_MESSAGE( (&cpr == &cp), "-= not returning a reference?");
156             } else
157                 cp = cp - delta;        // Test operator-
158             k -= delta;
159         }
160     }
161 
162     for( int i=0; std::size_t(i)<u.size(); i=(i<50?i+1:i*3) )
163         for( int j=-i; std::size_t(i+j)<u.size(); j=(j<50?j+1:j*5) ) {
164             REQUIRE( ((u.begin()+i)[j].bar()==i+j) );
165             REQUIRE( ((v.begin()+i)[j].bar()==i+j) );
166             REQUIRE( ((v.cbegin()+i)[j].bar()==i+j) );
167             REQUIRE( ((i+u.begin())[j].bar()==i+j) );
168             REQUIRE( ((i+v.begin())[j].bar()==i+j) );
169             REQUIRE(((i+v.cbegin())[j].bar()==i+j) );
170         }
171 
172     CheckIteratorComparison<typename V::iterator, typename V::iterator>(v);
173     CheckIteratorComparison<typename V::iterator, typename V::const_iterator>(v);
174     CheckIteratorComparison<typename V::const_iterator, typename V::iterator>(v);
175     CheckIteratorComparison<typename V::const_iterator, typename V::const_iterator>(v);
176 
177     TestIteratorAssignment<typename V::const_iterator>( u.begin() );
178     TestIteratorAssignment<typename V::const_iterator>( v.begin() );
179     TestIteratorAssignment<typename V::const_iterator>( v.cbegin() );
180     TestIteratorAssignment<typename V::iterator>( v.begin() );
181     // doesn't compile as expected: TestIteratorAssignment<typename V::iterator>( u.begin() );
182 
183     TestRangeAssignment<typename V::const_range_type>( u.range() );
184     TestRangeAssignment<typename V::const_range_type>( v.range() );
185     TestRangeAssignment<typename V::range_type>( v.range() );
186     // doesn't compile as expected: TestRangeAssignment<typename V::range_type>( u.range() );
187 
188     // Check reverse_iterator
189     typename V::reverse_iterator rp = v.rbegin();
190     for( std::size_t i=v.size(); i>0; --i, ++rp ) {
191         typename V::reference pref = *rp;
192         REQUIRE( (std::size_t(pref.bar())==i-1) );
193         REQUIRE( (rp!=v.rend()) );
194     }
195     REQUIRE( rp==v.rend() );
196 
197     // Check const_reverse_iterator
198     typename V::const_reverse_iterator crp = u.rbegin();
199     REQUIRE( crp == v.crbegin() );
200     REQUIRE( *crp == v.back() );
201     for(std::size_t i = v.size(); i>0; --i, ++crp) {
202         typename V::const_reference cpref = *crp;
203         REQUIRE( (std::size_t(cpref.bar())==i-1) );
204         REQUIRE( crp!=u.rend() );
205     }
206     REQUIRE( crp == u.rend() );
207     REQUIRE( crp == v.crend() );
208 
209     TestIteratorAssignment<typename V::const_reverse_iterator>( u.rbegin() );
210     TestIteratorAssignment<typename V::reverse_iterator>( v.rbegin() );
211 
212     {
213         oneapi::tbb::concurrent_vector<int> v1, v2(1ul, 100);
214         v1.assign(1, 100);
215         REQUIRE(v1 == v2);
216         REQUIRE_MESSAGE((v1.size() == 1 && v1[0] == 100), "used integral iterators");
217     }
218 }
219 
220 inline void NextSize( int& s ) {
221     if( s<=32 ) ++s;
222     else s += s/10;
223 }
224 
225 
226 template<typename T, std::size_t N>
227 inline T* end( T(& array)[N]) {
228     return array + utils::array_length(array) ;
229 }
230 
231 template<typename vector_t>
232 static void CheckVector( const vector_t& cv, std::size_t expected_size, std::size_t /*old_size*/ ) {
233     REQUIRE( cv.capacity()>=expected_size );
234     REQUIRE( cv.size()==expected_size );
235     REQUIRE( cv.empty()==(expected_size==0) );
236     for( int j=0; j<int(expected_size); ++j ) {
237         CHECK((cv[j].bar()==~j));
238     }
239 }
240 
241 void TestResizeAndCopy() {
242     using allocator_t = StaticSharedCountingAllocator<std::allocator<move_support_tests::Foo>>;
243     using vector_t = oneapi::tbb::concurrent_vector<move_support_tests::Foo, allocator_t>;
244     allocator_t::init_counters();
245     for( int old_size=0; old_size<=0; NextSize( old_size ) ) {
246         for( int new_size=0; new_size<=8; NextSize( new_size ) ) {
247             std::size_t count = move_support_tests::foo_count;
248 
249             vector_t v;
250             REQUIRE( count==move_support_tests::foo_count );
251             v.assign(old_size/2, move_support_tests::Foo() );
252             REQUIRE( ((count+old_size/2) == move_support_tests::foo_count) );
253             for( int j=0; j<old_size/2; ++j ){
254                 REQUIRE( v[j].state == move_support_tests::Foo::CopyInitialized);
255             }
256 
257             v.assign(move_support_tests::FooIterator(0), move_support_tests::FooIterator(old_size));
258             v.resize(new_size, move_support_tests::Foo(33) );
259             REQUIRE(count+new_size==move_support_tests::foo_count);
260             for( int j=0; j<new_size; ++j ) {
261                 int expected = j<old_size ? j : 33;
262                 CHECK((v[j].bar()==expected));
263             }
264             REQUIRE( v.size()==std::size_t(new_size) );
265             for( int j=0; j<new_size; ++j ) {
266                 v[j].bar() = ~j;
267             }
268 
269             const vector_t& cv = v;
270             // Try copy constructor
271             vector_t copy_of_v(cv);
272             CheckVector(cv,new_size,old_size);
273 
274             REQUIRE( !(v != copy_of_v) );
275             v.clear();
276 
277             REQUIRE( v.empty() );
278             swap(v, copy_of_v);
279             REQUIRE( copy_of_v.empty() );
280             CheckVector(v,new_size,old_size);
281         }
282     }
283     REQUIRE( allocator_t::items_constructed == allocator_t::items_destroyed );
284     REQUIRE( allocator_t::items_allocated == allocator_t::items_freed );
285     REQUIRE( allocator_t::allocations == allocator_t::frees );
286 }
287 
288 
289 void TestCopyAssignment() {
290     using allocator_t = StaticCountingAllocator<std::allocator<move_support_tests::FooWithAssign>>;
291     using vector_t = oneapi::tbb::concurrent_vector<move_support_tests::FooWithAssign, allocator_t>;
292     StaticCountingAllocator<std::allocator<move_support_tests::FooWithAssign>> init_alloc;
293     for( int dst_size=1; dst_size<=128; NextSize( dst_size ) ) {
294         for( int src_size=2; src_size<=128; NextSize( src_size ) ) {
295             vector_t u(move_support_tests::FooIterator(0), move_support_tests::FooIterator(src_size), init_alloc);
296             for( int i=0; i<src_size; ++i )
297                 REQUIRE( u[i].bar()==i );
298             vector_t v(dst_size, move_support_tests::FooWithAssign(), init_alloc);
299             for( int i=0; i<dst_size; ++i ) {
300                 REQUIRE( v[i].state==move_support_tests::Foo::CopyInitialized );
301                 v[i].bar() = ~i;
302             }
303             REQUIRE( v != u );
304             v.swap(u);
305             CheckVector(u, dst_size, src_size);
306             u.swap(v);
307             // using assignment
308             v = u;
309             REQUIRE( v == u );
310             u.clear();
311             REQUIRE( u.size()==0 );
312             REQUIRE( v.size()==std::size_t(src_size) );
313             for( int i=0; i<src_size; ++i ){
314                 REQUIRE( v[i].bar()==i );
315             }
316             u.shrink_to_fit(); // deallocate unused memory
317         }
318     }
319     REQUIRE( allocator_t::items_allocated == allocator_t::items_freed );
320     REQUIRE( allocator_t::allocations == allocator_t::frees );
321 }
322 
323 template<typename Vector, typename T>
324 void TestGrowToAtLeastWithSourceParameter(T const& src){
325     static const std::size_t vector_size = 10;
326     Vector v1(vector_size,src);
327     Vector v2;
328     v2.grow_to_at_least(vector_size,src);
329     REQUIRE_MESSAGE(v1==v2,"grow_to_at_least(vector_size,src) did not properly initialize new elements ?");
330 }
331 
332 void TestCapacity() {
333     using allocator_t = StaticCountingAllocator<std::allocator<move_support_tests::Foo> /*TODO: oneapi::tbb::cache_aligned_allocator*/>;
334     using vector_t = oneapi::tbb::concurrent_vector<move_support_tests::Foo, allocator_t>;
335     allocator_t::init_counters();
336     for( std::size_t old_size=0; old_size<=11000; old_size=(old_size<5 ? old_size+1 : 3*old_size) ) {
337         for( std::size_t new_size=0; new_size<=11000; new_size=(new_size<5 ? new_size+1 : 3*new_size) ) {
338             std::size_t count = move_support_tests::foo_count;
339             {
340                 vector_t v; v.reserve(old_size);
341                 REQUIRE( v.capacity()>=old_size );
342                 v.reserve( new_size );
343                 REQUIRE( v.capacity()>=old_size );
344                 REQUIRE( v.capacity()>=new_size );
345                 REQUIRE( v.empty() );
346                 std::size_t fill_size = 2*new_size;
347                 for (std::size_t i=0; i<fill_size; ++i) {
348                     REQUIRE( std::size_t(move_support_tests::foo_count)==count+i );
349                     std::size_t j = v.grow_by(1) - v.begin();
350                     REQUIRE( j==i );
351                     v[j].bar() = int(~j);
352                 }
353                 vector_t copy_of_v(v); // should allocate first segment with same size as for shrink_to_fit()
354                 if(oneapi::tbb::detail::log2(/*reserved size*/old_size|1) > oneapi::tbb::detail::log2(fill_size|1) ){
355                    REQUIRE( v.capacity() != copy_of_v.capacity() );
356                 }
357                 v.shrink_to_fit();
358                 REQUIRE( v.capacity() == copy_of_v.capacity() );
359                 CheckVector(v, new_size*2, old_size); // check vector correctness
360                 REQUIRE( v==copy_of_v ); // TODO: check also segments layout equality
361             }
362             REQUIRE( move_support_tests::foo_count==count );
363         }
364     }
365     REQUIRE( allocator_t::items_allocated == allocator_t::items_freed );
366     REQUIRE( allocator_t::allocations == allocator_t::frees );
367 }
368 
369 template<typename c_vector>
370 std::size_t get_early_size(c_vector & v){
371       return v.grow_by(0) - v.begin();
372 }
373 
374 void verify_c_vector_size(std::size_t size, std::size_t capacity, std::size_t early_size){
375     REQUIRE( size <= capacity );
376     REQUIRE( early_size >= size );
377 }
378 
379 template<typename c_vector_t>
380 void verify_c_vector_size(c_vector_t & c_v){
381     verify_c_vector_size(c_v.size(), c_v.capacity(), get_early_size(c_v));
382 }
383 
384 #if TBB_USE_EXCEPTIONS
385 void TestExceptions() {
386     using allocator_t = StaticSharedCountingAllocator<std::allocator<move_support_tests::FooWithAssign>>;
387     using vector_t = oneapi::tbb::concurrent_vector<move_support_tests::FooWithAssign, allocator_t>;
388 
389     enum methods {
390         zero_method = 0,
391         ctor_copy, ctor_size, assign_nt, assign_ir, reserve, compact,
392         all_methods
393     };
394     REQUIRE( !move_support_tests::foo_count );
395 
396     try {
397         vector_t src(move_support_tests::FooIterator(0), move_support_tests::FooIterator(N)); // original data
398 
399         for(int t = 0; t < 2; ++t) // exception type
400         for(int m = zero_method+1; m < all_methods; ++m)
401         {
402             move_support_tests::track_foo_count<__LINE__> check_all_foo_destroyed_on_exit{};
403             move_support_tests::track_allocator_memory<allocator_t> verify_no_leak_at_exit{};
404             allocator_t::init_counters();
405             if(t) move_support_tests::max_foo_count = move_support_tests::foo_count + N/4;
406             else allocator_t::set_limits(N/4);
407             vector_t victim;
408             try {
409                 switch(m) {
410                 case ctor_copy: {
411                         vector_t acopy(src);
412                     } break; // auto destruction after exception is checked by ~Foo
413                 case ctor_size: {
414                         vector_t sized(N);
415                     } break; // auto destruction after exception is checked by ~Foo
416                 // Do not test assignment constructor due to reusing of same methods as below
417                 case assign_nt: {
418                         victim.assign(N, move_support_tests::FooWithAssign());
419                     } break;
420                 case assign_ir: {
421                         victim.assign(move_support_tests::FooIterator(0), move_support_tests::FooIterator(N));
422                     } break;
423                 case reserve: {
424                         try {
425                             victim.reserve(victim.max_size()+1);
426                         } catch(std::length_error &) {
427                         } catch(...) {
428                             INFO("ERROR: unrecognized exception - known compiler issue\n");
429                         }
430                         victim.reserve(N);
431                     } break;
432                 case compact: {
433                         if(t) move_support_tests::max_foo_count = 0; else allocator_t::set_limits(); // reset limits
434                         victim.reserve(2);
435                         victim = src; // fragmented assignment
436                         if(t) {
437                             move_support_tests::max_foo_count = move_support_tests::foo_count + 10;
438                         }
439                         else {
440                             allocator_t::set_limits(1); // block any allocation
441                         }
442                         victim.shrink_to_fit(); // should start defragmenting first segment
443                     } break;
444                 default:;
445                 }
446                 if(!t || m != reserve) REQUIRE_MESSAGE(false, "should throw an exception");
447             } catch(std::bad_alloc &e) {
448                 allocator_t::set_limits(); move_support_tests::max_foo_count = 0;
449                 std::size_t capacity = victim.capacity();
450                 std::size_t size = victim.size();
451 
452                 std::size_t req_size = get_early_size(victim);
453 
454                 verify_c_vector_size(size, capacity, req_size);
455 
456                 switch(m) {
457                 case reserve:
458                     if(t) REQUIRE(false);
459                     utils_fallthrough;
460                 case assign_nt:
461                 case assign_ir:
462                     if(!t) {
463                         REQUIRE_MESSAGE(capacity < N/2, "unexpected capacity");
464                         REQUIRE_MESSAGE(size == 0, "unexpected size");
465                         break;
466                     } else {
467                         REQUIRE_MESSAGE(size == N, "unexpected size");
468                         REQUIRE_MESSAGE(capacity >= N, "unexpected capacity");
469                         int i;
470                         for(i = 1; ; ++i)
471                             if(!victim[i].zero_bar()) break;
472                             else {
473                                 REQUIRE(victim[i].bar() == (m == assign_ir? i : move_support_tests::initial_bar));
474                             }
475                         for(; size_t(i) < size; ++i) {
476                             REQUIRE(!victim[i].zero_bar());
477                         }
478                         REQUIRE(size_t(i) == size);
479                         break;
480                     }
481                 case compact:
482                     REQUIRE_MESSAGE(capacity > 0, "unexpected capacity");
483                     REQUIRE_MESSAGE(victim == src, "shrink_to_fit() is broken");
484                     break;
485 
486                 default:; // nothing to check here
487                 }
488                 INFO("Exception " << m << ": " << e.what() << "\t- ok\n");
489             }
490         }
491     } catch(...) {
492         REQUIRE_MESSAGE(false, "unexpected exception");
493     }
494 }
495 #endif
496 
497 void verify_c_vector_capacity_is_below(size_t capacity, size_t high){
498     REQUIRE_MESSAGE(capacity > 0, "unexpected capacity");
499     REQUIRE_MESSAGE(capacity < high, "unexpected capacity");
500 }
501 
502 template<typename allocator_t>
503 void verify_vector_partially_copied(
504         oneapi::tbb::concurrent_vector<move_support_tests::FooWithAssign, allocator_t> const& victim, size_t planned_victim_size,
505         oneapi::tbb::concurrent_vector<move_support_tests::FooWithAssign, allocator_t> const& src,  bool is_memory_allocation_failure)
506 {
507     if (is_memory_allocation_failure) { // allocator generated exception
508         using vector_t = oneapi::tbb::concurrent_vector<move_support_tests::FooWithAssign, allocator_t>;
509         REQUIRE_MESSAGE( victim == vector_t(src.begin(), src.begin() + victim.size(), src.get_allocator()), "failed to properly copy of source ?" );
510     }else{
511         REQUIRE_MESSAGE( std::equal(victim.begin(), victim.begin() + planned_victim_size, src.begin()), "failed to properly copy items before the exception?" );
512         REQUIRE_MESSAGE( (std::all_of( victim.begin() + planned_victim_size, victim.end(), is_state_predicate<move_support_tests::Foo::ZeroInitialized>()) ), "failed to zero-initialize items left not constructed after the exception?" );
513     }
514 }
515 
516 template<typename vector_t>
517 void verify_last_segment_allocation_failed(vector_t const& victim){
518     utils::suppress_unused_warning(victim);
519     CHECK_THROWS_AS((victim.at(victim.size())), std::out_of_range);
520 }
521 
522 template<typename vector_t>
523 void verify_copy_and_assign_from_produce_the_same(vector_t const& victim){
524     //TODO: remove explicit copy of allocator when full support of C++11 allocator_traits in concurrent_vector is present
525     vector_t copy_of_victim(victim, victim.get_allocator());
526     REQUIRE_MESSAGE(copy_of_victim == victim, "copy doesn't match original");
527     vector_t copy_of_victim2(10, victim[0], victim.get_allocator());
528     copy_of_victim2 = victim;
529     REQUIRE_MESSAGE(copy_of_victim == copy_of_victim2, "assignment doesn't match copying");
530 }
531 
532 template<typename vector_t>
533 void verify_assignment_operator_throws_bad_last_alloc(vector_t & victim){
534     vector_t copy_of_victim(victim, victim.get_allocator());
535     //CHECK_THROWS_AS(victim = copy_of_victim, oneapi::tbb::bad_last_alloc); //TODO exceptions support
536 }
537 
538 #if _MSC_VER
539 #pragma warning (push)
540 // Forcing value to bool 'true' or 'false'
541 #pragma warning (disable: 4800)
542 #endif //#if _MSC_VER
543 
544 //TODO: split into two separate tests
545 //TODO: remove code duplication in exception safety tests
546 void test_ex_assign_operator(){
547     //TODO: use __FUNCTION__ for test name
548     using allocator_t = StaticCountingAllocator<std::allocator<move_support_tests::FooWithAssign>>;
549     using vector_t = oneapi::tbb::concurrent_vector<move_support_tests::FooWithAssign, allocator_t>;
550 
551     move_support_tests::track_foo_count<__LINE__> check_all_foo_destroyed_on_exit{};
552     move_support_tests::track_allocator_memory<allocator_t> verify_no_leak_at_exit{};
553 
554     vector_t src(move_support_tests::FooIterator(0), move_support_tests::FooIterator(N)); // original data
555 
556     const size_t planned_victim_size = N/4;
557 
558     for(int t = 0; t < 2; ++t) { // exception type
559         vector_t victim;
560         victim.reserve(2); // get fragmented assignment
561         REQUIRE_THROWS_AS([&](){
562             move_support_tests::LimitFooCountInScope foo_limit(move_support_tests::foo_count + planned_victim_size, t);
563             move_support_tests::LimitAllocatedItemsInScope<allocator_t> allocator_limit(allocator_t::items_allocated + planned_victim_size, !t);
564 
565             victim = src; // fragmented assignment
566         }(), const std::bad_alloc);
567 
568         verify_c_vector_size(victim);
569 
570         if(!t) {
571             verify_c_vector_capacity_is_below(victim.capacity(), N);
572         }
573 
574         verify_vector_partially_copied(victim, planned_victim_size, src, !t);
575         verify_last_segment_allocation_failed(victim);
576         verify_copy_and_assign_from_produce_the_same(victim);
577         verify_assignment_operator_throws_bad_last_alloc(victim); //TODO exceptions support
578     }
579 }
580 
581 #if _MSC_VER
582 #pragma warning (pop)
583 #endif
584 
585 template<typename T>
586 void AssertSameType( const T& /*x*/, const T& /*y*/ ) {}
587 
588 struct test_grow_by {
589     template<typename container_type, typename element_type>
590     static void test( std::initializer_list<element_type> const& il, container_type const& expected ) {
591         container_type vd;
592         vd.grow_by( il );
593         REQUIRE_MESSAGE( vd == expected, "grow_by with an initializer list failed" );
594     }
595 };
596 
597 template<typename Iterator, typename T>
598 void TestIteratorTraits() {
599     AssertSameType( static_cast<typename Iterator::difference_type*>(nullptr), static_cast<std::ptrdiff_t*>(nullptr) );
600     AssertSameType( static_cast<typename Iterator::value_type*>(nullptr), static_cast<T*>(nullptr) );
601     AssertSameType( static_cast<typename Iterator::pointer*>(nullptr), static_cast<T**>(nullptr) );
602     AssertSameType( static_cast<typename Iterator::iterator_category*>(nullptr), static_cast<std::random_access_iterator_tag*>(nullptr) );
603     T x;
604     typename Iterator::reference xr = x;
605     typename Iterator::pointer xp = &x;
606     REQUIRE( &xr==xp );
607 }
608 
609 void TestInitList() {
610     using namespace initializer_list_support_tests;
611     test_initializer_list_support<oneapi::tbb::concurrent_vector<char>, test_grow_by>( { 1, 2, 3, 4, 5 } );
612     test_initializer_list_support<oneapi::tbb::concurrent_vector<int>, test_grow_by>( {} );
613 }
614 
615 namespace TestMoveInShrinkToFitHelpers {
616     struct dummy : StateTrackable<>{
617         int i;
618         dummy(int an_i) noexcept : StateTrackable<>(0), i(an_i) {}
619 
620         friend bool operator== (const dummy &lhs, const dummy &rhs){ return lhs.i == rhs.i; }
621     };
622 }
623 
624 void TestSerialMoveInShrinkToFit(){
625     using TestMoveInShrinkToFitHelpers::dummy;
626 
627     static_assert(std::is_nothrow_move_constructible<dummy>::value,"incorrect test setup or broken configuration?");
628     {
629         dummy src(0);
630         REQUIRE_MESSAGE(is_state<StateTrackableBase::MoveInitialized>(dummy(std::move_if_noexcept(src))),"broken configuration ?");
631     }
632     static const std::size_t sequence_size = 15;
633     using c_vector_t = oneapi::tbb::concurrent_vector<dummy>;
634     std::vector<dummy> source(sequence_size, 0);
635     std::generate_n(source.begin(), source.size(), std::rand);
636 
637     c_vector_t c_vector;
638     c_vector.reserve(1); //make it fragmented
639 
640     c_vector.assign(source.begin(), source.end());
641     move_support_tests::MemoryLocations c_vector_before_shrink(c_vector);
642     c_vector.shrink_to_fit();
643 
644     REQUIRE_MESSAGE(c_vector_before_shrink.content_location_changed(c_vector), "incorrect test setup? shrink_to_fit should cause moving elements to other memory locations while it is not");
645     REQUIRE_MESSAGE((std::all_of(c_vector.begin(), c_vector.end(), is_state_predicate<StateTrackableBase::MoveInitialized>())), "container did not move construct some elements?");
646     REQUIRE((c_vector == c_vector_t(source.begin(),source.end())));
647 }
648 
649 struct default_container_traits {
650     template <typename container_type, typename iterator_type>
651     static container_type& construct_container(typename std::aligned_storage<sizeof(container_type)>::type& storage, iterator_type begin, iterator_type end){
652         container_type* ptr = reinterpret_cast<container_type*>(&storage);
653         new (ptr) container_type(begin, end);
654         return *ptr;
655     }
656 
657     template <typename container_type, typename iterator_type, typename allocator_type>
658     static container_type& construct_container(typename std::aligned_storage<sizeof(container_type)>::type& storage, iterator_type begin, iterator_type end, allocator_type const& a){
659         container_type* ptr = reinterpret_cast<container_type*>(&storage);
660         new (ptr) container_type(begin, end, a);
661         return *ptr;
662     }
663 };
664 
665 struct c_vector_type : default_container_traits {
666     template <typename T, typename Allocator>
667     using container_type = oneapi::tbb::concurrent_vector<T, Allocator>;
668 
669     template <typename T>
670     using container_value_type = T;
671 
672     using init_iterator_type = move_support_tests::FooIterator;
673     template<typename element_type, typename allocator_type>
674     struct apply{
675         using type = oneapi::tbb::concurrent_vector<element_type,  allocator_type >;
676     };
677 
678     enum{ expected_number_of_items_to_allocate_for_steal_move = 0 };
679 
680     template<typename element_type, typename allocator_type, typename iterator>
681     static bool equal(oneapi::tbb::concurrent_vector<element_type, allocator_type > const& c, iterator begin, iterator end){
682         bool equal_sizes = (std::size_t)std::distance(begin, end) == c.size();
683         return  equal_sizes && std::equal(c.begin(), c.end(), begin);
684     }
685 };
686 
687 void TestSerialGrowByWithMoveIterators(){
688     using fixture_t = move_support_tests::DefaultStatefulFixtureHelper<c_vector_type>::type;
689     using vector_t = fixture_t::container_type;
690 
691     fixture_t fixture;
692 
693     vector_t dst(fixture.dst_allocator);
694     dst.grow_by(std::make_move_iterator(fixture.source.begin()), std::make_move_iterator(fixture.source.end()));
695 
696     fixture.verify_content_deep_moved(dst);
697 }
698 
699 namespace test_grow_to_at_least_helpers {
700     template<typename MyVector >
701     class GrowToAtLeast {
702         using const_reference = typename MyVector::const_reference;
703 
704         const bool my_use_two_args_form ;
705         MyVector& my_vector;
706         const_reference my_init_from;
707     public:
708         void operator()( const oneapi::tbb::blocked_range<std::size_t>& range ) const {
709             for( std::size_t i=range.begin(); i!=range.end(); ++i ) {
710                 std::size_t n = my_vector.size();
711                 std::size_t req = (i % (2*n+1))+1;
712 
713                 typename MyVector::iterator p;
714                 move_support_tests::Foo::State desired_state;
715                 if (my_use_two_args_form){
716                     p = my_vector.grow_to_at_least(req,my_init_from);
717                     desired_state = move_support_tests::Foo::CopyInitialized;
718                 }else{
719                     p = my_vector.grow_to_at_least(req);
720                     desired_state = move_support_tests::Foo::DefaultInitialized;
721                 }
722                 if( p-my_vector.begin() < typename MyVector::difference_type(req) )
723                     CHECK((p->state == desired_state || p->state == move_support_tests::Foo::ZeroInitialized));
724                 CHECK(my_vector.size() >= req);
725             }
726         }
727         GrowToAtLeast(bool use_two_args_form, MyVector& vector, const_reference init_from )
728             : my_use_two_args_form(use_two_args_form), my_vector(vector), my_init_from(init_from) {}
729     };
730 }
731 
732 template<bool use_two_arg_form>
733 void TestConcurrentGrowToAtLeastImpl() {
734     using namespace test_grow_to_at_least_helpers;
735     using MyAllocator = StaticCountingAllocator<std::allocator<move_support_tests::Foo>>;
736     using MyVector = oneapi::tbb::concurrent_vector<move_support_tests::Foo, MyAllocator>;
737     move_support_tests::Foo copy_from;
738     MyAllocator::init_counters();
739     MyVector v(2, move_support_tests::Foo(), MyAllocator());
740     for (std::size_t s=1; s<1000; s*=10) {
741         oneapi::tbb::parallel_for(oneapi::tbb::blocked_range<std::size_t>(0, 10000*s, s), GrowToAtLeast<MyVector>(use_two_arg_form, v, copy_from), oneapi::tbb::simple_partitioner());
742     }
743 
744     v.clear();
745     v.shrink_to_fit();
746     std::size_t items_allocated = v.get_allocator().items_allocated,
747            items_freed = v.get_allocator().items_freed;
748     std::size_t allocations = v.get_allocator().allocations,
749            frees = v.get_allocator().frees;
750     REQUIRE( items_allocated == items_freed );
751     REQUIRE( allocations == frees );
752 }
753 
754 struct AssignElement {
755     using iterator = oneapi::tbb::concurrent_vector<int>::range_type::iterator;
756     iterator base;
757     void operator()( const oneapi::tbb::concurrent_vector<int>::range_type& range ) const {
758         for (iterator i = range.begin(); i != range.end(); ++i) {
759             if (*i != 0) {
760                 REPORT("ERROR for v[%ld]\n", long(i - base));
761             }
762             *i = int(i-base);
763         }
764     }
765     AssignElement( iterator base_ ) : base(base_) {}
766 };
767 
768 struct CheckElement {
769     using iterator = oneapi::tbb::concurrent_vector<int>::const_range_type::iterator;
770     iterator base;
771     void operator()( const oneapi::tbb::concurrent_vector<int>::const_range_type& range ) const {
772         for (iterator i = range.begin(); i != range.end(); ++i) {
773             if (*i != int(i-base)) {
774                 REPORT("ERROR for v[%ld]\n", long(i-base));
775             }
776         }
777     }
778     CheckElement( iterator base_ ) : base(base_) {}
779 };
780 
781 // Test parallel access by iterators
782 void TestParallelFor( std::size_t nthread ) {
783     using vector_type = oneapi::tbb::concurrent_vector<int>;
784     vector_type v;
785     v.resize(N);
786     oneapi::tbb::tick_count t0 = oneapi::tbb::tick_count::now();
787     INFO("Calling parallel_for with " << nthread << " threads");
788     oneapi::tbb::parallel_for(v.range(10000), AssignElement(v.begin()));
789     oneapi::tbb::tick_count t1 = oneapi::tbb::tick_count::now();
790     const vector_type& u = v;
791     oneapi::tbb::parallel_for(u.range(10000), CheckElement(u.begin()));
792     oneapi::tbb::tick_count t2 = oneapi::tbb::tick_count::now();
793     INFO("Time for parallel_for: assign time = " << (t1 - t0).seconds() <<
794         " , check time = " << (t2 - t1).seconds());
795     for (int i = 0; std::size_t(i) < v.size(); ++i) {
796         if (v[i] != i) {
797             REPORT("ERROR for v[%ld]\n", i);
798         }
799     }
800 }
801 
802 
803 struct grain_map {
804     enum grow_method_enum {
805         grow_by_range = 1,
806         grow_by_default,
807         grow_by_copy,
808         grow_by_init_list,
809         push_back,
810         push_back_move,
811         emplace_back,
812         last_method
813     };
814 
815     struct range_part {
816         std::size_t number_of_parts;
817         grain_map::grow_method_enum method;
818         bool distribute;
819         move_support_tests::Foo::State expected_element_state;
820     };
821 
822     const std::vector<range_part> distributed;
823     const std::vector<range_part> batched;
824     const std::size_t total_number_of_parts;
825 
826     grain_map(const range_part* begin, const range_part* end)
827     : distributed(separate(begin,end, &distributed::is_not))
828     , batched(separate(begin,end, &distributed::is_yes))
829     , total_number_of_parts(std::accumulate(begin, end, (std::size_t)0, &sum_number_of_parts::sum))
830     {}
831 
832 private:
833     struct sum_number_of_parts{
834         static std::size_t sum(std::size_t accumulator, grain_map::range_part const& rp){ return accumulator + rp.number_of_parts;}
835     };
836 
837     template <typename functor_t>
838     static std::vector<range_part> separate(const range_part* begin, const range_part* end, functor_t f){
839         std::vector<range_part> part;
840         part.reserve(std::distance(begin,end));
841         //copy all that false==f(*it)
842         std::remove_copy_if(begin, end, std::back_inserter(part), f);
843 
844         return part;
845     }
846 
847     struct distributed {
848         static bool is_not(range_part const& rp){ return !rp.distribute;}
849         static bool is_yes(range_part const& rp){ return rp.distribute;}
850     };
851 };
852 
853 
854 //! Test concurrent invocations of method concurrent_vector::grow_by
855 template<typename MyVector>
856 class GrowBy {
857     MyVector& my_vector;
858     const grain_map& my_grain_map;
859     std::size_t my_part_weight;
860 public:
861     void operator()( const oneapi::tbb::blocked_range<std::size_t>& range ) const {
862         CHECK(range.begin() < range.end());
863 
864         std::size_t current_adding_index_in_cvector = range.begin();
865 
866         for (std::size_t index = 0; index < my_grain_map.batched.size(); ++index){
867             const grain_map::range_part& batch_part = my_grain_map.batched[index];
868             const std::size_t number_of_items_to_add = batch_part.number_of_parts * my_part_weight;
869             const std::size_t end = current_adding_index_in_cvector + number_of_items_to_add;
870 
871             switch(batch_part.method){
872             case grain_map::grow_by_range : {
873                     my_vector.grow_by(move_support_tests::FooIterator(current_adding_index_in_cvector), move_support_tests::FooIterator(end));
874                 } break;
875             case grain_map::grow_by_default : {
876                     typename MyVector::iterator const s = my_vector.grow_by(number_of_items_to_add);
877                     for (std::size_t k = 0; k < number_of_items_to_add; ++k) {
878                         s[k].bar() = current_adding_index_in_cvector + k;
879                     }
880                 } break;
881             case grain_map::grow_by_init_list : {
882                     move_support_tests::FooIterator curr(current_adding_index_in_cvector);
883                     for (std::size_t k = 0; k < number_of_items_to_add; ++k) {
884                         if (k + 4 < number_of_items_to_add) {
885                             my_vector.grow_by( { *curr++, *curr++, *curr++, *curr++, *curr++ } );
886                             k += 4;
887                         } else {
888                             my_vector.grow_by( { *curr++ } );
889                         }
890                     }
891                     CHECK(curr == move_support_tests::FooIterator(end));
892                 } break;
893             default : { REQUIRE_MESSAGE(false, "using unimplemented method of batch add in ConcurrentGrow test.");} break;
894             };
895 
896             current_adding_index_in_cvector = end;
897         }
898 
899         std::vector<std::size_t> items_left_to_add(my_grain_map.distributed.size());
900         for (std::size_t i=0; i < my_grain_map.distributed.size(); ++i) {
901             items_left_to_add[i] = my_grain_map.distributed[i].number_of_parts * my_part_weight;
902         }
903 
904         for (;current_adding_index_in_cvector < range.end(); ++current_adding_index_in_cvector) {
905             std::size_t method_index = current_adding_index_in_cvector % my_grain_map.distributed.size();
906 
907             if (!items_left_to_add[method_index]) {
908                 struct not_zero{
909                     static bool is(std::size_t items_to_add){ return items_to_add != 0;}
910                 };
911                 method_index = std::distance(items_left_to_add.begin(), std::find_if(items_left_to_add.begin(), items_left_to_add.end(), &not_zero::is));
912                 REQUIRE_MESSAGE(method_index < my_grain_map.distributed.size(), "incorrect test setup - wrong expected distribution: left free space but no elements to add?");
913             };
914 
915             REQUIRE_MESSAGE(items_left_to_add[method_index], "logic error ?");
916             const grain_map::range_part& distributed_part = my_grain_map.distributed[method_index];
917 
918             typename MyVector::iterator r;
919             typename MyVector::value_type source;
920             source.bar() = current_adding_index_in_cvector;
921 
922             switch(distributed_part.method){
923             case grain_map::grow_by_default : {
924                     (r = my_vector.grow_by(1))->bar() = current_adding_index_in_cvector;
925                 } break;
926             case grain_map::grow_by_copy : {
927                     r = my_vector.grow_by(1, source);
928                 } break;
929             case grain_map::push_back : {
930                     r = my_vector.push_back(source);
931                 } break;
932             case grain_map::push_back_move : {
933                     r = my_vector.push_back(std::move(source));
934                 } break;
935             case grain_map::emplace_back : {
936                     r = my_vector.emplace_back(current_adding_index_in_cvector);
937                 } break;
938 
939             default : { REQUIRE_MESSAGE(false, "using unimplemented method of batch add in ConcurrentGrow test.");} break;
940             };
941 
942             CHECK(static_cast<std::size_t>(r->bar()) == current_adding_index_in_cvector);
943             }
944 
945         }
946 
947     GrowBy( MyVector& vector, const grain_map& m, std::size_t part_weight )
948     : my_vector(vector), my_grain_map(m), my_part_weight(part_weight)
949     {}
950 };
951 
952 //! Test concurrent invocations of grow methods
953 void TestConcurrentGrowBy() {
954 
955     const grain_map::range_part concurrent_grow_single_range_map [] = {
956     //  number_of_parts,         method,             distribute,   expected_element_state
957             {3,           grain_map::grow_by_range,     false,   move_support_tests::Foo::MoveInitialized},
958 
959             {1,           grain_map::grow_by_init_list, false,   move_support_tests::Foo::CopyInitialized},
960 
961             {2,           grain_map::grow_by_default,   false,   move_support_tests::Foo::DefaultInitialized},
962             {1,           grain_map::grow_by_default,   true,    move_support_tests::Foo::DefaultInitialized},
963             {1,           grain_map::grow_by_copy,      true,    move_support_tests::Foo::CopyInitialized},
964             {1,           grain_map::push_back,         true,    move_support_tests::Foo::CopyInitialized},
965 
966             {1,           grain_map::push_back_move,    true,    move_support_tests::Foo::MoveInitialized},
967 
968             {1,           grain_map::emplace_back,      true,    move_support_tests::Foo::DirectInitialized},
969 
970     };
971 
972     using MyAllocator = StaticCountingAllocator<std::allocator<move_support_tests::Foo> >;
973     using MyVector = oneapi::tbb::concurrent_vector<move_support_tests::Foo, MyAllocator>;
974 
975     MyAllocator::init_counters();
976     {
977         grain_map m(concurrent_grow_single_range_map, end(concurrent_grow_single_range_map));
978 
979         static const std::size_t desired_grain_size = 100;
980 
981         static const std::size_t part_weight = desired_grain_size / m.total_number_of_parts;
982         static const std::size_t grain_size = part_weight * m.total_number_of_parts;
983         static const std::size_t number_of_grains = 8; //this should be (power of two) in order to get minimal ranges equal to grain_size
984         static const std::size_t range_size = grain_size * number_of_grains;
985 
986         MyAllocator a;
987         MyVector v(a);
988         oneapi::tbb::parallel_for(oneapi::tbb::blocked_range<std::size_t>(0, range_size, grain_size), GrowBy<MyVector>(v, m, part_weight), oneapi::tbb::simple_partitioner());
989 
990         REQUIRE( v.size() == std::size_t(range_size) );
991 
992         // Verify that v is a permutation of 0..m
993         size_t direct_inits = 0, def_inits = 0, copy_inits = 0, move_inits = 0;
994         std::vector<bool> found(range_size, 0);
995         for( std::size_t i=0; i<range_size; ++i ) {
996             if( v[i].state == move_support_tests::Foo::DefaultInitialized ) ++def_inits;
997             else if( v[i].state == move_support_tests::Foo::DirectInitialized ) ++direct_inits;
998             else if( v[i].state == move_support_tests::Foo::CopyInitialized ) ++copy_inits;
999             else if( v[i].state == move_support_tests::Foo::MoveInitialized ) ++move_inits;
1000             else {
1001                 REQUIRE_MESSAGE( false, "v[i] seems not initialized");
1002             }
1003             intptr_t index = v[i].bar();
1004             REQUIRE( !found[index] );
1005             found[index] = true;
1006         }
1007 
1008         std::size_t expected_direct_inits = 0, expected_def_inits = 0, expected_copy_inits = 0, expected_move_inits = 0;
1009         for (std::size_t i=0; i < utils::array_length(concurrent_grow_single_range_map); ++i){
1010             const grain_map::range_part& rp =concurrent_grow_single_range_map[i];
1011             switch (rp.expected_element_state){
1012             case move_support_tests::Foo::DefaultInitialized: { expected_def_inits += rp.number_of_parts ; } break;
1013             case move_support_tests::Foo::DirectInitialized:  { expected_direct_inits += rp.number_of_parts ;} break;
1014             case move_support_tests::Foo::MoveInitialized:    { expected_move_inits += rp.number_of_parts ;} break;
1015             case move_support_tests::Foo::CopyInitialized:    { expected_copy_inits += rp.number_of_parts ;} break;
1016             default: {REQUIRE_MESSAGE(false, "unexpected expected state");}break;
1017             };
1018         }
1019 
1020         expected_def_inits    *= part_weight * number_of_grains;
1021         expected_move_inits   *= part_weight * number_of_grains;
1022         expected_copy_inits   *= part_weight * number_of_grains;
1023         expected_direct_inits *= part_weight * number_of_grains;
1024 
1025         REQUIRE( def_inits == expected_def_inits );
1026         REQUIRE( copy_inits == expected_copy_inits );
1027         REQUIRE( move_inits == expected_move_inits );
1028         REQUIRE( direct_inits == expected_direct_inits );
1029     }
1030     //TODO: factor this into separate thing, as it seems to used in big number of tests
1031     std::size_t items_allocated = MyAllocator::items_allocated,
1032            items_freed = MyAllocator::items_freed;
1033     std::size_t allocations = MyAllocator::allocations,
1034            frees = MyAllocator::frees;
1035     REQUIRE(items_allocated == items_freed);
1036     REQUIRE(allocations == frees);
1037 }
1038 
1039 void TestComparison() {
1040     std::string str[3];
1041     str[0] = "abc";
1042     str[1].assign("cba");
1043     str[2].assign("abc"); // same as 0th
1044     oneapi::tbb::concurrent_vector<char> var[3];
1045     var[0].assign(str[0].begin(), str[0].end());
1046     var[1].assign(str[0].rbegin(), str[0].rend());
1047     var[2].assign(var[1].rbegin(), var[1].rend()); // same as 0th
1048     for (int i = 0; i < 3; ++i) {
1049         for (int j = 0; j < 3; ++j) {
1050             REQUIRE( (var[i] == var[j]) == (str[i] == str[j]) );
1051             REQUIRE( (var[i] != var[j]) == (str[i] != str[j]) );
1052             REQUIRE( (var[i] < var[j]) == (str[i] < str[j]) );
1053             REQUIRE( (var[i] > var[j]) == (str[i] > str[j]) );
1054             REQUIRE( (var[i] <= var[j]) == (str[i] <= str[j]) );
1055             REQUIRE( (var[i] >= var[j]) == (str[i] >= str[j]) );
1056         }
1057     }
1058 }
1059 
1060 #if TBB_USE_EXCEPTIONS
1061 void test_ex_move_assignment_memory_failure() {
1062     using fixture_type = move_support_tests::DefaultStatefulFixtureHelper<c_vector_type, /*POCMA = */std::false_type>::type;
1063     using arena_allocator_fixture_type = move_support_tests::ArenaAllocatorFixture<move_support_tests::FooWithAssign, /*POCMA = */std::false_type>;
1064     using allocator_type = fixture_type::allocator_type;
1065     using vector_type = fixture_type::container_type;
1066 
1067     fixture_type fixture;
1068     arena_allocator_fixture_type arena_allocator_fixture(4 * fixture.container_size);
1069 
1070     const std::size_t allocation_limit = fixture.container_size/4;
1071 
1072     vector_type victim(arena_allocator_fixture.allocator);
1073     victim.reserve(2); // for fragmented assignment
1074 
1075     REQUIRE_THROWS_AS(
1076         [&]() {
1077             move_support_tests::LimitAllocatedItemsInScope<allocator_type> allocator_limit(allocator_type::items_allocated + allocation_limit);
1078             victim = std::move(fixture.source); // fragmented assignment
1079         }(),
1080         std::bad_alloc
1081     );
1082 
1083     verify_c_vector_size(victim);
1084     verify_c_vector_capacity_is_below(victim.capacity(), allocation_limit + 2);
1085     fixture.verify_part_of_content_deep_moved(victim, victim.size());
1086 
1087     verify_last_segment_allocation_failed(victim);
1088     verify_copy_and_assign_from_produce_the_same(victim);
1089     verify_assignment_operator_throws_bad_last_alloc(victim);
1090 }
1091 
1092 void test_ex_move_assignment_element_ctor_exception(){
1093     using fixture_type = move_support_tests::DefaultStatefulFixtureHelper<c_vector_type, std::false_type>::type;
1094     using arena_allocator_fixture_type = move_support_tests::ArenaAllocatorFixture<move_support_tests::FooWithAssign, std::false_type>;
1095     using vector_type = fixture_type::container_type;
1096 
1097     fixture_type fixture;
1098     const size_t planned_victim_size = fixture.container_size/4;
1099     arena_allocator_fixture_type arena_allocator_fixture(4 * fixture.container_size);
1100 
1101     vector_type victim(arena_allocator_fixture.allocator);
1102     victim.reserve(2); // get fragmented assignment
1103 
1104     REQUIRE_THROWS_AS(
1105         [&](){
1106             move_support_tests::LimitFooCountInScope foo_limit(move_support_tests::foo_count + planned_victim_size);
1107             victim = std::move(fixture.source); // fragmented assignment
1108         }(),
1109         std::bad_alloc
1110     );
1111 
1112     verify_c_vector_size(victim);
1113 
1114     fixture.verify_part_of_content_deep_moved(victim, planned_victim_size);
1115 
1116     verify_last_segment_allocation_failed(victim);
1117     verify_copy_and_assign_from_produce_the_same(victim);
1118     verify_assignment_operator_throws_bad_last_alloc(victim);
1119 }
1120 
1121 void test_ex_move_assignment() {
1122     test_ex_move_assignment_memory_failure();
1123     test_ex_move_assignment_element_ctor_exception();
1124 }
1125 #endif
1126 
1127 template <typename Type, typename Allocator>
1128 class test_grow_by_and_resize {
1129     oneapi::tbb::concurrent_vector<Type, Allocator> &my_c;
1130 public:
1131     test_grow_by_and_resize( oneapi::tbb::concurrent_vector<Type, Allocator> &c ) : my_c(c) {}
1132     void operator()() const {
1133         const typename oneapi::tbb::concurrent_vector<Type, Allocator>::size_type sz = my_c.size();
1134         my_c.grow_by( 5 );
1135         REQUIRE( my_c.size() == sz + 5 );
1136         my_c.resize( sz );
1137         REQUIRE( my_c.size() == sz );
1138     }
1139 };
1140 
1141 namespace push_back_exception_safety_helpers {
1142     //TODO: remove code duplication with emplace_helpers::wrapper_type
1143     struct throwing_foo:move_support_tests::Foo {
1144         int value1;
1145         int value2;
1146         explicit throwing_foo(int v1, int v2) : value1 (v1), value2(v2) {}
1147     };
1148 
1149     template< typename foo_t = throwing_foo>
1150     struct fixture {
1151         using vector_t = oneapi::tbb::concurrent_vector<foo_t, std::allocator<foo_t> >;
1152         vector_t v;
1153 
1154         void test( void(*p_test)(vector_t&)){
1155             utils::suppress_unused_warning(p_test);
1156             move_support_tests::track_foo_count<__LINE__> verify_no_foo_leaked_during_exception{};
1157             utils::suppress_unused_warning(verify_no_foo_leaked_during_exception);
1158             REQUIRE_MESSAGE(v.empty(),"incorrect test setup?" );
1159             REQUIRE_THROWS_AS(p_test(v), move_support_tests::FooException);
1160             REQUIRE_MESSAGE(is_state<move_support_tests::Foo::ZeroInitialized>(v[0]),"incorrectly filled item during exception in emplace_back?");
1161         }
1162     };
1163 }
1164 
1165 void TestPushBackMoveExceptionSafety() {
1166     using fixture_t = push_back_exception_safety_helpers::fixture<move_support_tests::Foo>;
1167     fixture_t t;
1168 
1169     move_support_tests::LimitFooCountInScope foo_limit(move_support_tests::foo_count + 1);
1170 
1171     struct test {
1172         static void test_move_push_back(fixture_t::vector_t& v) {
1173             move_support_tests::Foo f;
1174             v.push_back(std::move(f));
1175         }
1176     };
1177     t.test(&test::test_move_push_back);
1178 }
1179 
1180 void TestEmplaceBackExceptionSafety(){
1181     using fixture_t = push_back_exception_safety_helpers::fixture<>;
1182     fixture_t t;
1183 
1184     move_support_tests::Foo dummy; //make FooCount non zero;
1185     utils::suppress_unused_warning(dummy);
1186     move_support_tests::LimitFooCountInScope foo_limit(move_support_tests::foo_count);
1187 
1188     struct test {
1189         static void test_emplace(fixture_t::vector_t& v) {
1190             v.emplace_back(1,2);
1191         }
1192     };
1193     t.test(&test::test_emplace);
1194 }
1195 
1196 namespace move_semantics_helpers {
1197     struct move_only_type {
1198         const int* my_pointer;
1199         move_only_type(move_only_type && other): my_pointer(other.my_pointer){other.my_pointer=nullptr;}
1200         explicit move_only_type(const int* value): my_pointer(value) {}
1201     };
1202 }
1203 
1204 void TestPushBackMoveOnlyContainer(){
1205     using namespace move_semantics_helpers;
1206     using vector_t = oneapi::tbb::concurrent_vector<move_only_type >;
1207     vector_t v;
1208     static const int magic_number = 7;
1209     move_only_type src(&magic_number);
1210     v.push_back(std::move(src));
1211     REQUIRE_MESSAGE((v[0].my_pointer == &magic_number),"item was incorrectly moved during push_back?");
1212     REQUIRE_MESSAGE(src.my_pointer == nullptr,"item was incorrectly moved during push_back?");
1213 }
1214 
1215 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
1216 template <template <typename...> typename TVector>
1217 void TestDeductionGuides() {
1218     using ComplexType = const std::string*;
1219     std::vector<ComplexType> v;
1220     std::string s = "s";
1221     auto l = {ComplexType(&s), ComplexType(&s)};
1222 
1223     // check TVector(InputIterator, InputIterator)
1224     TVector v1(v.begin(), v.end());
1225     static_assert(std::is_same<decltype(v1), TVector<ComplexType>>::value);
1226 
1227     // check TVector(InputIterator, InputIterator, Alocator)
1228     TVector v2(v.begin(), v.end(), std::allocator<ComplexType>());
1229     static_assert(std::is_same<decltype(v2),
1230        TVector<ComplexType, std::allocator<ComplexType>>>::value);
1231 
1232     // check TVector(std::initializer_list<T>)
1233     TVector v3(l);
1234     static_assert(std::is_same<decltype(v3),
1235         TVector<ComplexType>>::value);
1236 
1237     // check TVector(std::initializer_list, Alocator)
1238     TVector v4(l, std::allocator<ComplexType>());
1239     static_assert(std::is_same<decltype(v4), TVector<ComplexType, std::allocator<ComplexType>>>::value);
1240 
1241     // check TVector(TVector&)
1242     TVector v5(v1);
1243     static_assert(std::is_same<decltype(v5), TVector<ComplexType>>::value);
1244 
1245     // check TVector(TVector&, Allocator)
1246     TVector v6(v5, oneapi::tbb::cache_aligned_allocator<ComplexType>());
1247     static_assert(std::is_same<decltype(v6), TVector<ComplexType, oneapi::tbb::cache_aligned_allocator<ComplexType>>>::value);
1248 
1249     // check TVector(TVector&&)
1250     TVector v7(std::move(v1));
1251     static_assert(std::is_same<decltype(v7), decltype(v1)>::value);
1252 
1253     // check TVector(TVector&&, Allocator)
1254     TVector v8(std::move(v5), oneapi::tbb::cache_aligned_allocator<ComplexType>());
1255     static_assert(std::is_same<decltype(v8), TVector<ComplexType, oneapi::tbb::cache_aligned_allocator<ComplexType>>>::value);
1256 
1257 }
1258 #endif
1259 
1260 template <template <typename... > class ContainerType>
1261 void test_member_types() {
1262     using default_container_type = ContainerType<int>;
1263 
1264     static_assert(std::is_same<typename default_container_type::allocator_type,
1265                                oneapi::tbb::cache_aligned_allocator<int>>::value,
1266                   "Incorrect default template allocator");
1267 
1268 
1269     using test_allocator_type = oneapi::tbb::cache_aligned_allocator<int>;
1270     using container_type = ContainerType<int, test_allocator_type>;
1271 
1272     static_assert(std::is_same<typename container_type::value_type, int>::value,
1273                   "Incorrect container value_type member type");
1274 
1275     static_assert(std::is_unsigned<typename container_type::size_type>::value,
1276                   "Incorrect container size_type member type");
1277     static_assert(std::is_signed<typename container_type::difference_type>::value,
1278                   "Incorrect container difference_type member type");
1279 
1280     using value_type = typename container_type::value_type;
1281     static_assert(std::is_same<typename container_type::reference, value_type&>::value,
1282                   "Incorrect container reference member type");
1283     static_assert(std::is_same<typename container_type::const_reference, const value_type&>::value,
1284                   "Incorrect container const_reference member type");
1285     using allocator_type = typename container_type::allocator_type;
1286     static_assert(std::is_same<typename container_type::pointer, typename std::allocator_traits<allocator_type>::pointer>::value,
1287                   "Incorrect container pointer member type");
1288     static_assert(std::is_same<typename container_type::const_pointer, typename std::allocator_traits<allocator_type>::const_pointer>::value,
1289                   "Incorrect container const_pointer member type");
1290 
1291     static_assert(utils::is_random_access_iterator<typename container_type::iterator>::value,
1292                   "Incorrect container iterator member type");
1293     static_assert(!std::is_const<typename container_type::iterator::value_type>::value,
1294                   "Incorrect container iterator member type");
1295     static_assert(utils::is_random_access_iterator<typename container_type::const_iterator>::value,
1296                   "Incorrect container const_iterator member type");
1297     static_assert(std::is_const<typename container_type::const_iterator::value_type>::value,
1298                   "Incorrect container iterator member type");
1299 }
1300 
1301 void TestConcurrentGrowToAtLeast() {
1302     TestConcurrentGrowToAtLeastImpl<false>();
1303     TestConcurrentGrowToAtLeastImpl<true>();
1304 }
1305 
1306 template <typename Vector>
1307 void test_comparisons_basic() {
1308     using comparisons_testing::testEqualityAndLessComparisons;
1309     Vector v1, v2;
1310     testEqualityAndLessComparisons</*ExpectEqual = */true, /*ExpectLess = */false>(v1, v2);
1311 
1312     v1.emplace_back(1);
1313     testEqualityAndLessComparisons</*ExpectEqual = */false, /*ExpectLess = */false>(v1, v2);
1314 
1315     v2.emplace_back(1);
1316     testEqualityAndLessComparisons</*ExpectEqual = */true, /*ExpectLess = */false>(v1, v2);
1317 
1318     v2.emplace_back(2);
1319     testEqualityAndLessComparisons</*ExpectEqual = */false, /*ExpectLess = */true>(v1, v2);
1320 
1321     v1.clear();
1322     v2.clear();
1323     testEqualityAndLessComparisons</*ExpectEqual = */true, /*ExpectLess = */false>(v1, v2);
1324 }
1325 
1326 template <typename TwoWayComparableVectorType>
1327 void test_two_way_comparable_vector() {
1328     TwoWayComparableVectorType v1, v2;
1329     v1.emplace_back(1);
1330     v2.emplace_back(1);
1331     comparisons_testing::TwoWayComparable::reset();
1332     REQUIRE_MESSAGE(!(v1 < v2), "Incorrect operator < result");
1333     comparisons_testing::check_two_way_comparison();
1334     REQUIRE_MESSAGE(!(v1 > v2), "Incorrect operator > result");
1335     comparisons_testing::check_two_way_comparison();
1336     REQUIRE_MESSAGE(v1 <= v2, "Incorrect operator <= result");
1337     comparisons_testing::check_two_way_comparison();
1338     REQUIRE_MESSAGE(v1 >= v2, "Incorrect operator >= result");
1339     comparisons_testing::check_two_way_comparison();
1340 }
1341 
1342 #if __TBB_TEST_CPP20_COMPARISONS
1343 template <typename ThreeWayComparableVectorType>
1344 void test_three_way_comparable_vector() {
1345     ThreeWayComparableVectorType v1, v2;
1346     v1.emplace_back(1);
1347     v2.emplace_back(1);
1348     comparisons_testing::ThreeWayComparable::reset();
1349     REQUIRE_MESSAGE(!(v1 <=> v2 < 0), "Incorrect operator<=> result");
1350     comparisons_testing::check_three_way_comparison();
1351 
1352     REQUIRE_MESSAGE(!(v1 < v2), "Incorrect operator< result");
1353     comparisons_testing::check_three_way_comparison();
1354 
1355     REQUIRE_MESSAGE(!(v1 > v2), "Incorrect operator> result");
1356     comparisons_testing::check_three_way_comparison();
1357 
1358     REQUIRE_MESSAGE(v1 <= v2, "Incorrect operator>= result");
1359     comparisons_testing::check_three_way_comparison();
1360 
1361     REQUIRE_MESSAGE(v1 >= v2, "Incorrect operator>= result");
1362     comparisons_testing::check_three_way_comparison();
1363 }
1364 #endif // __TBB_TEST_CPP20_COMPARISONS
1365 
1366 void TestVectorComparisons() {
1367     using integral_vector = oneapi::tbb::concurrent_vector<int>;
1368     using two_way_comparable_vector = oneapi::tbb::concurrent_vector<comparisons_testing::TwoWayComparable>;
1369 
1370     test_comparisons_basic<integral_vector>();
1371     test_comparisons_basic<two_way_comparable_vector>();
1372     test_two_way_comparable_vector<two_way_comparable_vector>();
1373 
1374 #if __TBB_TEST_CPP20_COMPARISONS
1375     using two_way_less_only_vector = oneapi::tbb::concurrent_vector<comparisons_testing::LessComparableOnly>;
1376     using three_way_only_vector = oneapi::tbb::concurrent_vector<comparisons_testing::ThreeWayComparableOnly>;
1377     using three_way_comparable_vector = oneapi::tbb::concurrent_vector<comparisons_testing::ThreeWayComparable>;
1378 
1379     test_comparisons_basic<two_way_less_only_vector>();
1380     test_comparisons_basic<three_way_only_vector>();
1381     test_comparisons_basic<three_way_comparable_vector>();
1382     test_three_way_comparable_vector<three_way_comparable_vector>();
1383 #endif // __TBB_CPP20_COMPARISONS_PRESENT && __TBB_CPP20_CONVEPTS_PRESENT
1384 }
1385 
1386 template <bool ExpectEqual, bool ExpectLess, typename Iterator>
1387 void DoVectorIteratorComparisons( const Iterator& lhs, const Iterator& rhs ) {
1388     // TODO: replace with testEqualityAndLessComparisons after adding <=> operator for concurrent_vector iterator
1389     using namespace comparisons_testing;
1390     testEqualityComparisons<ExpectEqual>(lhs, rhs);
1391     testTwoWayComparisons<ExpectEqual, ExpectLess>(lhs, rhs);
1392 }
1393 
1394 template <typename Iterator, typename VectorType>
1395 void TestVectorIteratorComparisonsBasic( VectorType& vec ) {
1396     REQUIRE_MESSAGE(!vec.empty(), "Incorrect test setup");
1397     Iterator it1, it2;
1398     DoVectorIteratorComparisons</*ExpectEqual = */true, /*ExpectLess = */false>(it1, it2);
1399     it1 = vec.begin();
1400     it2 = vec.begin();
1401     DoVectorIteratorComparisons</*ExpectEqual = */true, /*ExpectLess = */false>(it1, it2);
1402     it2 = std::prev(vec.end());
1403     DoVectorIteratorComparisons</*ExpectEqual = */false, /*ExpectLess = */true>(it1, it2);
1404 }
1405 
1406 void TestVectorIteratorComparisons() {
1407     using vector_type = oneapi::tbb::concurrent_vector<int>;
1408     vector_type vec = {1, 2, 3, 4, 5};
1409     TestVectorIteratorComparisonsBasic<typename vector_type::iterator>(vec);
1410     const vector_type& cvec = vec;
1411     TestVectorIteratorComparisonsBasic<typename vector_type::const_iterator>(cvec);
1412 }
1413 
1414 //! Test type matching
1415 //! \brief \ref interface \ref requirement
1416 TEST_CASE("test type matching") {
1417     test_member_types<oneapi::tbb::concurrent_vector>();
1418 }
1419 
1420 //! Test sequential access to elements
1421 //! \brief \ref interface \ref requirement
1422 TEST_CASE("testing sequential for") {
1423     TestSequentialFor<move_support_tests::FooWithAssign> ();
1424 }
1425 
1426 //! Test of assign, grow, copying with various sizes
1427 //! \brief \ref interface \ref requirement
1428 TEST_CASE("testing resize and copy"){
1429     TestResizeAndCopy();
1430 }
1431 
1432 //! Test the assignment operator and swap
1433 //! \brief \ref interface \ref requirement
1434 TEST_CASE("testing copy assignment"){
1435     TestCopyAssignment();
1436 }
1437 
1438 //! Testing grow_to_at_least operations
1439 //! \brief \ref interface
1440 TEST_CASE("testing grow_to_at_least with source parameter"){
1441     TestGrowToAtLeastWithSourceParameter<oneapi::tbb::concurrent_vector<int>>(12345);
1442 }
1443 
1444 //! Test of capacity, reserve, and shrink_to_fit
1445 //! \brief \ref interface \ref requirement
1446 TEST_CASE("testing capacity"){
1447    TestCapacity();
1448 }
1449 
1450 #if TBB_USE_EXCEPTIONS
1451 //! Test exceptions
1452 //! \brief \ref requirement
1453 TEST_CASE("testing exceptions"){
1454     TestExceptions();
1455 }
1456 
1457 //! Test of push_back move exception safety
1458 //! \brief \ref requirement
1459 TEST_CASE("testing push_back move exception safety"){
1460     TestPushBackMoveExceptionSafety();
1461 }
1462 
1463 //! Test of emplace back move exception safety
1464 //! \brief \ref requirement
1465 TEST_CASE("testing emplace back exception safety"){
1466     TestEmplaceBackExceptionSafety();
1467 }
1468 
1469 //! Test exceptions guarantees for assign operator
1470 //! \brief \ref requirement
1471 TEST_CASE("testing exception safety guaranteees for assign operator"){
1472     test_ex_assign_operator();
1473 }
1474 
1475 //! Test exceptions safety guarantees for concurrent_vector move constructor
1476 //! \brief \ref requirement
1477 TEST_CASE("exception safety guarantees for concurrent_vector move constructor") {
1478     move_support_tests::test_ex_move_constructor<c_vector_type>();
1479 }
1480 
1481 //! Test exceptions safety guarantees for concurrent_vector move assignment
1482 //! \brief \ref requirement
1483 TEST_CASE("test exception safety on concurrent_vector move assignment") {
1484     test_ex_move_assignment();
1485 }
1486 #endif
1487 //! Test push_back in move only container
1488 //! \brief \ref requirement
1489 TEST_CASE("testing push_back move only container"){
1490     TestPushBackMoveOnlyContainer();
1491 }
1492 
1493 //! Test types for std::iterator_traits in concurrent_vector::iterator
1494 //! \brief \ref requirement
1495 TEST_CASE("testing std::iterator_traits for concurrent_vector::iterator"){
1496     TestIteratorTraits<oneapi::tbb::concurrent_vector<move_support_tests::Foo>::iterator,move_support_tests::Foo>();
1497 }
1498 
1499 //! Test types for std::iterator_traits in concurrent_vector::const_iterator
1500 //! \brief \ref requirement
1501 TEST_CASE("testing std::iterator_traits for concurrent_vector::const_iterator"){
1502     TestIteratorTraits<oneapi::tbb::concurrent_vector<move_support_tests::Foo>::const_iterator,const move_support_tests::Foo>();
1503 }
1504 
1505 //! Test initializer_list support
1506 //! \brief \ref interface \ref requirement
1507 TEST_CASE("testing initializer_list support"){
1508     TestInitList();
1509 }
1510 
1511 //! Test move constructor
1512 //! \brief \ref interface \ref requirement
1513 TEST_CASE("testing move constructor"){
1514     move_support_tests::test_move_constructor<c_vector_type>();
1515 }
1516 
1517 //! Test move assign operator
1518 //! \brief \ref interface \ref requirement
1519 TEST_CASE("testing move assign operator"){
1520     move_support_tests::test_move_assignment<c_vector_type>();
1521 }
1522 
1523 //! Test constructor with move iterators
1524 //! \brief \ref requirement
1525 TEST_CASE("testing constructor with move iterators"){
1526     move_support_tests::test_constructor_with_move_iterators<c_vector_type>();
1527 }
1528 
1529 //! Test assign with move iterators
1530 //! \brief \ref interface \ref requirement
1531 TEST_CASE("testing assign with move iterators"){
1532     move_support_tests::test_assign_with_move_iterators<c_vector_type>();
1533 }
1534 
1535 //! Test grow_by with move iterator
1536 //! \brief \ref requirement
1537 TEST_CASE("testing serial grow_by with move iterator"){
1538     TestSerialGrowByWithMoveIterators();
1539 }
1540 
1541 //! Test grow_by with move iterator
1542 //! \brief \ref requirement
1543 TEST_CASE("testing serial move in shrink_to_fit"){
1544     TestSerialMoveInShrinkToFit();
1545 }
1546 
1547 //! Test concurrent grow
1548 //! \brief \ref requirement
1549 TEST_CASE("testing concurrency"){
1550     REQUIRE(!move_support_tests::foo_count);
1551     for (std::size_t p = 1; p <= 4; ++p) {
1552         oneapi::tbb::global_control limit(oneapi::tbb::global_control::max_allowed_parallelism, p);
1553         TestParallelFor(p);
1554         TestConcurrentGrowToAtLeast();
1555         TestConcurrentGrowBy();
1556     }
1557 
1558     REQUIRE(!move_support_tests::foo_count);
1559 }
1560 
1561 //! Test assign operations
1562 //! \brief \ref interface \ref requirement
1563 TEST_CASE("testing comparison on assign operations"){
1564     TestComparison();
1565 }
1566 
1567 //! Test allocator_traits support in concurrent_vector
1568 //! \brief \ref requirement
1569 TEST_CASE("test allocator_traits support in concurrent_vector") {
1570     test_allocator_traits_support<c_vector_type>();
1571 }
1572 
1573 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
1574 //! Test deduction guides
1575 //! \brief \ref requirement
1576 TEST_CASE("testing deduction guides"){
1577     TestDeductionGuides<oneapi::tbb::concurrent_vector>();
1578 }
1579 #endif
1580 
1581 //! Test concurrent_vector comparisons
1582 //! \brief \ref interface \ref requirement
1583 TEST_CASE("concurrent_vector comparisons") {
1584     TestVectorComparisons();
1585 }
1586 
1587 //! Test concurrent_vector iterators comparisons
1588 //! \brief \ref interface \ref requirement
1589 TEST_CASE("concurrent_vector iterators comparisons") {
1590     TestVectorIteratorComparisons();
1591 }
1592