1 /* 2 Copyright (c) 2005-2021 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 #if _MSC_VER 18 #if __INTEL_COMPILER 19 #pragma warning(disable : 2586) // decorated name length exceeded, name was truncated 20 #else 21 // Workaround for vs2015 and warning name was longer than the compiler limit (4096). 22 #pragma warning (disable: 4503) 23 #endif 24 #endif 25 26 #include <common/test.h> 27 #include <common/utils.h> 28 #include <common/range_based_for_support.h> 29 #include <common/custom_allocators.h> 30 #include <common/containers_common.h> 31 #define TBB_DEFINE_STD_HASH_SPECIALIZATIONS 1 32 #include <tbb/concurrent_hash_map.h> 33 #include <tbb/parallel_for.h> 34 #include <common/concurrent_associative_common.h> 35 #include <vector> 36 #include <list> 37 #include <algorithm> 38 #include <functional> 39 #include <scoped_allocator> 40 41 //! \file test_concurrent_hash_map.cpp 42 //! \brief Test for [containers.concurrent_hash_map containers.tbb_hash_compare] specification 43 44 void TestRangeBasedFor(){ 45 using namespace range_based_for_support_tests; 46 47 INFO("testing range based for loop compatibility \n"); 48 using ch_map = tbb::concurrent_hash_map<int,int>; 49 ch_map a_ch_map; 50 51 const int sequence_length = 100; 52 for (int i = 1; i <= sequence_length; ++i){ 53 a_ch_map.insert(ch_map::value_type(i,i)); 54 } 55 56 REQUIRE_MESSAGE((range_based_for_accumulate(a_ch_map, pair_second_summer(), 0) == gauss_summ_of_int_sequence(sequence_length)), 57 "incorrect accumulated value generated via range based for ?"); 58 } 59 60 // The helper to run a test only when a default construction is present. 61 template <bool default_construction_present> struct do_default_construction_test { 62 template<typename FuncType> void operator() ( FuncType func ) const { func(); } 63 }; 64 65 template <> struct do_default_construction_test<false> { 66 template<typename FuncType> void operator()( FuncType ) const {} 67 }; 68 69 template <typename Table> 70 class test_insert_by_key { 71 using value_type = typename Table::value_type; 72 Table &my_c; 73 const value_type &my_value; 74 public: 75 test_insert_by_key( Table &c, const value_type &value ) : my_c(c), my_value(value) {} 76 void operator()() const { 77 { 78 typename Table::accessor a; 79 CHECK(my_c.insert( a, my_value.first )); 80 CHECK(utils::IsEqual()(a->first, my_value.first)); 81 a->second = my_value.second; 82 } 83 { 84 typename Table::const_accessor ca; 85 CHECK(!my_c.insert( ca, my_value.first )); 86 CHECK(utils::IsEqual()(ca->first, my_value.first)); 87 CHECK(utils::IsEqual()(ca->second, my_value.second)); 88 } 89 } 90 }; 91 92 template <typename Table, typename Iterator, typename Range = typename Table::range_type> 93 class test_range { 94 using value_type = typename Table::value_type; 95 Table &my_c; 96 const std::list<value_type> &my_lst; 97 std::vector<detail::atomic_type<bool>>& my_marks; 98 public: 99 test_range( Table &c, const std::list<value_type> &lst, std::vector<detail::atomic_type<bool>> &marks ) : my_c(c), my_lst(lst), my_marks(marks) { 100 for (std::size_t i = 0; i < my_marks.size(); ++i) { 101 my_marks[i].store(false, std::memory_order_relaxed); 102 } 103 } 104 105 void operator()( const Range &r ) const { do_test_range( r.begin(), r.end() ); } 106 void do_test_range( Iterator i, Iterator j ) const { 107 for ( Iterator it = i; it != j; ) { 108 Iterator it_prev = it++; 109 typename std::list<value_type>::const_iterator it2 = std::search( my_lst.begin(), my_lst.end(), it_prev, it, utils::IsEqual() ); 110 CHECK(it2 != my_lst.end()); 111 typename std::list<value_type>::difference_type dist = std::distance( my_lst.begin(), it2 ); 112 CHECK(!my_marks[dist]); 113 my_marks[dist].store(true); 114 } 115 } 116 }; 117 118 template <bool default_construction_present, typename Table> 119 class check_value { 120 using const_iterator = typename Table::const_iterator; 121 using iterator = typename Table::iterator; 122 using size_type = typename Table::size_type; 123 Table &my_c; 124 public: 125 check_value( Table &c ) : my_c(c) {} 126 void operator()(const typename Table::value_type &value ) { 127 const Table &const_c = my_c; 128 CHECK(my_c.count( value.first ) == 1); 129 { // tests with a const accessor. 130 typename Table::const_accessor ca; 131 // find 132 CHECK(my_c.find( ca, value.first )); 133 CHECK(!ca.empty() ); 134 CHECK(utils::IsEqual()(ca->first, value.first)); 135 CHECK(utils::IsEqual()(ca->second, value.second)); 136 // erase 137 CHECK(my_c.erase( ca )); 138 CHECK(my_c.count( value.first ) == 0); 139 // insert (pair) 140 CHECK(my_c.insert( ca, value )); 141 CHECK(utils::IsEqual()(ca->first, value.first)); 142 CHECK(utils::IsEqual()(ca->second, value.second)); 143 } { // tests with a non-const accessor. 144 typename Table::accessor a; 145 // find 146 CHECK(my_c.find( a, value.first )); 147 CHECK(!a.empty() ); 148 CHECK(utils::IsEqual()(a->first, value.first)); 149 CHECK(utils::IsEqual()(a->second, value.second)); 150 // erase 151 CHECK(my_c.erase( a )); 152 CHECK(my_c.count( value.first ) == 0); 153 // insert 154 CHECK(my_c.insert( a, value )); 155 CHECK(utils::IsEqual()(a->first, value.first)); 156 CHECK(utils::IsEqual()(a->second, value.second)); 157 } 158 // erase by key 159 CHECK(my_c.erase( value.first )); 160 CHECK(my_c.count( value.first ) == 0); 161 do_default_construction_test<default_construction_present>()(test_insert_by_key<Table>( my_c, value )); 162 // insert by value 163 CHECK(my_c.insert( value ) != default_construction_present); 164 // equal_range 165 std::pair<iterator,iterator> r1 = my_c.equal_range( value.first ); 166 iterator r1_first_prev = r1.first++; 167 CHECK((utils::IsEqual()( *r1_first_prev, value ) && utils::IsEqual()( r1.first, r1.second ))); 168 std::pair<const_iterator,const_iterator> r2 = const_c.equal_range( value.first ); 169 const_iterator r2_first_prev = r2.first++; 170 CHECK((utils::IsEqual()( *r2_first_prev, value ) && utils::IsEqual()( r2.first, r2.second ))); 171 } 172 }; 173 174 template <typename Value, typename U = Value> 175 struct CompareTables { 176 template <typename T> 177 static bool IsEqual( const T& t1, const T& t2 ) { 178 return (t1 == t2) && !(t1 != t2); 179 } 180 }; 181 182 template <typename U> 183 struct CompareTables< std::pair<const std::weak_ptr<U>, std::weak_ptr<U> > > { 184 template <typename T> 185 static bool IsEqual( const T&, const T& ) { 186 /* do nothing for std::weak_ptr */ 187 return true; 188 } 189 }; 190 191 template <bool default_construction_present, typename Table> 192 void Examine( Table c, const std::list<typename Table::value_type> &lst) { 193 using const_table = const Table; 194 using const_iterator = typename Table::const_iterator; 195 using iterator = typename Table::iterator; 196 using value_type = typename Table::value_type; 197 using size_type = typename Table::size_type; 198 199 CHECK(!c.empty()); 200 CHECK(c.size() == lst.size()); 201 CHECK(c.max_size() >= c.size()); 202 203 const check_value<default_construction_present, Table> cv(c); 204 std::for_each( lst.begin(), lst.end(), cv ); 205 206 std::vector<detail::atomic_type<bool>> marks( lst.size() ); 207 208 test_range<Table,iterator>( c, lst, marks ).do_test_range( c.begin(), c.end() ); 209 CHECK(std::find( marks.begin(), marks.end(), false ) == marks.end()); 210 211 test_range<const_table,const_iterator>( c, lst, marks ).do_test_range( c.begin(), c.end() ); 212 CHECK(std::find( marks.begin(), marks.end(), false ) == marks.end()); 213 214 using range_type = typename Table::range_type; 215 tbb::parallel_for( c.range(), test_range<Table,typename range_type::iterator,range_type>( c, lst, marks ) ); 216 CHECK(std::find( marks.begin(), marks.end(), false ) == marks.end()); 217 218 const_table const_c = c; 219 CHECK(CompareTables<value_type>::IsEqual( c, const_c )); 220 221 const size_type new_bucket_count = 2*c.bucket_count(); 222 c.rehash( new_bucket_count ); 223 CHECK(c.bucket_count() >= new_bucket_count); 224 225 Table c2; 226 typename std::list<value_type>::const_iterator begin5 = lst.begin(); 227 std::advance( begin5, 5 ); 228 c2.insert( lst.begin(), begin5 ); 229 std::for_each( lst.begin(), begin5, check_value<default_construction_present, Table>( c2 ) ); 230 231 c2.swap( c ); 232 CHECK(CompareTables<value_type>::IsEqual( c2, const_c )); 233 CHECK(c.size() == 5); 234 std::for_each( lst.begin(), lst.end(), check_value<default_construction_present,Table>(c2) ); 235 236 swap( c, c2 ); 237 CHECK(CompareTables<value_type>::IsEqual( c, const_c )); 238 CHECK(c2.size() == 5); 239 240 c2.clear(); 241 CHECK(CompareTables<value_type>::IsEqual( c2, Table() )); 242 243 typename Table::allocator_type a = c.get_allocator(); 244 value_type *ptr = a.allocate(1); 245 CHECK(ptr); 246 a.deallocate( ptr, 1 ); 247 } 248 249 template <typename T> 250 struct debug_hash_compare : public tbb::detail::d1::tbb_hash_compare<T> {}; 251 252 template <bool default_construction_present, typename Value> 253 void TypeTester( const std::list<Value> &lst ) { 254 using first_type = typename Value::first_type; 255 using key_type = typename std::remove_const<first_type>::type; 256 using second_type = typename Value::second_type; 257 using ch_map = tbb::concurrent_hash_map<key_type, second_type>; 258 debug_hash_compare<key_type> compare{}; 259 // Construct an empty hash map. 260 ch_map c1; 261 c1.insert( lst.begin(), lst.end() ); 262 Examine<default_construction_present>( c1, lst ); 263 264 // Constructor from initializer_list. 265 typename std::list<Value>::const_iterator it = lst.begin(); 266 std::initializer_list<Value> il = { *it++, *it++, *it++ }; 267 ch_map c2( il ); 268 c2.insert( it, lst.end() ); 269 Examine<default_construction_present>( c2, lst ); 270 271 // Constructor from initializer_list and compare object 272 ch_map c3( il, compare); 273 c3.insert( it, lst.end() ); 274 Examine<default_construction_present>( c3, lst ); 275 276 // Constructor from initializer_list, compare object and allocator 277 ch_map c4( il, compare, typename ch_map::allocator_type()); 278 c4.insert( it, lst.end()); 279 Examine<default_construction_present>( c4, lst ); 280 281 // Copying constructor. 282 ch_map c5(c1); 283 Examine<default_construction_present>( c5, lst ); 284 // Construct with non-default allocator 285 using ch_map_debug_alloc = tbb::concurrent_hash_map<key_type, second_type, 286 tbb::detail::d1::tbb_hash_compare<key_type>, 287 LocalCountingAllocator<std::allocator<Value>>>; 288 ch_map_debug_alloc c6; 289 c6.insert( lst.begin(), lst.end() ); 290 Examine<default_construction_present>( c6, lst ); 291 // Copying constructor 292 ch_map_debug_alloc c7(c6); 293 Examine<default_construction_present>( c7, lst ); 294 // Construction empty table with n preallocated buckets. 295 ch_map c8( lst.size() ); 296 c8.insert( lst.begin(), lst.end() ); 297 Examine<default_construction_present>( c8, lst ); 298 ch_map_debug_alloc c9( lst.size() ); 299 c9.insert( lst.begin(), lst.end() ); 300 Examine<default_construction_present>( c9, lst ); 301 // Construction with copying iteration range. 302 ch_map c10_1( c1.begin(), c1.end() ), c10_2(c1.cbegin(), c1.cend()); 303 Examine<default_construction_present>( c10_1, lst ); 304 Examine<default_construction_present>( c10_2, lst ); 305 // Construction with copying iteration range and given allocator instance. 306 LocalCountingAllocator<std::allocator<Value>> allocator; 307 ch_map_debug_alloc c11( lst.begin(), lst.end(), allocator ); 308 Examine<default_construction_present>( c11, lst ); 309 310 using ch_map_debug_hash = tbb::concurrent_hash_map<key_type, second_type, 311 debug_hash_compare<key_type>, 312 typename ch_map::allocator_type>; 313 314 // Constructor with two iterators and hash_compare 315 ch_map_debug_hash c12(c1.begin(), c1.end(), compare); 316 Examine<default_construction_present>( c12, lst ); 317 318 ch_map_debug_hash c13(c1.begin(), c1.end(), compare, typename ch_map::allocator_type()); 319 Examine<default_construction_present>( c13, lst ); 320 } 321 322 void TestSpecificTypes() { 323 const int NUMBER = 10; 324 325 using int_int_t = std::pair<const int, int>; 326 std::list<int_int_t> arrIntInt; 327 for ( int i=0; i<NUMBER; ++i ) arrIntInt.push_back( int_int_t(i, NUMBER-i) ); 328 TypeTester</*default_construction_present = */true>( arrIntInt ); 329 330 using ref_int_t = std::pair<const std::reference_wrapper<const int>, int>; 331 std::list<ref_int_t> arrRefInt; 332 for ( std::list<int_int_t>::iterator it = arrIntInt.begin(); it != arrIntInt.end(); ++it ) 333 arrRefInt.push_back( ref_int_t( it->first, it->second ) ); 334 TypeTester</*default_construction_present = */true>( arrRefInt ); 335 336 using int_ref_t = std::pair< const int, std::reference_wrapper<int> >; 337 std::list<int_ref_t> arrIntRef; 338 for ( std::list<int_int_t>::iterator it = arrIntInt.begin(); it != arrIntInt.end(); ++it ) 339 arrIntRef.push_back( int_ref_t( it->first, it->second ) ); 340 TypeTester</*default_construction_present = */false>( arrIntRef ); 341 342 using shr_shr_t = std::pair< const std::shared_ptr<int>, std::shared_ptr<int> >; 343 std::list<shr_shr_t> arrShrShr; 344 for ( int i=0; i<NUMBER; ++i ) { 345 const int NUMBER_minus_i = NUMBER - i; 346 arrShrShr.push_back( shr_shr_t( std::make_shared<int>(i), std::make_shared<int>(NUMBER_minus_i) ) ); 347 } 348 TypeTester< /*default_construction_present = */true>( arrShrShr ); 349 350 using wk_wk_t = std::pair< const std::weak_ptr<int>, std::weak_ptr<int> >; 351 std::list< wk_wk_t > arrWkWk; 352 std::copy( arrShrShr.begin(), arrShrShr.end(), std::back_inserter(arrWkWk) ); 353 TypeTester< /*default_construction_present = */true>( arrWkWk ); 354 355 // Check working with deprecated hashers 356 using pair_key_type = std::pair<int, int>; 357 using pair_int_t = std::pair<const pair_key_type, int>; 358 std::list<pair_int_t> arr_pair_int; 359 for (int i = 0; i < NUMBER; ++i) { 360 arr_pair_int.push_back(pair_int_t(pair_key_type{i, i}, i)); 361 } 362 TypeTester</*default_construction_present = */true>(arr_pair_int); 363 364 using tbb_string_key_type = std::basic_string<char, std::char_traits<char>, tbb::tbb_allocator<char>>; 365 using pair_tbb_string_int_t = std::pair<const tbb_string_key_type, int>; 366 std::list<pair_tbb_string_int_t> arr_pair_string_int; 367 for (int i = 0; i < NUMBER; ++i) { 368 tbb_string_key_type key(i, char(i)); 369 arr_pair_string_int.push_back(pair_tbb_string_int_t(key, i)); 370 } 371 TypeTester</*default_construction_present = */true>(arr_pair_string_int); 372 } 373 374 struct custom_hash_compare { 375 template<typename Allocator> 376 size_t hash(const AllocatorAwareData<Allocator>& key) const { 377 return my_hash_compare.hash(key.value()); 378 } 379 380 template<typename Allocator> 381 bool equal(const AllocatorAwareData<Allocator>& key1, const AllocatorAwareData<Allocator>& key2) const { 382 return my_hash_compare.equal(key1.value(), key2.value()); 383 } 384 385 private: 386 tbb::tbb_hash_compare<int> my_hash_compare; 387 }; 388 389 void TestScopedAllocator() { 390 using allocator_data_type = AllocatorAwareData<std::scoped_allocator_adaptor<tbb::tbb_allocator<int>>>; 391 using allocator_type = std::scoped_allocator_adaptor<tbb::tbb_allocator<std::pair<const allocator_data_type, allocator_data_type>>>; 392 using hash_map_type = tbb::concurrent_hash_map<allocator_data_type, allocator_data_type, 393 custom_hash_compare, allocator_type>; 394 395 allocator_type allocator; 396 allocator_data_type key1(1, allocator), key2(2, allocator); 397 allocator_data_type data1(1, allocator), data2(data1, allocator); 398 hash_map_type map1(allocator), map2(allocator); 399 400 hash_map_type::value_type v1(key1, data1), v2(key2, data2); 401 402 auto init_list = { v1, v2 }; 403 404 allocator_data_type::assert_on_constructions = true; 405 map1.emplace(key1, data1); 406 map2.emplace(key2, std::move(data2)); 407 408 map1.clear(); 409 map2.clear(); 410 411 map1.insert(v1); 412 map2.insert(std::move(v2)); 413 414 map1.clear(); 415 map2.clear(); 416 417 map1.insert(init_list); 418 419 map1.clear(); 420 map2.clear(); 421 422 hash_map_type::accessor a; 423 map2.insert(a, allocator_data_type(3)); 424 a.release(); 425 426 map1 = map2; 427 map2 = std::move(map1); 428 429 hash_map_type map3(allocator); 430 map3.rehash(1000); 431 map3 = map2; 432 } 433 434 // A test for undocumented member function internal_fast_find 435 // which is declared protected in concurrent_hash_map for internal TBB use 436 void TestInternalFastFind() { 437 typedef tbb::concurrent_hash_map<int, int> basic_chmap_type; 438 typedef basic_chmap_type::const_pointer const_pointer; 439 440 class chmap : public basic_chmap_type { 441 public: 442 chmap() : basic_chmap_type() {} 443 444 using basic_chmap_type::internal_fast_find; 445 }; 446 447 chmap m; 448 int sz = 100; 449 450 for (int i = 0; i != sz; ++i) { 451 m.insert(std::make_pair(i, i * i)); 452 } 453 REQUIRE_MESSAGE(m.size() == 100, "Incorrect concurrent_hash_map size"); 454 455 for (int i = 0; i != sz; ++i) { 456 const_pointer res = m.internal_fast_find(i); 457 REQUIRE_MESSAGE(res != nullptr, "Incorrect internal_fast_find return value for existing key"); 458 basic_chmap_type::value_type val = *res; 459 REQUIRE_MESSAGE(val.first == i, "Incorrect key in internal_fast_find return value"); 460 REQUIRE_MESSAGE(val.second == i * i, "Incorrect mapped in internal_fast_find return value"); 461 } 462 463 for (int i = sz; i != 2 * sz; ++i) { 464 const_pointer res = m.internal_fast_find(i); 465 REQUIRE_MESSAGE(res == nullptr, "Incorrect internal_fast_find return value for not existing key"); 466 } 467 } 468 469 struct default_container_traits { 470 template <typename container_type, typename iterator_type> 471 static container_type& construct_container(typename std::aligned_storage<sizeof(container_type)>::type& storage, iterator_type begin, iterator_type end){ 472 container_type* ptr = reinterpret_cast<container_type*>(&storage); 473 new (ptr) container_type(begin, end); 474 return *ptr; 475 } 476 477 template <typename container_type, typename iterator_type, typename allocator_type> 478 static container_type& construct_container(typename std::aligned_storage<sizeof(container_type)>::type& storage, iterator_type begin, iterator_type end, allocator_type const& a){ 479 container_type* ptr = reinterpret_cast<container_type*>(&storage); 480 new (ptr) container_type(begin, end, a); 481 return *ptr; 482 } 483 }; 484 485 struct hash_map_traits : default_container_traits { 486 enum{ expected_number_of_items_to_allocate_for_steal_move = 0 }; 487 488 template<typename T> 489 struct hash_compare { 490 bool equal( const T& lhs, const T& rhs ) const { 491 return lhs==rhs; 492 } 493 size_t hash( const T& k ) const { 494 return my_hash_func(k); 495 } 496 std::hash<T> my_hash_func; 497 }; 498 499 template <typename T, typename Allocator> 500 using container_type = tbb::concurrent_hash_map<T, T, hash_compare<T>, Allocator>; 501 502 template <typename T> 503 using container_value_type = std::pair<const T, T>; 504 505 template<typename element_type, typename allocator_type> 506 struct apply { 507 using type = tbb::concurrent_hash_map<element_type, element_type, hash_compare<element_type>, allocator_type>; 508 }; 509 510 using init_iterator_type = move_support_tests::FooPairIterator; 511 template <typename hash_map_type, typename iterator> 512 static bool equal(hash_map_type const& c, iterator begin, iterator end){ 513 bool equal_sizes = ( static_cast<size_t>(std::distance(begin, end)) == c.size() ); 514 if (!equal_sizes) 515 return false; 516 517 for (iterator it = begin; it != end; ++it ){ 518 if (c.count( (*it).first) == 0){ 519 return false; 520 } 521 } 522 return true; 523 } 524 }; 525 526 //! Test of insert operation 527 //! \brief \ref error_guessing 528 TEST_CASE("testing range based for support"){ 529 TestRangeBasedFor(); 530 } 531 532 //! Test concurrent_hash_map with specific key/mapped types 533 //! \brief \ref regression \ref error_guessing 534 TEST_CASE("testing concurrent_hash_map with specific key/mapped types") { 535 TestSpecificTypes(); 536 } 537 538 //! Test work with scoped allocator 539 //! \brief \ref regression 540 TEST_CASE("testing work with scoped allocator") { 541 TestScopedAllocator(); 542 } 543 544 //! Test internal fast find for concurrent_hash_map 545 //! \brief \ref regression 546 TEST_CASE("testing internal fast find for concurrent_hash_map") { 547 TestInternalFastFind(); 548 } 549 550 //! Test constructor with move iterators 551 //! \brief \ref error_guessing 552 TEST_CASE("testing constructor with move iterators"){ 553 move_support_tests::test_constructor_with_move_iterators<hash_map_traits>(); 554 } 555 556 #if TBB_USE_EXCEPTIONS 557 //! Test exception in constructors 558 //! \brief \ref regression \ref error_guessing 559 TEST_CASE("Test exception in constructors") { 560 using allocator_type = StaticSharedCountingAllocator<std::allocator<std::pair<const int, int>>>; 561 using map_type = tbb::concurrent_hash_map<int, int, tbb::tbb_hash_compare<int>, allocator_type>; 562 563 auto init_list = {std::pair<const int, int>(1, 42), std::pair<const int, int>(2, 42), std::pair<const int, int>(3, 42), 564 std::pair<const int, int>(4, 42), std::pair<const int, int>(5, 42), std::pair<const int, int>(6, 42)}; 565 map_type map(init_list); 566 567 allocator_type::set_limits(1); 568 REQUIRE_THROWS_AS( [&] { 569 map_type map1(map); 570 utils::suppress_unused_warning(map1); 571 }(), const std::bad_alloc); 572 573 REQUIRE_THROWS_AS( [&] { 574 map_type map2(init_list.begin(), init_list.end()); 575 utils::suppress_unused_warning(map2); 576 }(), const std::bad_alloc); 577 578 tbb::tbb_hash_compare<int> test_hash; 579 580 REQUIRE_THROWS_AS( [&] { 581 map_type map3(init_list.begin(), init_list.end(), test_hash); 582 utils::suppress_unused_warning(map3); 583 }(), const std::bad_alloc); 584 585 REQUIRE_THROWS_AS( [&] { 586 map_type map4(init_list, test_hash); 587 utils::suppress_unused_warning(map4); 588 }(), const std::bad_alloc); 589 590 REQUIRE_THROWS_AS( [&] { 591 map_type map5(init_list); 592 utils::suppress_unused_warning(map5); 593 }(), const std::bad_alloc); 594 595 allocator_type::set_limits(0); 596 map_type big_map{}; 597 for (std::size_t i = 0; i < 1000; ++i) { 598 big_map.insert(std::pair<const int, int>(i, 42)); 599 } 600 601 allocator_type::init_counters(); 602 allocator_type::set_limits(300); 603 REQUIRE_THROWS_AS( [&] { 604 map_type map6(big_map); 605 utils::suppress_unused_warning(map6); 606 }(), const std::bad_alloc); 607 } 608 #endif // TBB_USE_EXCEPTIONS 609 610 //! \brief \ref error_guessing 611 TEST_CASE("swap with NotAlwaysEqualAllocator allocators"){ 612 using allocator_type = NotAlwaysEqualAllocator<std::pair<const int, int>>; 613 using map_type = tbb::concurrent_hash_map<int, int, tbb::tbb_hash_compare<int>, allocator_type>; 614 615 map_type map1{}; 616 map_type map2({{42, 42}, {24, 42}}); 617 map_type map3(map2); 618 619 swap(map1, map2); 620 621 CHECK(map2.empty()); 622 CHECK(map1 == map3); 623 } 624