151c0b2f7Stbbdev /*
2*c4a799dfSJhaShweta1     Copyright (c) 2005-2023 Intel Corporation
351c0b2f7Stbbdev 
451c0b2f7Stbbdev     Licensed under the Apache License, Version 2.0 (the "License");
551c0b2f7Stbbdev     you may not use this file except in compliance with the License.
651c0b2f7Stbbdev     You may obtain a copy of the License at
751c0b2f7Stbbdev 
851c0b2f7Stbbdev         http://www.apache.org/licenses/LICENSE-2.0
951c0b2f7Stbbdev 
1051c0b2f7Stbbdev     Unless required by applicable law or agreed to in writing, software
1151c0b2f7Stbbdev     distributed under the License is distributed on an "AS IS" BASIS,
1251c0b2f7Stbbdev     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1351c0b2f7Stbbdev     See the License for the specific language governing permissions and
1451c0b2f7Stbbdev     limitations under the License.
1551c0b2f7Stbbdev */
1651c0b2f7Stbbdev 
17b15aabb3Stbbdev #if _MSC_VER
18b15aabb3Stbbdev #if __INTEL_COMPILER
19b15aabb3Stbbdev     #pragma warning(disable : 2586) // decorated name length exceeded, name was truncated
20b15aabb3Stbbdev #else
2151c0b2f7Stbbdev     // Workaround for vs2015 and warning name was longer than the compiler limit (4096).
2251c0b2f7Stbbdev     #pragma warning (disable: 4503)
2351c0b2f7Stbbdev #endif
24b15aabb3Stbbdev #endif
2551c0b2f7Stbbdev 
264523a761Stbbdev #define TBB_DEFINE_STD_HASH_SPECIALIZATIONS 1
274523a761Stbbdev #define TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS 1
2851c0b2f7Stbbdev #include <common/test.h>
2951c0b2f7Stbbdev #include <common/utils.h>
3051c0b2f7Stbbdev #include <common/range_based_for_support.h>
3151c0b2f7Stbbdev #include <common/custom_allocators.h>
3251c0b2f7Stbbdev #include <common/containers_common.h>
33478de5b1Stbbdev #include <common/concepts_common.h>
3451c0b2f7Stbbdev #include <tbb/concurrent_hash_map.h>
3551c0b2f7Stbbdev #include <tbb/parallel_for.h>
3651c0b2f7Stbbdev #include <common/concurrent_associative_common.h>
3751c0b2f7Stbbdev #include <vector>
3851c0b2f7Stbbdev #include <list>
3951c0b2f7Stbbdev #include <algorithm>
4051c0b2f7Stbbdev #include <functional>
4151c0b2f7Stbbdev #include <scoped_allocator>
429e15720bStbbdev #include <mutex>
4351c0b2f7Stbbdev 
4451c0b2f7Stbbdev //! \file test_concurrent_hash_map.cpp
4551c0b2f7Stbbdev //! \brief Test for [containers.concurrent_hash_map containers.tbb_hash_compare] specification
4651c0b2f7Stbbdev 
TestRangeBasedFor()4751c0b2f7Stbbdev void TestRangeBasedFor(){
4851c0b2f7Stbbdev     using namespace range_based_for_support_tests;
4951c0b2f7Stbbdev 
5051c0b2f7Stbbdev     INFO("testing range based for loop compatibility \n");
5151c0b2f7Stbbdev     using ch_map = tbb::concurrent_hash_map<int,int>;
5251c0b2f7Stbbdev     ch_map a_ch_map;
5351c0b2f7Stbbdev 
5451c0b2f7Stbbdev     const int sequence_length = 100;
5551c0b2f7Stbbdev     for (int i = 1; i <= sequence_length; ++i){
5651c0b2f7Stbbdev         a_ch_map.insert(ch_map::value_type(i,i));
5751c0b2f7Stbbdev     }
5851c0b2f7Stbbdev 
5951c0b2f7Stbbdev     REQUIRE_MESSAGE((range_based_for_accumulate(a_ch_map, pair_second_summer(), 0) == gauss_summ_of_int_sequence(sequence_length)),
6051c0b2f7Stbbdev         "incorrect accumulated value generated via range based for ?");
6151c0b2f7Stbbdev }
6251c0b2f7Stbbdev 
6351c0b2f7Stbbdev // The helper to run a test only when a default construction is present.
6451c0b2f7Stbbdev template <bool default_construction_present> struct do_default_construction_test {
operator ()do_default_construction_test6551c0b2f7Stbbdev     template<typename FuncType> void operator() ( FuncType func ) const { func(); }
6651c0b2f7Stbbdev };
6751c0b2f7Stbbdev 
6851c0b2f7Stbbdev template <> struct do_default_construction_test<false> {
operator ()do_default_construction_test6951c0b2f7Stbbdev     template<typename FuncType> void operator()( FuncType ) const {}
7051c0b2f7Stbbdev };
7151c0b2f7Stbbdev 
7251c0b2f7Stbbdev template <typename Table>
7351c0b2f7Stbbdev class test_insert_by_key {
7451c0b2f7Stbbdev     using value_type = typename Table::value_type;
7551c0b2f7Stbbdev     Table &my_c;
7651c0b2f7Stbbdev     const value_type &my_value;
7751c0b2f7Stbbdev public:
test_insert_by_key(Table & c,const value_type & value)7851c0b2f7Stbbdev     test_insert_by_key( Table &c, const value_type &value ) : my_c(c), my_value(value) {}
operator ()() const7951c0b2f7Stbbdev     void operator()() const {
8051c0b2f7Stbbdev         {
8151c0b2f7Stbbdev             typename Table::accessor a;
8251c0b2f7Stbbdev             CHECK(my_c.insert( a, my_value.first ));
8351c0b2f7Stbbdev             CHECK(utils::IsEqual()(a->first, my_value.first));
8451c0b2f7Stbbdev             a->second = my_value.second;
8551c0b2f7Stbbdev         }
8651c0b2f7Stbbdev         {
8751c0b2f7Stbbdev             typename Table::const_accessor ca;
8851c0b2f7Stbbdev             CHECK(!my_c.insert( ca, my_value.first ));
8951c0b2f7Stbbdev             CHECK(utils::IsEqual()(ca->first, my_value.first));
9051c0b2f7Stbbdev             CHECK(utils::IsEqual()(ca->second, my_value.second));
9151c0b2f7Stbbdev         }
9251c0b2f7Stbbdev     }
9351c0b2f7Stbbdev };
9451c0b2f7Stbbdev 
9551c0b2f7Stbbdev template <typename Table, typename Iterator, typename Range = typename Table::range_type>
9651c0b2f7Stbbdev class test_range {
9751c0b2f7Stbbdev     using value_type = typename Table::value_type;
9851c0b2f7Stbbdev     Table &my_c;
9951c0b2f7Stbbdev     const std::list<value_type> &my_lst;
10051c0b2f7Stbbdev     std::vector<detail::atomic_type<bool>>& my_marks;
10151c0b2f7Stbbdev public:
test_range(Table & c,const std::list<value_type> & lst,std::vector<detail::atomic_type<bool>> & marks)10251c0b2f7Stbbdev     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) {
10351c0b2f7Stbbdev         for (std::size_t i = 0; i < my_marks.size(); ++i) {
10451c0b2f7Stbbdev             my_marks[i].store(false, std::memory_order_relaxed);
10551c0b2f7Stbbdev         }
10651c0b2f7Stbbdev     }
10751c0b2f7Stbbdev 
operator ()(const Range & r) const10851c0b2f7Stbbdev     void operator()( const Range &r ) const { do_test_range( r.begin(), r.end() ); }
do_test_range(Iterator i,Iterator j) const10951c0b2f7Stbbdev     void do_test_range( Iterator i, Iterator j ) const {
11051c0b2f7Stbbdev         for ( Iterator it = i; it != j; ) {
11151c0b2f7Stbbdev             Iterator it_prev = it++;
11251c0b2f7Stbbdev             typename std::list<value_type>::const_iterator it2 = std::search( my_lst.begin(), my_lst.end(), it_prev, it, utils::IsEqual() );
11351c0b2f7Stbbdev             CHECK(it2 != my_lst.end());
11451c0b2f7Stbbdev             typename std::list<value_type>::difference_type dist = std::distance( my_lst.begin(), it2 );
11551c0b2f7Stbbdev             CHECK(!my_marks[dist]);
11651c0b2f7Stbbdev             my_marks[dist].store(true);
11751c0b2f7Stbbdev         }
11851c0b2f7Stbbdev     }
11951c0b2f7Stbbdev };
12051c0b2f7Stbbdev 
12151c0b2f7Stbbdev template <bool default_construction_present, typename Table>
12251c0b2f7Stbbdev class check_value {
12351c0b2f7Stbbdev     using const_iterator = typename Table::const_iterator;
12451c0b2f7Stbbdev     using iterator = typename Table::iterator;
12551c0b2f7Stbbdev     using size_type = typename Table::size_type;
12651c0b2f7Stbbdev     Table &my_c;
12751c0b2f7Stbbdev public:
check_value(Table & c)12851c0b2f7Stbbdev     check_value( Table &c ) : my_c(c) {}
operator ()(const typename Table::value_type & value)12951c0b2f7Stbbdev     void operator()(const typename Table::value_type &value ) {
13051c0b2f7Stbbdev         const Table &const_c = my_c;
13151c0b2f7Stbbdev         CHECK(my_c.count( value.first ) == 1);
13251c0b2f7Stbbdev         { // tests with a const accessor.
13351c0b2f7Stbbdev             typename Table::const_accessor ca;
13451c0b2f7Stbbdev             // find
13551c0b2f7Stbbdev             CHECK(my_c.find( ca, value.first ));
13651c0b2f7Stbbdev             CHECK(!ca.empty() );
13751c0b2f7Stbbdev             CHECK(utils::IsEqual()(ca->first, value.first));
13851c0b2f7Stbbdev             CHECK(utils::IsEqual()(ca->second, value.second));
13951c0b2f7Stbbdev             // erase
14051c0b2f7Stbbdev             CHECK(my_c.erase( ca ));
14151c0b2f7Stbbdev             CHECK(my_c.count( value.first ) == 0);
14251c0b2f7Stbbdev             // insert (pair)
14351c0b2f7Stbbdev             CHECK(my_c.insert( ca, value ));
14451c0b2f7Stbbdev             CHECK(utils::IsEqual()(ca->first, value.first));
14551c0b2f7Stbbdev             CHECK(utils::IsEqual()(ca->second, value.second));
14651c0b2f7Stbbdev         } { // tests with a non-const accessor.
14751c0b2f7Stbbdev             typename Table::accessor a;
14851c0b2f7Stbbdev             // find
14951c0b2f7Stbbdev             CHECK(my_c.find( a, value.first ));
15051c0b2f7Stbbdev             CHECK(!a.empty() );
15151c0b2f7Stbbdev             CHECK(utils::IsEqual()(a->first, value.first));
15251c0b2f7Stbbdev             CHECK(utils::IsEqual()(a->second, value.second));
15351c0b2f7Stbbdev             // erase
15451c0b2f7Stbbdev             CHECK(my_c.erase( a ));
15551c0b2f7Stbbdev             CHECK(my_c.count( value.first ) == 0);
15651c0b2f7Stbbdev             // insert
15751c0b2f7Stbbdev             CHECK(my_c.insert( a, value ));
15851c0b2f7Stbbdev             CHECK(utils::IsEqual()(a->first, value.first));
15951c0b2f7Stbbdev             CHECK(utils::IsEqual()(a->second, value.second));
16051c0b2f7Stbbdev         }
16151c0b2f7Stbbdev         // erase by key
16251c0b2f7Stbbdev         CHECK(my_c.erase( value.first ));
16351c0b2f7Stbbdev         CHECK(my_c.count( value.first ) == 0);
16451c0b2f7Stbbdev         do_default_construction_test<default_construction_present>()(test_insert_by_key<Table>( my_c, value ));
16551c0b2f7Stbbdev         // insert by value
16651c0b2f7Stbbdev         CHECK(my_c.insert( value ) != default_construction_present);
16751c0b2f7Stbbdev         // equal_range
16851c0b2f7Stbbdev         std::pair<iterator,iterator> r1 = my_c.equal_range( value.first );
16951c0b2f7Stbbdev         iterator r1_first_prev = r1.first++;
17051c0b2f7Stbbdev         CHECK((utils::IsEqual()( *r1_first_prev, value ) && utils::IsEqual()( r1.first, r1.second )));
17151c0b2f7Stbbdev         std::pair<const_iterator,const_iterator> r2 = const_c.equal_range( value.first );
17251c0b2f7Stbbdev         const_iterator r2_first_prev = r2.first++;
17351c0b2f7Stbbdev         CHECK((utils::IsEqual()( *r2_first_prev, value ) && utils::IsEqual()( r2.first, r2.second )));
17451c0b2f7Stbbdev     }
17551c0b2f7Stbbdev };
17651c0b2f7Stbbdev 
17751c0b2f7Stbbdev template <typename Value, typename U = Value>
17851c0b2f7Stbbdev struct CompareTables {
17951c0b2f7Stbbdev     template <typename T>
IsEqualCompareTables18051c0b2f7Stbbdev     static bool IsEqual( const T& t1, const T& t2 ) {
18151c0b2f7Stbbdev         return (t1 == t2) && !(t1 != t2);
18251c0b2f7Stbbdev     }
18351c0b2f7Stbbdev };
18451c0b2f7Stbbdev 
18551c0b2f7Stbbdev template <typename U>
18651c0b2f7Stbbdev struct CompareTables< std::pair<const std::weak_ptr<U>, std::weak_ptr<U> > > {
18751c0b2f7Stbbdev     template <typename T>
IsEqualCompareTables18851c0b2f7Stbbdev     static bool IsEqual( const T&, const T& ) {
18951c0b2f7Stbbdev         /* do nothing for std::weak_ptr */
19051c0b2f7Stbbdev         return true;
19151c0b2f7Stbbdev     }
19251c0b2f7Stbbdev };
19351c0b2f7Stbbdev 
19451c0b2f7Stbbdev template <bool default_construction_present, typename Table>
Examine(Table c,const std::list<typename Table::value_type> & lst)19551c0b2f7Stbbdev void Examine( Table c, const std::list<typename Table::value_type> &lst) {
19651c0b2f7Stbbdev     using const_table = const Table;
19751c0b2f7Stbbdev     using const_iterator = typename Table::const_iterator;
19851c0b2f7Stbbdev     using iterator = typename Table::iterator;
19951c0b2f7Stbbdev     using value_type = typename Table::value_type;
20051c0b2f7Stbbdev     using size_type = typename Table::size_type;
20151c0b2f7Stbbdev 
20251c0b2f7Stbbdev     CHECK(!c.empty());
20351c0b2f7Stbbdev     CHECK(c.size() == lst.size());
20451c0b2f7Stbbdev     CHECK(c.max_size() >= c.size());
20551c0b2f7Stbbdev 
20651c0b2f7Stbbdev     const check_value<default_construction_present, Table> cv(c);
20751c0b2f7Stbbdev     std::for_each( lst.begin(), lst.end(), cv );
20851c0b2f7Stbbdev 
20951c0b2f7Stbbdev     std::vector<detail::atomic_type<bool>> marks( lst.size() );
21051c0b2f7Stbbdev 
21151c0b2f7Stbbdev     test_range<Table,iterator>( c, lst, marks ).do_test_range( c.begin(), c.end() );
21251c0b2f7Stbbdev     CHECK(std::find( marks.begin(), marks.end(), false ) == marks.end());
21351c0b2f7Stbbdev 
21451c0b2f7Stbbdev     test_range<const_table,const_iterator>( c, lst, marks ).do_test_range( c.begin(), c.end() );
21551c0b2f7Stbbdev     CHECK(std::find( marks.begin(), marks.end(), false ) == marks.end());
21651c0b2f7Stbbdev 
21751c0b2f7Stbbdev     using range_type = typename Table::range_type;
21851c0b2f7Stbbdev     tbb::parallel_for( c.range(), test_range<Table,typename range_type::iterator,range_type>( c, lst, marks ) );
21951c0b2f7Stbbdev     CHECK(std::find( marks.begin(), marks.end(), false ) == marks.end());
22051c0b2f7Stbbdev 
22151c0b2f7Stbbdev     const_table const_c = c;
22251c0b2f7Stbbdev     CHECK(CompareTables<value_type>::IsEqual( c, const_c ));
22351c0b2f7Stbbdev 
22451c0b2f7Stbbdev     const size_type new_bucket_count = 2*c.bucket_count();
22551c0b2f7Stbbdev     c.rehash( new_bucket_count );
22651c0b2f7Stbbdev     CHECK(c.bucket_count() >= new_bucket_count);
22751c0b2f7Stbbdev 
22851c0b2f7Stbbdev     Table c2;
22951c0b2f7Stbbdev     typename std::list<value_type>::const_iterator begin5 = lst.begin();
23051c0b2f7Stbbdev     std::advance( begin5, 5 );
23151c0b2f7Stbbdev     c2.insert( lst.begin(), begin5 );
23251c0b2f7Stbbdev     std::for_each( lst.begin(), begin5, check_value<default_construction_present, Table>( c2 ) );
23351c0b2f7Stbbdev 
23451c0b2f7Stbbdev     c2.swap( c );
23551c0b2f7Stbbdev     CHECK(CompareTables<value_type>::IsEqual( c2, const_c ));
23651c0b2f7Stbbdev     CHECK(c.size() == 5);
23751c0b2f7Stbbdev     std::for_each( lst.begin(), lst.end(), check_value<default_construction_present,Table>(c2) );
23851c0b2f7Stbbdev 
23951c0b2f7Stbbdev     swap( c, c2 );
24051c0b2f7Stbbdev     CHECK(CompareTables<value_type>::IsEqual( c, const_c ));
24151c0b2f7Stbbdev     CHECK(c2.size() == 5);
24251c0b2f7Stbbdev 
24351c0b2f7Stbbdev     c2.clear();
24451c0b2f7Stbbdev     CHECK(CompareTables<value_type>::IsEqual( c2, Table() ));
24551c0b2f7Stbbdev 
24651c0b2f7Stbbdev     typename Table::allocator_type a = c.get_allocator();
24751c0b2f7Stbbdev     value_type *ptr = a.allocate(1);
24851c0b2f7Stbbdev     CHECK(ptr);
24951c0b2f7Stbbdev     a.deallocate( ptr, 1 );
25051c0b2f7Stbbdev }
25151c0b2f7Stbbdev 
25251c0b2f7Stbbdev template <typename T>
25351c0b2f7Stbbdev struct debug_hash_compare : public tbb::detail::d1::tbb_hash_compare<T> {};
25451c0b2f7Stbbdev 
25551c0b2f7Stbbdev template <bool default_construction_present, typename Value>
TypeTester(const std::list<Value> & lst)25651c0b2f7Stbbdev void TypeTester( const std::list<Value> &lst ) {
25751c0b2f7Stbbdev     using first_type = typename Value::first_type;
25851c0b2f7Stbbdev     using key_type = typename std::remove_const<first_type>::type;
25951c0b2f7Stbbdev     using second_type = typename Value::second_type;
26051c0b2f7Stbbdev     using ch_map = tbb::concurrent_hash_map<key_type, second_type>;
26151c0b2f7Stbbdev     debug_hash_compare<key_type> compare{};
26251c0b2f7Stbbdev     // Construct an empty hash map.
26351c0b2f7Stbbdev     ch_map c1;
26451c0b2f7Stbbdev     c1.insert( lst.begin(), lst.end() );
26551c0b2f7Stbbdev     Examine<default_construction_present>( c1, lst );
26651c0b2f7Stbbdev 
26751c0b2f7Stbbdev     // Constructor from initializer_list.
26851c0b2f7Stbbdev     typename std::list<Value>::const_iterator it = lst.begin();
26951c0b2f7Stbbdev     std::initializer_list<Value> il = { *it++, *it++, *it++ };
27051c0b2f7Stbbdev     ch_map c2( il );
27151c0b2f7Stbbdev     c2.insert( it, lst.end() );
27251c0b2f7Stbbdev     Examine<default_construction_present>( c2, lst );
27351c0b2f7Stbbdev 
27451c0b2f7Stbbdev     // Constructor from initializer_list and compare object
27551c0b2f7Stbbdev     ch_map c3( il, compare);
27651c0b2f7Stbbdev     c3.insert( it, lst.end() );
27751c0b2f7Stbbdev     Examine<default_construction_present>( c3, lst );
27851c0b2f7Stbbdev 
27951c0b2f7Stbbdev     // Constructor from initializer_list, compare object and allocator
28051c0b2f7Stbbdev     ch_map c4( il, compare, typename ch_map::allocator_type());
28151c0b2f7Stbbdev     c4.insert( it, lst.end());
28251c0b2f7Stbbdev     Examine<default_construction_present>( c4, lst );
28351c0b2f7Stbbdev 
28451c0b2f7Stbbdev     // Copying constructor.
28551c0b2f7Stbbdev     ch_map c5(c1);
28651c0b2f7Stbbdev     Examine<default_construction_present>( c5, lst );
28751c0b2f7Stbbdev     // Construct with non-default allocator
28851c0b2f7Stbbdev     using ch_map_debug_alloc = tbb::concurrent_hash_map<key_type, second_type,
28951c0b2f7Stbbdev                                                         tbb::detail::d1::tbb_hash_compare<key_type>,
29051c0b2f7Stbbdev                                                         LocalCountingAllocator<std::allocator<Value>>>;
29151c0b2f7Stbbdev     ch_map_debug_alloc c6;
29251c0b2f7Stbbdev     c6.insert( lst.begin(), lst.end() );
29351c0b2f7Stbbdev     Examine<default_construction_present>( c6, lst );
29451c0b2f7Stbbdev     // Copying constructor
29551c0b2f7Stbbdev     ch_map_debug_alloc c7(c6);
29651c0b2f7Stbbdev     Examine<default_construction_present>( c7, lst );
29751c0b2f7Stbbdev     // Construction empty table with n preallocated buckets.
29851c0b2f7Stbbdev     ch_map c8( lst.size() );
29951c0b2f7Stbbdev     c8.insert( lst.begin(), lst.end() );
30051c0b2f7Stbbdev     Examine<default_construction_present>( c8, lst );
30151c0b2f7Stbbdev     ch_map_debug_alloc c9( lst.size() );
30251c0b2f7Stbbdev     c9.insert( lst.begin(), lst.end() );
30351c0b2f7Stbbdev     Examine<default_construction_present>( c9, lst );
30451c0b2f7Stbbdev     // Construction with copying iteration range.
30551c0b2f7Stbbdev     ch_map c10_1( c1.begin(), c1.end() ), c10_2(c1.cbegin(), c1.cend());
30651c0b2f7Stbbdev     Examine<default_construction_present>( c10_1, lst );
30751c0b2f7Stbbdev     Examine<default_construction_present>( c10_2, lst );
30851c0b2f7Stbbdev     // Construction with copying iteration range and given allocator instance.
30951c0b2f7Stbbdev     LocalCountingAllocator<std::allocator<Value>> allocator;
31051c0b2f7Stbbdev     ch_map_debug_alloc c11( lst.begin(), lst.end(), allocator );
31151c0b2f7Stbbdev     Examine<default_construction_present>( c11, lst );
31251c0b2f7Stbbdev 
31351c0b2f7Stbbdev     using ch_map_debug_hash = tbb::concurrent_hash_map<key_type, second_type,
31451c0b2f7Stbbdev                                                        debug_hash_compare<key_type>,
31551c0b2f7Stbbdev                                                        typename ch_map::allocator_type>;
31651c0b2f7Stbbdev 
31751c0b2f7Stbbdev     // Constructor with two iterators and hash_compare
31851c0b2f7Stbbdev     ch_map_debug_hash c12(c1.begin(), c1.end(), compare);
31951c0b2f7Stbbdev     Examine<default_construction_present>( c12, lst );
32051c0b2f7Stbbdev 
32151c0b2f7Stbbdev     ch_map_debug_hash c13(c1.begin(), c1.end(), compare, typename ch_map::allocator_type());
32251c0b2f7Stbbdev     Examine<default_construction_present>( c13, lst );
32351c0b2f7Stbbdev }
32451c0b2f7Stbbdev 
TestSpecificTypes()32551c0b2f7Stbbdev void TestSpecificTypes() {
32651c0b2f7Stbbdev     const int NUMBER = 10;
32751c0b2f7Stbbdev 
32851c0b2f7Stbbdev     using int_int_t = std::pair<const int, int>;
32951c0b2f7Stbbdev     std::list<int_int_t> arrIntInt;
33051c0b2f7Stbbdev     for ( int i=0; i<NUMBER; ++i ) arrIntInt.push_back( int_int_t(i, NUMBER-i) );
33151c0b2f7Stbbdev     TypeTester</*default_construction_present = */true>( arrIntInt );
33251c0b2f7Stbbdev 
33351c0b2f7Stbbdev     using ref_int_t = std::pair<const std::reference_wrapper<const int>, int>;
33451c0b2f7Stbbdev     std::list<ref_int_t> arrRefInt;
33551c0b2f7Stbbdev     for ( std::list<int_int_t>::iterator it = arrIntInt.begin(); it != arrIntInt.end(); ++it )
33651c0b2f7Stbbdev         arrRefInt.push_back( ref_int_t( it->first, it->second ) );
33751c0b2f7Stbbdev     TypeTester</*default_construction_present = */true>( arrRefInt );
33851c0b2f7Stbbdev 
33951c0b2f7Stbbdev     using int_ref_t = std::pair< const int, std::reference_wrapper<int> >;
34051c0b2f7Stbbdev     std::list<int_ref_t> arrIntRef;
34151c0b2f7Stbbdev     for ( std::list<int_int_t>::iterator it = arrIntInt.begin(); it != arrIntInt.end(); ++it )
34251c0b2f7Stbbdev         arrIntRef.push_back( int_ref_t( it->first, it->second ) );
34351c0b2f7Stbbdev     TypeTester</*default_construction_present = */false>( arrIntRef );
34451c0b2f7Stbbdev 
34551c0b2f7Stbbdev     using shr_shr_t = std::pair< const std::shared_ptr<int>, std::shared_ptr<int> >;
34651c0b2f7Stbbdev     std::list<shr_shr_t> arrShrShr;
34751c0b2f7Stbbdev     for ( int i=0; i<NUMBER; ++i ) {
34851c0b2f7Stbbdev         const int NUMBER_minus_i = NUMBER - i;
34951c0b2f7Stbbdev         arrShrShr.push_back( shr_shr_t( std::make_shared<int>(i), std::make_shared<int>(NUMBER_minus_i) ) );
35051c0b2f7Stbbdev     }
35151c0b2f7Stbbdev     TypeTester< /*default_construction_present = */true>( arrShrShr );
35251c0b2f7Stbbdev 
35351c0b2f7Stbbdev     using wk_wk_t = std::pair< const std::weak_ptr<int>, std::weak_ptr<int> >;
35451c0b2f7Stbbdev     std::list< wk_wk_t > arrWkWk;
35551c0b2f7Stbbdev     std::copy( arrShrShr.begin(), arrShrShr.end(), std::back_inserter(arrWkWk) );
35651c0b2f7Stbbdev     TypeTester< /*default_construction_present = */true>( arrWkWk );
35751c0b2f7Stbbdev 
35851c0b2f7Stbbdev     // Check working with deprecated hashers
35951c0b2f7Stbbdev     using pair_key_type = std::pair<int, int>;
36051c0b2f7Stbbdev     using pair_int_t = std::pair<const pair_key_type, int>;
36151c0b2f7Stbbdev     std::list<pair_int_t> arr_pair_int;
36251c0b2f7Stbbdev     for (int i = 0; i < NUMBER; ++i) {
36351c0b2f7Stbbdev         arr_pair_int.push_back(pair_int_t(pair_key_type{i, i}, i));
36451c0b2f7Stbbdev     }
36551c0b2f7Stbbdev     TypeTester</*default_construction_present = */true>(arr_pair_int);
36651c0b2f7Stbbdev 
36751c0b2f7Stbbdev     using tbb_string_key_type = std::basic_string<char, std::char_traits<char>, tbb::tbb_allocator<char>>;
36851c0b2f7Stbbdev     using pair_tbb_string_int_t = std::pair<const tbb_string_key_type, int>;
36951c0b2f7Stbbdev     std::list<pair_tbb_string_int_t> arr_pair_string_int;
37051c0b2f7Stbbdev     for (int i = 0; i < NUMBER; ++i) {
37151c0b2f7Stbbdev         tbb_string_key_type key(i, char(i));
37251c0b2f7Stbbdev         arr_pair_string_int.push_back(pair_tbb_string_int_t(key, i));
37351c0b2f7Stbbdev     }
37451c0b2f7Stbbdev     TypeTester</*default_construction_present = */true>(arr_pair_string_int);
37551c0b2f7Stbbdev }
37651c0b2f7Stbbdev 
37751c0b2f7Stbbdev struct custom_hash_compare {
37851c0b2f7Stbbdev     template<typename Allocator>
hashcustom_hash_compare37951c0b2f7Stbbdev     size_t hash(const AllocatorAwareData<Allocator>& key) const {
38051c0b2f7Stbbdev         return my_hash_compare.hash(key.value());
38151c0b2f7Stbbdev     }
38251c0b2f7Stbbdev 
38351c0b2f7Stbbdev     template<typename Allocator>
equalcustom_hash_compare38451c0b2f7Stbbdev     bool equal(const AllocatorAwareData<Allocator>& key1, const AllocatorAwareData<Allocator>& key2) const {
38551c0b2f7Stbbdev         return my_hash_compare.equal(key1.value(), key2.value());
38651c0b2f7Stbbdev     }
38751c0b2f7Stbbdev 
38851c0b2f7Stbbdev private:
38951c0b2f7Stbbdev     tbb::tbb_hash_compare<int> my_hash_compare;
39051c0b2f7Stbbdev };
39151c0b2f7Stbbdev 
TestScopedAllocator()39251c0b2f7Stbbdev void TestScopedAllocator() {
39351c0b2f7Stbbdev     using allocator_data_type = AllocatorAwareData<std::scoped_allocator_adaptor<tbb::tbb_allocator<int>>>;
39451c0b2f7Stbbdev     using allocator_type = std::scoped_allocator_adaptor<tbb::tbb_allocator<std::pair<const allocator_data_type, allocator_data_type>>>;
39551c0b2f7Stbbdev     using hash_map_type = tbb::concurrent_hash_map<allocator_data_type, allocator_data_type,
39651c0b2f7Stbbdev                                                    custom_hash_compare, allocator_type>;
39751c0b2f7Stbbdev 
39851c0b2f7Stbbdev     allocator_type allocator;
39951c0b2f7Stbbdev     allocator_data_type key1(1, allocator), key2(2, allocator);
40051c0b2f7Stbbdev     allocator_data_type data1(1, allocator), data2(data1, allocator);
40151c0b2f7Stbbdev     hash_map_type map1(allocator), map2(allocator);
40251c0b2f7Stbbdev 
40351c0b2f7Stbbdev     hash_map_type::value_type v1(key1, data1), v2(key2, data2);
40451c0b2f7Stbbdev 
40551c0b2f7Stbbdev     auto init_list = { v1, v2 };
40651c0b2f7Stbbdev 
40751c0b2f7Stbbdev     allocator_data_type::assert_on_constructions = true;
40851c0b2f7Stbbdev     map1.emplace(key1, data1);
40951c0b2f7Stbbdev     map2.emplace(key2, std::move(data2));
41051c0b2f7Stbbdev 
41151c0b2f7Stbbdev     map1.clear();
41251c0b2f7Stbbdev     map2.clear();
41351c0b2f7Stbbdev 
41451c0b2f7Stbbdev     map1.insert(v1);
41551c0b2f7Stbbdev     map2.insert(std::move(v2));
41651c0b2f7Stbbdev 
41751c0b2f7Stbbdev     map1.clear();
41851c0b2f7Stbbdev     map2.clear();
41951c0b2f7Stbbdev 
42051c0b2f7Stbbdev     map1.insert(init_list);
42151c0b2f7Stbbdev 
42251c0b2f7Stbbdev     map1.clear();
42351c0b2f7Stbbdev     map2.clear();
42451c0b2f7Stbbdev 
42551c0b2f7Stbbdev     hash_map_type::accessor a;
42651c0b2f7Stbbdev     map2.insert(a, allocator_data_type(3));
42751c0b2f7Stbbdev     a.release();
42851c0b2f7Stbbdev 
42951c0b2f7Stbbdev     map1 = map2;
43051c0b2f7Stbbdev     map2 = std::move(map1);
43151c0b2f7Stbbdev 
43251c0b2f7Stbbdev     hash_map_type map3(allocator);
43351c0b2f7Stbbdev     map3.rehash(1000);
43451c0b2f7Stbbdev     map3 = map2;
43551c0b2f7Stbbdev }
43651c0b2f7Stbbdev 
43751c0b2f7Stbbdev // A test for undocumented member function internal_fast_find
43851c0b2f7Stbbdev // which is declared protected in concurrent_hash_map for internal TBB use
TestInternalFastFind()43951c0b2f7Stbbdev void TestInternalFastFind() {
44051c0b2f7Stbbdev     typedef tbb::concurrent_hash_map<int, int> basic_chmap_type;
44151c0b2f7Stbbdev     typedef basic_chmap_type::const_pointer const_pointer;
44251c0b2f7Stbbdev 
44351c0b2f7Stbbdev     class chmap : public basic_chmap_type {
44451c0b2f7Stbbdev     public:
44551c0b2f7Stbbdev         chmap() : basic_chmap_type() {}
44651c0b2f7Stbbdev 
44751c0b2f7Stbbdev         using basic_chmap_type::internal_fast_find;
44851c0b2f7Stbbdev     };
44951c0b2f7Stbbdev 
45051c0b2f7Stbbdev     chmap m;
45151c0b2f7Stbbdev     int sz = 100;
45251c0b2f7Stbbdev 
45351c0b2f7Stbbdev     for (int i = 0; i != sz; ++i) {
45451c0b2f7Stbbdev         m.insert(std::make_pair(i, i * i));
45551c0b2f7Stbbdev     }
45651c0b2f7Stbbdev     REQUIRE_MESSAGE(m.size() == 100, "Incorrect concurrent_hash_map size");
45751c0b2f7Stbbdev 
45851c0b2f7Stbbdev     for (int i = 0; i != sz; ++i) {
45951c0b2f7Stbbdev         const_pointer res = m.internal_fast_find(i);
46051c0b2f7Stbbdev         REQUIRE_MESSAGE(res != nullptr, "Incorrect internal_fast_find return value for existing key");
46151c0b2f7Stbbdev         basic_chmap_type::value_type val = *res;
46251c0b2f7Stbbdev         REQUIRE_MESSAGE(val.first == i, "Incorrect key in internal_fast_find return value");
46351c0b2f7Stbbdev         REQUIRE_MESSAGE(val.second == i * i, "Incorrect mapped in internal_fast_find return value");
46451c0b2f7Stbbdev     }
46551c0b2f7Stbbdev 
46651c0b2f7Stbbdev     for (int i = sz; i != 2 * sz; ++i) {
46751c0b2f7Stbbdev         const_pointer res = m.internal_fast_find(i);
46851c0b2f7Stbbdev         REQUIRE_MESSAGE(res == nullptr, "Incorrect internal_fast_find return value for not existing key");
46951c0b2f7Stbbdev     }
47051c0b2f7Stbbdev }
47151c0b2f7Stbbdev 
47251c0b2f7Stbbdev struct default_container_traits {
47351c0b2f7Stbbdev     template <typename container_type, typename iterator_type>
construct_containerdefault_container_traits47451c0b2f7Stbbdev     static container_type& construct_container(typename std::aligned_storage<sizeof(container_type)>::type& storage, iterator_type begin, iterator_type end){
47551c0b2f7Stbbdev         container_type* ptr = reinterpret_cast<container_type*>(&storage);
47651c0b2f7Stbbdev         new (ptr) container_type(begin, end);
47751c0b2f7Stbbdev         return *ptr;
47851c0b2f7Stbbdev     }
47951c0b2f7Stbbdev 
48051c0b2f7Stbbdev     template <typename container_type, typename iterator_type, typename allocator_type>
construct_containerdefault_container_traits48151c0b2f7Stbbdev     static container_type& construct_container(typename std::aligned_storage<sizeof(container_type)>::type& storage, iterator_type begin, iterator_type end, allocator_type const& a){
48251c0b2f7Stbbdev         container_type* ptr = reinterpret_cast<container_type*>(&storage);
48351c0b2f7Stbbdev         new (ptr) container_type(begin, end, a);
48451c0b2f7Stbbdev         return *ptr;
48551c0b2f7Stbbdev     }
48651c0b2f7Stbbdev };
48751c0b2f7Stbbdev 
48851c0b2f7Stbbdev struct hash_map_traits : default_container_traits {
48951c0b2f7Stbbdev     enum{ expected_number_of_items_to_allocate_for_steal_move = 0 };
49051c0b2f7Stbbdev 
49151c0b2f7Stbbdev     template<typename T>
49251c0b2f7Stbbdev     struct hash_compare {
equalhash_map_traits::hash_compare49351c0b2f7Stbbdev         bool equal( const T& lhs, const T& rhs ) const {
49451c0b2f7Stbbdev             return lhs==rhs;
49551c0b2f7Stbbdev         }
hashhash_map_traits::hash_compare49651c0b2f7Stbbdev         size_t hash( const T& k ) const {
49751c0b2f7Stbbdev             return my_hash_func(k);
49851c0b2f7Stbbdev         }
49951c0b2f7Stbbdev         std::hash<T> my_hash_func;
50051c0b2f7Stbbdev     };
50151c0b2f7Stbbdev 
50251c0b2f7Stbbdev     template <typename T, typename Allocator>
50351c0b2f7Stbbdev     using container_type = tbb::concurrent_hash_map<T, T, hash_compare<T>, Allocator>;
50451c0b2f7Stbbdev 
50551c0b2f7Stbbdev     template <typename T>
50651c0b2f7Stbbdev     using container_value_type = std::pair<const T, T>;
50751c0b2f7Stbbdev 
50851c0b2f7Stbbdev     template<typename element_type, typename allocator_type>
50951c0b2f7Stbbdev     struct apply {
51051c0b2f7Stbbdev         using type = tbb::concurrent_hash_map<element_type, element_type, hash_compare<element_type>, allocator_type>;
51151c0b2f7Stbbdev     };
51251c0b2f7Stbbdev 
51351c0b2f7Stbbdev     using init_iterator_type = move_support_tests::FooPairIterator;
51451c0b2f7Stbbdev     template <typename hash_map_type, typename iterator>
equalhash_map_traits51551c0b2f7Stbbdev     static bool equal(hash_map_type const& c, iterator begin, iterator end){
51651c0b2f7Stbbdev         bool equal_sizes = ( static_cast<size_t>(std::distance(begin, end)) == c.size() );
51751c0b2f7Stbbdev         if (!equal_sizes)
51851c0b2f7Stbbdev             return false;
51951c0b2f7Stbbdev 
52051c0b2f7Stbbdev         for (iterator it = begin; it != end; ++it ){
52151c0b2f7Stbbdev             if (c.count( (*it).first) == 0){
52251c0b2f7Stbbdev                 return false;
52351c0b2f7Stbbdev             }
52451c0b2f7Stbbdev         }
52551c0b2f7Stbbdev         return true;
52651c0b2f7Stbbdev     }
52751c0b2f7Stbbdev };
52851c0b2f7Stbbdev 
5299e15720bStbbdev template <bool SimulateReacquiring>
5309e15720bStbbdev class MinimalisticMutex {
5319e15720bStbbdev public:
532478de5b1Stbbdev     static constexpr bool is_rw_mutex = true;
533478de5b1Stbbdev     static constexpr bool is_recursive_mutex = false;
534478de5b1Stbbdev     static constexpr bool is_fair_mutex = false;
535478de5b1Stbbdev 
5369e15720bStbbdev     class scoped_lock {
5379e15720bStbbdev     public:
scoped_lock()5389e15720bStbbdev         constexpr scoped_lock() noexcept : my_mutex_ptr(nullptr) {}
5399e15720bStbbdev 
scoped_lock(MinimalisticMutex & m,bool=true)5409e15720bStbbdev         scoped_lock( MinimalisticMutex& m, bool = true ) : my_mutex_ptr(&m) {
5419e15720bStbbdev             my_mutex_ptr->my_mutex.lock();
5429e15720bStbbdev         }
5439e15720bStbbdev 
5449e15720bStbbdev         scoped_lock( const scoped_lock& ) = delete;
5459e15720bStbbdev         scoped_lock& operator=( const scoped_lock& ) = delete;
5469e15720bStbbdev 
~scoped_lock()5479e15720bStbbdev         ~scoped_lock() {
5489e15720bStbbdev             if (my_mutex_ptr) release();
5499e15720bStbbdev         }
5509e15720bStbbdev 
acquire(MinimalisticMutex & m,bool=true)5519e15720bStbbdev         void acquire( MinimalisticMutex& m, bool = true ) {
5529e15720bStbbdev             CHECK(my_mutex_ptr == nullptr);
5539e15720bStbbdev             my_mutex_ptr = &m;
5549e15720bStbbdev             my_mutex_ptr->my_mutex.lock();
5559e15720bStbbdev         }
5569e15720bStbbdev 
try_acquire(MinimalisticMutex & m,bool=true)5579e15720bStbbdev         bool try_acquire( MinimalisticMutex& m, bool = true ) {
5589e15720bStbbdev             if (m.my_mutex.try_lock()) {
5599e15720bStbbdev                 my_mutex_ptr = &m;
5609e15720bStbbdev                 return true;
5619e15720bStbbdev             }
5629e15720bStbbdev             return false;
5639e15720bStbbdev         }
5649e15720bStbbdev 
release()5659e15720bStbbdev         void release() {
5669e15720bStbbdev             CHECK(my_mutex_ptr != nullptr);
5679e15720bStbbdev             my_mutex_ptr->my_mutex.unlock();
5689e15720bStbbdev             my_mutex_ptr = nullptr;
5699e15720bStbbdev         }
5709e15720bStbbdev 
upgrade_to_writer() const5719e15720bStbbdev         bool upgrade_to_writer() const {
5729e15720bStbbdev             // upgrade_to_writer should return false if the mutex simulates
573*c4a799dfSJhaShweta1             // reacquiring the lock on upgrade operation
5749e15720bStbbdev             return !SimulateReacquiring;
5759e15720bStbbdev         }
5769e15720bStbbdev 
downgrade_to_reader() const5779e15720bStbbdev         bool downgrade_to_reader() const {
5789e15720bStbbdev             // downgrade_to_reader should return false if the mutex simulates
579*c4a799dfSJhaShweta1             // reacquiring the lock on upgrade operation
5809e15720bStbbdev             return !SimulateReacquiring;
5819e15720bStbbdev         }
5829e15720bStbbdev 
is_writer() const5839e15720bStbbdev         bool is_writer() const {
5849e15720bStbbdev             CHECK(my_mutex_ptr != nullptr);
5859e15720bStbbdev             return true; // Always a writer
5869e15720bStbbdev         }
5879e15720bStbbdev 
5889e15720bStbbdev     private:
5899e15720bStbbdev         MinimalisticMutex* my_mutex_ptr;
5909e15720bStbbdev     }; // class scoped_lock
5919e15720bStbbdev private:
5929e15720bStbbdev     std::mutex my_mutex;
5939e15720bStbbdev }; // class MinimalisticMutex
5949e15720bStbbdev 
5959e15720bStbbdev template <bool SimulateReacquiring>
test_with_minimalistic_mutex()5969e15720bStbbdev void test_with_minimalistic_mutex() {
5979e15720bStbbdev     using mutex_type = MinimalisticMutex<SimulateReacquiring>;
5989e15720bStbbdev     using chmap_type = tbb::concurrent_hash_map<int, int, tbb::tbb_hash_compare<int>,
5999e15720bStbbdev                                                 tbb::tbb_allocator<std::pair<const int, int>>,
6009e15720bStbbdev                                                 mutex_type>;
6019e15720bStbbdev 
6029e15720bStbbdev     chmap_type chmap;
6039e15720bStbbdev 
6049e15720bStbbdev     // Insert pre-existing elements
6059e15720bStbbdev     for (int i = 0; i < 100; ++i) {
6069e15720bStbbdev         bool result = chmap.emplace(i, i);
6079e15720bStbbdev         CHECK(result);
6089e15720bStbbdev     }
6099e15720bStbbdev 
6109e15720bStbbdev     // Insert elements to erase
6119e15720bStbbdev     for (int i = 10000; i < 10005; ++i) {
6129e15720bStbbdev         bool result = chmap.emplace(i, i);
6139e15720bStbbdev         CHECK(result);
6149e15720bStbbdev     }
6159e15720bStbbdev 
6169e15720bStbbdev     auto thread_body = [&]( const tbb::blocked_range<std::size_t>& range ) {
6179e15720bStbbdev         for (std::size_t item = range.begin(); item != range.end(); ++item) {
6189e15720bStbbdev             switch(item % 4) {
6199e15720bStbbdev                 case 0 :
6209e15720bStbbdev                     // Insert new elements
6219e15720bStbbdev                     for (int i = 100; i < 200; ++i) {
6229e15720bStbbdev                         typename chmap_type::const_accessor acc;
6239e15720bStbbdev                         chmap.emplace(acc, i, i);
6249e15720bStbbdev                         CHECK(acc->first == i);
6259e15720bStbbdev                         CHECK(acc->second == i);
6269e15720bStbbdev                     }
6279e15720bStbbdev                     break;
6289e15720bStbbdev                 case 1 :
6299e15720bStbbdev                     // Insert pre-existing elements
6309e15720bStbbdev                     for (int i = 0; i < 100; ++i) {
6319e15720bStbbdev                         typename chmap_type::const_accessor acc;
6329e15720bStbbdev                         bool result = chmap.emplace(acc, i, i * 10000);
6339e15720bStbbdev                         CHECK(!result);
6349e15720bStbbdev                         CHECK(acc->first == i);
6359e15720bStbbdev                         CHECK(acc->second == i);
6369e15720bStbbdev                     }
6379e15720bStbbdev                     break;
6389e15720bStbbdev                 case 2 :
6399e15720bStbbdev                     // Find pre-existing elements
6409e15720bStbbdev                     for (int i = 0; i < 100; ++i) {
6419e15720bStbbdev                         typename chmap_type::const_accessor acc;
6429e15720bStbbdev                         bool result = chmap.find(acc, i);
6439e15720bStbbdev                         CHECK(result);
6449e15720bStbbdev                         CHECK(acc->first == i);
6459e15720bStbbdev                         CHECK(acc->second == i);
6469e15720bStbbdev                     }
6479e15720bStbbdev                     break;
6489e15720bStbbdev                 case 3 :
6499e15720bStbbdev                     // Erase pre-existing elements
6509e15720bStbbdev                     for (int i = 10000; i < 10005; ++i) {
6519e15720bStbbdev                         chmap.erase(i);
6529e15720bStbbdev                     }
6539e15720bStbbdev                 break;
6549e15720bStbbdev             }
6559e15720bStbbdev         }
6569e15720bStbbdev     }; // thread_body
6579e15720bStbbdev 
6589e15720bStbbdev     tbb::blocked_range<std::size_t> br(0, 1000, 8);
6599e15720bStbbdev 
6609e15720bStbbdev     tbb::parallel_for(br, thread_body);
6619e15720bStbbdev 
6629e15720bStbbdev     // Check pre-existing and new elements
6639e15720bStbbdev     for (int i = 0; i < 200; ++i) {
6649e15720bStbbdev         typename chmap_type::const_accessor acc;
6659e15720bStbbdev         bool result = chmap.find(acc, i);
6669e15720bStbbdev         REQUIRE_MESSAGE(result, "Some element was unexpectedly removed or not inserted");
6679e15720bStbbdev         REQUIRE_MESSAGE(acc->first == i, "Incorrect key");
6689e15720bStbbdev         REQUIRE_MESSAGE(acc->second == i, "Incorrect value");
6699e15720bStbbdev     }
6709e15720bStbbdev 
6719e15720bStbbdev     // Check elements for erasure
6729e15720bStbbdev     for (int i = 10000; i < 10005; ++i) {
6739e15720bStbbdev         typename chmap_type::const_accessor acc;
6749e15720bStbbdev         bool result = chmap.find(acc, i);
6759e15720bStbbdev         REQUIRE_MESSAGE(!result, "Some element was not removed");
6769e15720bStbbdev     }
6779e15720bStbbdev }
6789e15720bStbbdev 
test_mutex_customization()6799e15720bStbbdev void test_mutex_customization() {
6809e15720bStbbdev     test_with_minimalistic_mutex</*SimulateReacquiring = */false>();
6819e15720bStbbdev     test_with_minimalistic_mutex</*SimulateReacquiring = */true>();
6829e15720bStbbdev }
6839e15720bStbbdev 
68456cb263eSkboyarinov struct SimpleTransparentHashCompare {
68556cb263eSkboyarinov     using is_transparent = void;
68656cb263eSkboyarinov 
68756cb263eSkboyarinov     template <typename T>
hashSimpleTransparentHashCompare68856cb263eSkboyarinov     std::size_t hash(const T&) const { return 0; }
68956cb263eSkboyarinov 
69056cb263eSkboyarinov     template <typename T, typename U>
equalSimpleTransparentHashCompare69156cb263eSkboyarinov     bool equal(const T& key1, const U& key2) const { return key1 == key2; }
69256cb263eSkboyarinov };
69356cb263eSkboyarinov 
69456cb263eSkboyarinov template <typename Accessor>
69556cb263eSkboyarinov struct IsWriterAccessor : public Accessor {
69656cb263eSkboyarinov     using Accessor::is_writer;
69756cb263eSkboyarinov };
69856cb263eSkboyarinov 
69956cb263eSkboyarinov template <typename Map, typename Accessor>
test_chmap_access_mode(bool expect_write)70056cb263eSkboyarinov void test_chmap_access_mode(bool expect_write) {
70156cb263eSkboyarinov     static_assert(std::is_same<int, typename Map::key_type>::value, "Incorrect test setup");
70256cb263eSkboyarinov     Map map;
70356cb263eSkboyarinov     Accessor acc;
70456cb263eSkboyarinov 
70556cb263eSkboyarinov     // Test homogeneous insert
70656cb263eSkboyarinov     bool result = map.insert(acc, 1);
70756cb263eSkboyarinov     CHECK(result);
70856cb263eSkboyarinov     CHECK_MESSAGE(acc.is_writer() == expect_write, "Incorrect access into the map from homogeneous insert");
70956cb263eSkboyarinov 
71056cb263eSkboyarinov     // Test heterogeneous insert
71156cb263eSkboyarinov     result = map.insert(acc, 2L);
71256cb263eSkboyarinov     CHECK(result);
71356cb263eSkboyarinov     CHECK_MESSAGE(acc.is_writer() == expect_write, "Incorrect access into the map from heterogeneous insert");
71456cb263eSkboyarinov 
71556cb263eSkboyarinov     // Test lvalue insert
71656cb263eSkboyarinov     typename Map::value_type value{3, 3};
71756cb263eSkboyarinov     result = map.insert(acc, value);
71856cb263eSkboyarinov     CHECK(result);
71956cb263eSkboyarinov     CHECK_MESSAGE(acc.is_writer() == expect_write, "Incorrect access into the map from lvalue insert");
72056cb263eSkboyarinov 
72156cb263eSkboyarinov     // Test rvalue insert
72256cb263eSkboyarinov     result = map.insert(acc, typename Map::value_type{4, 4});
72356cb263eSkboyarinov     CHECK(result);
72456cb263eSkboyarinov     CHECK_MESSAGE(acc.is_writer() == expect_write, "Incorrect access into the map from rvalue insert");
72556cb263eSkboyarinov 
72656cb263eSkboyarinov     // Test homogeneous find
72756cb263eSkboyarinov     result = map.find(acc, 1);
72856cb263eSkboyarinov     CHECK(result);
72956cb263eSkboyarinov     CHECK_MESSAGE(acc.is_writer() == expect_write, "Incorrect access into the map from homogeneous find");
73056cb263eSkboyarinov 
73156cb263eSkboyarinov     // Test heterogeneous find
73256cb263eSkboyarinov     result = map.find(acc, 2L);
73356cb263eSkboyarinov     CHECK(result);
73456cb263eSkboyarinov     CHECK_MESSAGE(acc.is_writer() == expect_write, "Incorrect access into the map from heterogeneous find");
73556cb263eSkboyarinov }
73656cb263eSkboyarinov 
73751c0b2f7Stbbdev //! Test of insert operation
73851c0b2f7Stbbdev //! \brief \ref error_guessing
73951c0b2f7Stbbdev TEST_CASE("testing range based for support"){
74051c0b2f7Stbbdev     TestRangeBasedFor();
74151c0b2f7Stbbdev }
74251c0b2f7Stbbdev 
74351c0b2f7Stbbdev //! Test concurrent_hash_map with specific key/mapped types
74451c0b2f7Stbbdev //! \brief \ref regression \ref error_guessing
74551c0b2f7Stbbdev TEST_CASE("testing concurrent_hash_map with specific key/mapped types") {
74651c0b2f7Stbbdev     TestSpecificTypes();
74751c0b2f7Stbbdev }
74851c0b2f7Stbbdev 
74951c0b2f7Stbbdev //! Test work with scoped allocator
75051c0b2f7Stbbdev //! \brief \ref regression
75151c0b2f7Stbbdev TEST_CASE("testing work with scoped allocator") {
75251c0b2f7Stbbdev     TestScopedAllocator();
75351c0b2f7Stbbdev }
75451c0b2f7Stbbdev 
75551c0b2f7Stbbdev //! Test internal fast find for concurrent_hash_map
75651c0b2f7Stbbdev //! \brief \ref regression
75751c0b2f7Stbbdev TEST_CASE("testing internal fast find for concurrent_hash_map") {
75851c0b2f7Stbbdev     TestInternalFastFind();
75951c0b2f7Stbbdev }
76051c0b2f7Stbbdev 
76151c0b2f7Stbbdev //! Test constructor with move iterators
76251c0b2f7Stbbdev //! \brief \ref error_guessing
76351c0b2f7Stbbdev TEST_CASE("testing constructor with move iterators"){
76451c0b2f7Stbbdev     move_support_tests::test_constructor_with_move_iterators<hash_map_traits>();
76551c0b2f7Stbbdev }
76651c0b2f7Stbbdev 
76751c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
76851c0b2f7Stbbdev //! Test exception in constructors
76951c0b2f7Stbbdev //! \brief \ref regression \ref error_guessing
77051c0b2f7Stbbdev TEST_CASE("Test exception in constructors") {
77151c0b2f7Stbbdev     using allocator_type = StaticSharedCountingAllocator<std::allocator<std::pair<const int, int>>>;
77251c0b2f7Stbbdev     using map_type = tbb::concurrent_hash_map<int, int, tbb::tbb_hash_compare<int>, allocator_type>;
77351c0b2f7Stbbdev 
77451c0b2f7Stbbdev     auto init_list = {std::pair<const int, int>(1, 42), std::pair<const int, int>(2, 42), std::pair<const int, int>(3, 42),
77551c0b2f7Stbbdev         std::pair<const int, int>(4, 42), std::pair<const int, int>(5, 42), std::pair<const int, int>(6, 42)};
77651c0b2f7Stbbdev     map_type map(init_list);
77751c0b2f7Stbbdev 
77851c0b2f7Stbbdev     allocator_type::set_limits(1);
__anon3741356d0302null77951c0b2f7Stbbdev     REQUIRE_THROWS_AS( [&] {
78051c0b2f7Stbbdev         map_type map1(map);
78151c0b2f7Stbbdev         utils::suppress_unused_warning(map1);
78251c0b2f7Stbbdev     }(), const std::bad_alloc);
78351c0b2f7Stbbdev 
__anon3741356d0402null78451c0b2f7Stbbdev     REQUIRE_THROWS_AS( [&] {
78551c0b2f7Stbbdev         map_type map2(init_list.begin(), init_list.end());
78651c0b2f7Stbbdev         utils::suppress_unused_warning(map2);
78751c0b2f7Stbbdev     }(), const std::bad_alloc);
78851c0b2f7Stbbdev 
78951c0b2f7Stbbdev     tbb::tbb_hash_compare<int> test_hash;
79051c0b2f7Stbbdev 
__anon3741356d0502null79151c0b2f7Stbbdev     REQUIRE_THROWS_AS( [&] {
79251c0b2f7Stbbdev         map_type map3(init_list.begin(), init_list.end(), test_hash);
79351c0b2f7Stbbdev         utils::suppress_unused_warning(map3);
79451c0b2f7Stbbdev     }(), const std::bad_alloc);
79551c0b2f7Stbbdev 
__anon3741356d0602null79651c0b2f7Stbbdev     REQUIRE_THROWS_AS( [&] {
79751c0b2f7Stbbdev         map_type map4(init_list, test_hash);
79851c0b2f7Stbbdev         utils::suppress_unused_warning(map4);
79951c0b2f7Stbbdev     }(), const std::bad_alloc);
80051c0b2f7Stbbdev 
__anon3741356d0702null80151c0b2f7Stbbdev     REQUIRE_THROWS_AS( [&] {
80251c0b2f7Stbbdev         map_type map5(init_list);
80351c0b2f7Stbbdev         utils::suppress_unused_warning(map5);
80451c0b2f7Stbbdev     }(), const std::bad_alloc);
80551c0b2f7Stbbdev 
80651c0b2f7Stbbdev     allocator_type::set_limits(0);
80751c0b2f7Stbbdev     map_type big_map{};
80855f9b178SIvan Kochin     for (int i = 0; i < 1000; ++i) {
80951c0b2f7Stbbdev         big_map.insert(std::pair<const int, int>(i, 42));
81051c0b2f7Stbbdev     }
81151c0b2f7Stbbdev 
81251c0b2f7Stbbdev     allocator_type::init_counters();
81351c0b2f7Stbbdev     allocator_type::set_limits(300);
__anon3741356d0802null81451c0b2f7Stbbdev     REQUIRE_THROWS_AS( [&] {
81551c0b2f7Stbbdev         map_type map6(big_map);
81651c0b2f7Stbbdev         utils::suppress_unused_warning(map6);
81751c0b2f7Stbbdev     }(), const std::bad_alloc);
81851c0b2f7Stbbdev }
81951c0b2f7Stbbdev #endif // TBB_USE_EXCEPTIONS
82051c0b2f7Stbbdev 
82151c0b2f7Stbbdev //! \brief \ref error_guessing
82251c0b2f7Stbbdev TEST_CASE("swap with NotAlwaysEqualAllocator allocators") {
82351c0b2f7Stbbdev     using allocator_type = NotAlwaysEqualAllocator<std::pair<const int, int>>;
82451c0b2f7Stbbdev     using map_type = tbb::concurrent_hash_map<int, int, tbb::tbb_hash_compare<int>, allocator_type>;
82551c0b2f7Stbbdev 
82651c0b2f7Stbbdev     map_type map1{};
82751c0b2f7Stbbdev     map_type map2({{42, 42}, {24, 42}});
82851c0b2f7Stbbdev     map_type map3(map2);
82951c0b2f7Stbbdev 
83051c0b2f7Stbbdev     swap(map1, map2);
83151c0b2f7Stbbdev 
83251c0b2f7Stbbdev     CHECK(map2.empty());
83351c0b2f7Stbbdev     CHECK(map1 == map3);
83451c0b2f7Stbbdev }
8354523a761Stbbdev 
8364523a761Stbbdev //! \brief \ref error_guessing
8379e15720bStbbdev TEST_CASE("test concurrent_hash_map mutex customization") {
8389e15720bStbbdev     test_mutex_customization();
8399e15720bStbbdev }
840478de5b1Stbbdev 
84156cb263eSkboyarinov // A test for an issue when const_accessor passed to find provides write access into the map after the lookup
84256cb263eSkboyarinov //! \brief \ref regression
84356cb263eSkboyarinov TEST_CASE("test concurrent_hash_map accessors issue") {
84456cb263eSkboyarinov     using map_type = tbb::concurrent_hash_map<int, int, SimpleTransparentHashCompare>;
84556cb263eSkboyarinov     using accessor = IsWriterAccessor<typename map_type::accessor>;
84656cb263eSkboyarinov     using const_accessor = IsWriterAccessor<typename map_type::const_accessor>;
84756cb263eSkboyarinov 
84856cb263eSkboyarinov     test_chmap_access_mode<map_type, accessor>(/*expect_write = */true);
84956cb263eSkboyarinov     test_chmap_access_mode<map_type, const_accessor>(/*expect_write = */false);
85056cb263eSkboyarinov }
85156cb263eSkboyarinov 
852478de5b1Stbbdev #if __TBB_CPP20_CONCEPTS_PRESENT
853478de5b1Stbbdev template <bool ExpectSatisfies, typename Key, typename Mapped, typename... HCTypes>
854478de5b1Stbbdev     requires (... && (utils::well_formed_instantiation<tbb::concurrent_hash_map, Key, Mapped, HCTypes> == ExpectSatisfies))
test_chmap_hash_compare_constraints()855478de5b1Stbbdev void test_chmap_hash_compare_constraints() {}
856478de5b1Stbbdev 
857478de5b1Stbbdev //! \brief \ref error_guessing
858478de5b1Stbbdev TEST_CASE("tbb::concurrent_hash_map hash_compare constraints") {
859478de5b1Stbbdev     using key_type = int;
860478de5b1Stbbdev     using mapped_type = int;
861478de5b1Stbbdev     using namespace test_concepts::hash_compare;
862478de5b1Stbbdev 
863478de5b1Stbbdev     test_chmap_hash_compare_constraints</*Expected = */true, /*key = */key_type, /*mapped = */mapped_type,
864478de5b1Stbbdev                                         Correct<key_type>, tbb::tbb_hash_compare<key_type>>();
865478de5b1Stbbdev 
866478de5b1Stbbdev     test_chmap_hash_compare_constraints</*Expected = */false, /*key = */key_type, /*mapped = */mapped_type,
867478de5b1Stbbdev                                         NonCopyable<key_type>, NonDestructible<key_type>,
868478de5b1Stbbdev                                         NoHash<key_type>, HashNonConst<key_type>, WrongInputHash<key_type>, WrongReturnHash<key_type>,
869478de5b1Stbbdev                                         NoEqual<key_type>, EqualNonConst<key_type>,
870478de5b1Stbbdev                                         WrongFirstInputEqual<key_type>, WrongSecondInputEqual<key_type>, WrongReturnEqual<key_type>>();
871478de5b1Stbbdev }
872478de5b1Stbbdev 
873478de5b1Stbbdev template <bool ExpectSatisfies, typename Key, typename Mapped, typename... RWMutexes>
874478de5b1Stbbdev     requires (... && (utils::well_formed_instantiation<tbb::concurrent_hash_map, Key, Mapped,
875478de5b1Stbbdev                                                 tbb::tbb_hash_compare<Key>, tbb::tbb_allocator<std::pair<const Key, Mapped>>, RWMutexes> == ExpectSatisfies))
test_chmap_mutex_constraints()876478de5b1Stbbdev void test_chmap_mutex_constraints() {}
877478de5b1Stbbdev 
878478de5b1Stbbdev //! \brief \ref error_guessing
879478de5b1Stbbdev TEST_CASE("tbb::concurrent_hash_map rw_mutex constraints") {
880478de5b1Stbbdev     using key_type = int;
881478de5b1Stbbdev     using mapped_type = int;
882478de5b1Stbbdev     using namespace test_concepts::rw_mutex;
883478de5b1Stbbdev 
884478de5b1Stbbdev     test_chmap_mutex_constraints</*Expected = */true, key_type, mapped_type,
885478de5b1Stbbdev                                  Correct>();
886478de5b1Stbbdev 
887478de5b1Stbbdev     test_chmap_mutex_constraints</*Expected = */false, key_type, mapped_type,
888478de5b1Stbbdev                                  NoScopedLock, ScopedLockNoDefaultCtor, ScopedLockNoMutexCtor,
889478de5b1Stbbdev                                  ScopedLockNoDtor, ScopedLockNoAcquire, ScopedLockWrongFirstInputAcquire, ScopedLockWrongSecondInputAcquire, ScopedLockNoTryAcquire,
890478de5b1Stbbdev                                  ScopedLockWrongFirstInputTryAcquire, ScopedLockWrongSecondInputTryAcquire, ScopedLockWrongReturnTryAcquire, ScopedLockNoRelease,
891478de5b1Stbbdev                                  ScopedLockNoUpgrade, ScopedLockWrongReturnUpgrade, ScopedLockNoDowngrade, ScopedLockWrongReturnDowngrade,
892478de5b1Stbbdev                                  ScopedLockNoIsWriter, ScopedLockIsWriterNonConst, ScopedLockWrongReturnIsWriter>();
893478de5b1Stbbdev }
894478de5b1Stbbdev 
895478de5b1Stbbdev //! \brief \ref error_guessing
896478de5b1Stbbdev TEST_CASE("container_range concept for tbb::concurrent_hash_map ranges") {
897478de5b1Stbbdev     static_assert(test_concepts::container_range<tbb::concurrent_hash_map<int, int>::range_type>);
898478de5b1Stbbdev     static_assert(test_concepts::container_range<tbb::concurrent_hash_map<int, int>::const_range_type>);
899478de5b1Stbbdev }
900478de5b1Stbbdev 
901478de5b1Stbbdev #endif // __TBB_CPP20_CONCEPTS_PRESENT
902