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