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