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