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