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 
1751c0b2f7Stbbdev #include <common/test.h>
1851c0b2f7Stbbdev #include <common/utils.h>
1951c0b2f7Stbbdev #include "common/utils_report.h"
2051c0b2f7Stbbdev #include <common/spin_barrier.h>
2151c0b2f7Stbbdev #include <common/state_trackable.h>
2251c0b2f7Stbbdev #include <common/container_move_support.h>
2351c0b2f7Stbbdev #include <common/containers_common.h>
2451c0b2f7Stbbdev #include <common/initializer_list_support.h>
2551c0b2f7Stbbdev #include <common/vector_types.h>
26b15aabb3Stbbdev #include <common/test_comparisons.h>
2749e08aacStbbdev #include "oneapi/tbb/concurrent_hash_map.h"
2849e08aacStbbdev #include "oneapi/tbb/global_control.h"
2949e08aacStbbdev #include "oneapi/tbb/parallel_for.h"
3051c0b2f7Stbbdev 
3151c0b2f7Stbbdev //! \file conformance_concurrent_hash_map.cpp
3251c0b2f7Stbbdev //! \brief Test for [containers.concurrent_hash_map containers.tbb_hash_compare] specification
3351c0b2f7Stbbdev 
3451c0b2f7Stbbdev /** Has tightly controlled interface so that we can verify
3551c0b2f7Stbbdev     that concurrent_hash_map uses only the required interface. */
3651c0b2f7Stbbdev class MyException : public std::bad_alloc {
3751c0b2f7Stbbdev public:
what() const3851c0b2f7Stbbdev     virtual const char *what() const throw() override { return "out of items limit"; }
~MyException()3951c0b2f7Stbbdev     virtual ~MyException() throw() {}
4051c0b2f7Stbbdev };
4151c0b2f7Stbbdev 
4251c0b2f7Stbbdev /** Has tightly controlled interface so that we can verify
4351c0b2f7Stbbdev     that concurrent_hash_map uses only the required interface. */
4451c0b2f7Stbbdev class MyKey {
4551c0b2f7Stbbdev private:
4651c0b2f7Stbbdev     int key;
4751c0b2f7Stbbdev     friend class MyHashCompare;
4851c0b2f7Stbbdev     friend class YourHashCompare;
4951c0b2f7Stbbdev public:
5051c0b2f7Stbbdev     MyKey() = default;
5151c0b2f7Stbbdev     MyKey( const MyKey& ) = default;
5251c0b2f7Stbbdev     void operator=( const MyKey&  ) = delete;
make(int i)5351c0b2f7Stbbdev     static MyKey make( int i ) {
5451c0b2f7Stbbdev         MyKey result;
5551c0b2f7Stbbdev         result.key = i;
5651c0b2f7Stbbdev         return result;
5751c0b2f7Stbbdev     }
5851c0b2f7Stbbdev 
value_of() const5951c0b2f7Stbbdev     int value_of() const { return key; }
6051c0b2f7Stbbdev };
6151c0b2f7Stbbdev 
6251c0b2f7Stbbdev std::atomic<long> MyDataCount;
6351c0b2f7Stbbdev long MyDataCountLimit = 0;
6451c0b2f7Stbbdev 
6551c0b2f7Stbbdev class MyData {
6651c0b2f7Stbbdev protected:
6751c0b2f7Stbbdev     friend class MyData2;
6851c0b2f7Stbbdev     int data;
6951c0b2f7Stbbdev     enum state_t {
7051c0b2f7Stbbdev         LIVE=0x1234,
7151c0b2f7Stbbdev         DEAD=0x5678
7251c0b2f7Stbbdev     } my_state;
7351c0b2f7Stbbdev     void operator=( const MyData& );    // Deny access
7451c0b2f7Stbbdev public:
MyData(int i=0)7551c0b2f7Stbbdev     MyData(int i = 0) {
7651c0b2f7Stbbdev         my_state = LIVE;
7751c0b2f7Stbbdev         data = i;
7851c0b2f7Stbbdev         if (MyDataCountLimit && MyDataCount + 1 >= MyDataCountLimit) {
7951c0b2f7Stbbdev             TBB_TEST_THROW(MyException{});
8051c0b2f7Stbbdev         }
8151c0b2f7Stbbdev         ++MyDataCount;
8251c0b2f7Stbbdev     }
8351c0b2f7Stbbdev 
MyData(const MyData & other)8451c0b2f7Stbbdev     MyData( const MyData& other ) {
85ec39c546SAlex         CHECK_FAST(other.my_state==LIVE);
8651c0b2f7Stbbdev         my_state = LIVE;
8751c0b2f7Stbbdev         data = other.data;
8851c0b2f7Stbbdev         if(MyDataCountLimit && MyDataCount + 1 >= MyDataCountLimit) {
8951c0b2f7Stbbdev             TBB_TEST_THROW(MyException{});
9051c0b2f7Stbbdev         }
9151c0b2f7Stbbdev         ++MyDataCount;
9251c0b2f7Stbbdev     }
9351c0b2f7Stbbdev 
~MyData()9451c0b2f7Stbbdev     ~MyData() {
9551c0b2f7Stbbdev         --MyDataCount;
9651c0b2f7Stbbdev         my_state = DEAD;
9751c0b2f7Stbbdev     }
9851c0b2f7Stbbdev 
make(int i)9951c0b2f7Stbbdev     static MyData make( int i ) {
10051c0b2f7Stbbdev         MyData result;
10151c0b2f7Stbbdev         result.data = i;
10251c0b2f7Stbbdev         return result;
10351c0b2f7Stbbdev     }
10451c0b2f7Stbbdev 
value_of() const10551c0b2f7Stbbdev     int value_of() const {
106ec39c546SAlex         CHECK_FAST(my_state==LIVE);
10751c0b2f7Stbbdev         return data;
10851c0b2f7Stbbdev     }
10951c0b2f7Stbbdev 
set_value(int i)11051c0b2f7Stbbdev     void set_value( int i ) {
111ec39c546SAlex         CHECK_FAST(my_state==LIVE);
11251c0b2f7Stbbdev         data = i;
11351c0b2f7Stbbdev     }
11451c0b2f7Stbbdev 
operator ==(const MyData & other) const11551c0b2f7Stbbdev     bool operator==( const MyData& other ) const {
116ec39c546SAlex         CHECK_FAST(other.my_state==LIVE);
117ec39c546SAlex         CHECK_FAST(my_state==LIVE);
11851c0b2f7Stbbdev         return data == other.data;
11951c0b2f7Stbbdev     }
12051c0b2f7Stbbdev };
12151c0b2f7Stbbdev 
12251c0b2f7Stbbdev class MyData2 : public MyData {
12351c0b2f7Stbbdev public:
MyData2()12451c0b2f7Stbbdev     MyData2( ) {}
12551c0b2f7Stbbdev 
MyData2(const MyData2 & other)12651c0b2f7Stbbdev     MyData2( const MyData2& other ) : MyData() {
127ec39c546SAlex         CHECK_FAST(other.my_state==LIVE);
128ec39c546SAlex         CHECK_FAST(my_state==LIVE);
12951c0b2f7Stbbdev         data = other.data;
13051c0b2f7Stbbdev     }
13151c0b2f7Stbbdev 
MyData2(const MyData & other)13251c0b2f7Stbbdev     MyData2( const MyData& other ) {
133ec39c546SAlex         CHECK_FAST(other.my_state==LIVE);
134ec39c546SAlex         CHECK_FAST(my_state==LIVE);
13551c0b2f7Stbbdev         data = other.data;
13651c0b2f7Stbbdev     }
13751c0b2f7Stbbdev 
operator =(const MyData & other)13851c0b2f7Stbbdev     void operator=( const MyData& other ) {
139ec39c546SAlex         CHECK_FAST(other.my_state==LIVE);
140ec39c546SAlex         CHECK_FAST(my_state==LIVE);
14151c0b2f7Stbbdev         data = other.data;
14251c0b2f7Stbbdev     }
14351c0b2f7Stbbdev 
operator =(const MyData2 & other)14451c0b2f7Stbbdev     void operator=( const MyData2& other ) {
145ec39c546SAlex         CHECK_FAST(other.my_state==LIVE);
146ec39c546SAlex         CHECK_FAST(my_state==LIVE);
14751c0b2f7Stbbdev         data = other.data;
14851c0b2f7Stbbdev     }
14951c0b2f7Stbbdev 
operator ==(const MyData2 & other) const15051c0b2f7Stbbdev     bool operator==( const MyData2& other ) const {
151ec39c546SAlex         CHECK_FAST(other.my_state==LIVE);
152ec39c546SAlex         CHECK_FAST(my_state==LIVE);
15351c0b2f7Stbbdev         return data == other.data;
15451c0b2f7Stbbdev     }
15551c0b2f7Stbbdev };
15651c0b2f7Stbbdev 
15751c0b2f7Stbbdev class MyHashCompare {
15851c0b2f7Stbbdev public:
equal(const MyKey & j,const MyKey & k) const15951c0b2f7Stbbdev     bool equal( const MyKey& j, const MyKey& k ) const {
16051c0b2f7Stbbdev         return j.key==k.key;
16151c0b2f7Stbbdev     }
16251c0b2f7Stbbdev 
hash(const MyKey & k) const1634a23d002Skboyarinov     std::size_t hash( const MyKey& k ) const {
16451c0b2f7Stbbdev         return k.key;
16551c0b2f7Stbbdev     }
16651c0b2f7Stbbdev };
16751c0b2f7Stbbdev 
16851c0b2f7Stbbdev class YourHashCompare {
16951c0b2f7Stbbdev public:
equal(const MyKey & j,const MyKey & k) const17051c0b2f7Stbbdev     bool equal( const MyKey& j, const MyKey& k ) const {
17151c0b2f7Stbbdev         return j.key==k.key;
17251c0b2f7Stbbdev     }
17351c0b2f7Stbbdev 
hash(const MyKey &) const1744a23d002Skboyarinov     std::size_t hash( const MyKey& ) const {
17551c0b2f7Stbbdev         return 1;
17651c0b2f7Stbbdev     }
17751c0b2f7Stbbdev };
17851c0b2f7Stbbdev 
17951c0b2f7Stbbdev using test_allocator_type = StaticSharedCountingAllocator<std::allocator<std::pair<const MyKey, MyData>>>;
18049e08aacStbbdev using test_table_type = oneapi::tbb::concurrent_hash_map<MyKey, MyData, MyHashCompare, test_allocator_type>;
18149e08aacStbbdev using other_test_table_type = oneapi::tbb::concurrent_hash_map<MyKey, MyData2, MyHashCompare>;
18251c0b2f7Stbbdev 
18351c0b2f7Stbbdev template <template <typename...> class ContainerType>
test_member_types()18451c0b2f7Stbbdev void test_member_types() {
18551c0b2f7Stbbdev     using container_type = ContainerType<int, int>;
18649e08aacStbbdev     static_assert(std::is_same<typename container_type::allocator_type, oneapi::tbb::tbb_allocator<std::pair<const int, int>>>::value,
18751c0b2f7Stbbdev                   "Incorrect default template allocator");
18851c0b2f7Stbbdev 
18951c0b2f7Stbbdev     static_assert(std::is_same<typename container_type::key_type, int>::value,
19051c0b2f7Stbbdev                   "Incorrect container key_type member type");
19151c0b2f7Stbbdev     static_assert(std::is_same<typename container_type::value_type, std::pair<const int, int>>::value,
19251c0b2f7Stbbdev                   "Incorrect container value_type member type");
19351c0b2f7Stbbdev 
19451c0b2f7Stbbdev     static_assert(std::is_unsigned<typename container_type::size_type>::value,
19551c0b2f7Stbbdev                   "Incorrect container size_type member type");
19651c0b2f7Stbbdev     static_assert(std::is_signed<typename container_type::difference_type>::value,
19751c0b2f7Stbbdev                   "Incorrect container difference_type member type");
19851c0b2f7Stbbdev 
19951c0b2f7Stbbdev     using value_type = typename container_type::value_type;
20051c0b2f7Stbbdev     static_assert(std::is_same<typename container_type::reference, value_type&>::value,
20151c0b2f7Stbbdev                   "Incorrect container reference member type");
20251c0b2f7Stbbdev     static_assert(std::is_same<typename container_type::const_reference, const value_type&>::value,
20351c0b2f7Stbbdev                   "Incorrect container const_reference member type");
20451c0b2f7Stbbdev     using allocator_type = typename container_type::allocator_type;
20551c0b2f7Stbbdev     static_assert(std::is_same<typename container_type::pointer, typename std::allocator_traits<allocator_type>::pointer>::value,
20651c0b2f7Stbbdev                   "Incorrect container pointer member type");
20751c0b2f7Stbbdev     static_assert(std::is_same<typename container_type::const_pointer, typename std::allocator_traits<allocator_type>::const_pointer>::value,
20851c0b2f7Stbbdev                   "Incorrect container const_pointer member type");
20951c0b2f7Stbbdev 
21051c0b2f7Stbbdev     static_assert(utils::is_forward_iterator<typename container_type::iterator>::value,
21151c0b2f7Stbbdev                   "Incorrect container iterator member type");
21251c0b2f7Stbbdev     static_assert(!std::is_const<typename container_type::iterator::value_type>::value,
21351c0b2f7Stbbdev                   "Incorrect container iterator member type");
21451c0b2f7Stbbdev     static_assert(utils::is_forward_iterator<typename container_type::const_iterator>::value,
21551c0b2f7Stbbdev                   "Incorrect container const_iterator member type");
21651c0b2f7Stbbdev     static_assert(std::is_const<typename container_type::const_iterator::value_type>::value,
21751c0b2f7Stbbdev                   "Incorrect container iterator member type");
21851c0b2f7Stbbdev }
21951c0b2f7Stbbdev 
22051c0b2f7Stbbdev template<typename test_table_type>
FillTable(test_table_type & x,int n)22151c0b2f7Stbbdev void FillTable( test_table_type& x, int n ) {
22251c0b2f7Stbbdev     for( int i=1; i<=n; ++i ) {
22351c0b2f7Stbbdev         MyKey key( MyKey::make(-i) ); // hash values must not be specified in direct order
22451c0b2f7Stbbdev         typename test_table_type::accessor a;
22551c0b2f7Stbbdev         bool b = x.insert(a,key);
226ec39c546SAlex         CHECK_FAST(b);
22751c0b2f7Stbbdev         a->second.set_value( i*i );
22851c0b2f7Stbbdev     }
22951c0b2f7Stbbdev }
23051c0b2f7Stbbdev 
23151c0b2f7Stbbdev template<typename test_table_type>
CheckTable(const test_table_type & x,int n)23251c0b2f7Stbbdev static void CheckTable( const test_table_type& x, int n ) {
23351c0b2f7Stbbdev     REQUIRE_MESSAGE( x.size()==size_t(n), "table is different size than expected" );
23451c0b2f7Stbbdev     CHECK(x.empty()==(n==0));
23551c0b2f7Stbbdev     CHECK(x.size()<=x.max_size());
23651c0b2f7Stbbdev     for( int i=1; i<=n; ++i ) {
23751c0b2f7Stbbdev         MyKey key( MyKey::make(-i) );
23851c0b2f7Stbbdev         typename test_table_type::const_accessor a;
23951c0b2f7Stbbdev         bool b = x.find(a,key);
240ec39c546SAlex         CHECK_FAST(b);
241ec39c546SAlex         CHECK_FAST(a->second.value_of()==i*i);
24251c0b2f7Stbbdev     }
24351c0b2f7Stbbdev     int count = 0;
24451c0b2f7Stbbdev     int key_sum = 0;
24551c0b2f7Stbbdev     for( typename test_table_type::const_iterator i(x.begin()); i!=x.end(); ++i ) {
24651c0b2f7Stbbdev         ++count;
24751c0b2f7Stbbdev         key_sum += -i->first.value_of();
24851c0b2f7Stbbdev     }
24951c0b2f7Stbbdev     CHECK(count==n);
25051c0b2f7Stbbdev     CHECK(key_sum==n*(n+1)/2);
25151c0b2f7Stbbdev }
25251c0b2f7Stbbdev 
TestCopy()25351c0b2f7Stbbdev void TestCopy() {
25451c0b2f7Stbbdev     INFO("testing copy\n");
25551c0b2f7Stbbdev     test_table_type t1;
25651c0b2f7Stbbdev     for( int i=0; i<10000; i=(i<100 ? i+1 : i*3) ) {
25751c0b2f7Stbbdev         MyDataCount = 0;
25851c0b2f7Stbbdev 
25951c0b2f7Stbbdev         FillTable(t1,i);
26051c0b2f7Stbbdev         // Do not call CheckTable(t1,i) before copying, it enforces rehashing
26151c0b2f7Stbbdev 
26251c0b2f7Stbbdev         test_table_type t2(t1);
26351c0b2f7Stbbdev         // Check that copy constructor did not mangle source table.
26451c0b2f7Stbbdev         CheckTable(t1,i);
26551c0b2f7Stbbdev         swap(t1, t2);
26651c0b2f7Stbbdev         CheckTable(t1,i);
26751c0b2f7Stbbdev         CHECK(!(t1 != t2));
26851c0b2f7Stbbdev 
26951c0b2f7Stbbdev         // Clear original table
27051c0b2f7Stbbdev         t2.clear();
27151c0b2f7Stbbdev         swap(t2, t1);
27251c0b2f7Stbbdev         CheckTable(t1,0);
27351c0b2f7Stbbdev 
27451c0b2f7Stbbdev         // Verify that copy of t1 is correct, even after t1 is cleared.
27551c0b2f7Stbbdev         CheckTable(t2,i);
27651c0b2f7Stbbdev         t2.clear();
27751c0b2f7Stbbdev         t1.swap( t2 );
27851c0b2f7Stbbdev         CheckTable(t1,0);
27951c0b2f7Stbbdev         CheckTable(t2,0);
28051c0b2f7Stbbdev         REQUIRE_MESSAGE( MyDataCount==0, "data leak?" );
28151c0b2f7Stbbdev     }
28251c0b2f7Stbbdev }
28351c0b2f7Stbbdev 
TestRehash()28451c0b2f7Stbbdev void TestRehash() {
28551c0b2f7Stbbdev     INFO("testing rehashing\n");
28651c0b2f7Stbbdev     test_table_type w;
28751c0b2f7Stbbdev     w.insert( std::make_pair(MyKey::make(-5), MyData()) );
28851c0b2f7Stbbdev     w.rehash(); // without this, assertion will fail
28951c0b2f7Stbbdev     test_table_type::iterator it = w.begin();
29051c0b2f7Stbbdev     int i = 0; // check for non-rehashed buckets
29151c0b2f7Stbbdev     for( ; it != w.end(); i++ )
29251c0b2f7Stbbdev         w.count( (it++)->first );
29351c0b2f7Stbbdev     CHECK(i == 1);
29451c0b2f7Stbbdev     for( i=0; i<1000; i=(i<29 ? i+1 : i*2) ) {
29551c0b2f7Stbbdev         for( int j=std::max(256+i, i*2); j<10000; j*=3 ) {
29651c0b2f7Stbbdev             test_table_type v;
29751c0b2f7Stbbdev             FillTable( v, i );
29851c0b2f7Stbbdev             CHECK(int(v.size()) == i);
29951c0b2f7Stbbdev             CHECK(int(v.bucket_count()) <= j);
30051c0b2f7Stbbdev             v.rehash( j );
30151c0b2f7Stbbdev             CHECK(int(v.bucket_count()) >= j);
30251c0b2f7Stbbdev             CheckTable( v, i );
30351c0b2f7Stbbdev         }
30451c0b2f7Stbbdev     }
30551c0b2f7Stbbdev }
30651c0b2f7Stbbdev 
TestAssignment()30751c0b2f7Stbbdev void TestAssignment() {
30851c0b2f7Stbbdev     INFO("testing assignment\n");
30949e08aacStbbdev     oneapi::tbb::concurrent_hash_map<int, int> test_map({{1, 2}, {2, 4}});
31051c0b2f7Stbbdev     test_map.operator=(test_map); // suppress self assign warning
31151c0b2f7Stbbdev     CHECK(!test_map.empty());
31251c0b2f7Stbbdev 
31351c0b2f7Stbbdev     for( int i=0; i<1000; i=(i<30 ? i+1 : i*5) ) {
31451c0b2f7Stbbdev         for( int j=0; j<1000; j=(j<30 ? j+1 : j*7) ) {
31551c0b2f7Stbbdev             test_table_type t1;
31651c0b2f7Stbbdev             test_table_type t2;
31751c0b2f7Stbbdev             FillTable(t1,i);
31851c0b2f7Stbbdev             FillTable(t2,j);
31951c0b2f7Stbbdev             CHECK((t1 == t2) == (i == j));
32051c0b2f7Stbbdev             CheckTable(t2,j);
32151c0b2f7Stbbdev 
32251c0b2f7Stbbdev             test_table_type& tref = t2=t1;
32351c0b2f7Stbbdev             CHECK(&tref==&t2);
32451c0b2f7Stbbdev             CHECK(t1 == t2);
32551c0b2f7Stbbdev             CheckTable(t1,i);
32651c0b2f7Stbbdev             CheckTable(t2,i);
32751c0b2f7Stbbdev 
32851c0b2f7Stbbdev             t1.clear();
32951c0b2f7Stbbdev             CheckTable(t1,0);
33051c0b2f7Stbbdev             CheckTable(t2,i);
33151c0b2f7Stbbdev             REQUIRE_MESSAGE( MyDataCount==i, "data leak?" );
33251c0b2f7Stbbdev 
33351c0b2f7Stbbdev             t2.clear();
33451c0b2f7Stbbdev             CheckTable(t1,0);
33551c0b2f7Stbbdev             CheckTable(t2,0);
33651c0b2f7Stbbdev             REQUIRE_MESSAGE( MyDataCount==0, "data leak?" );
33751c0b2f7Stbbdev         }
33851c0b2f7Stbbdev     }
33951c0b2f7Stbbdev }
34051c0b2f7Stbbdev 
34151c0b2f7Stbbdev template<typename Iterator, typename T>
TestIteratorTraits()34251c0b2f7Stbbdev void TestIteratorTraits() {
34351c0b2f7Stbbdev     T x;
34451c0b2f7Stbbdev     typename Iterator::reference xr = x;
34551c0b2f7Stbbdev     typename Iterator::pointer xp = &x;
34651c0b2f7Stbbdev     CHECK(&xr==xp);
34751c0b2f7Stbbdev }
34851c0b2f7Stbbdev 
34951c0b2f7Stbbdev template<typename Iterator1, typename Iterator2>
TestIteratorAssignment(Iterator2 j)35051c0b2f7Stbbdev void TestIteratorAssignment( Iterator2 j ) {
35151c0b2f7Stbbdev     Iterator1 i(j), k;
35251c0b2f7Stbbdev     CHECK(i==j);
35351c0b2f7Stbbdev     CHECK(!(i!=j));
35451c0b2f7Stbbdev     k = j;
35551c0b2f7Stbbdev     CHECK(k==j);
35651c0b2f7Stbbdev     CHECK(!(k!=j));
35751c0b2f7Stbbdev }
35851c0b2f7Stbbdev 
35951c0b2f7Stbbdev template<typename Range1, typename Range2>
TestRangeAssignment(Range2 r2)36051c0b2f7Stbbdev void TestRangeAssignment( Range2 r2 ) {
36151c0b2f7Stbbdev     Range1 r1(r2); r1 = r2;
36251c0b2f7Stbbdev }
36351c0b2f7Stbbdev 
TestIteratorsAndRanges()36451c0b2f7Stbbdev void TestIteratorsAndRanges() {
36551c0b2f7Stbbdev     INFO("testing iterators compliance\n");
36651c0b2f7Stbbdev     TestIteratorTraits<test_table_type::iterator,test_table_type::value_type>();
36751c0b2f7Stbbdev     TestIteratorTraits<test_table_type::const_iterator,const test_table_type::value_type>();
36851c0b2f7Stbbdev 
36951c0b2f7Stbbdev     test_table_type v;
37051c0b2f7Stbbdev     CHECK(v.range().grainsize() == 1);
37151c0b2f7Stbbdev     test_table_type const &u = v;
37251c0b2f7Stbbdev 
37351c0b2f7Stbbdev     TestIteratorAssignment<test_table_type::const_iterator>( u.begin() );
37451c0b2f7Stbbdev     TestIteratorAssignment<test_table_type::const_iterator>( v.begin() );
37551c0b2f7Stbbdev     TestIteratorAssignment<test_table_type::iterator>( v.begin() );
37651c0b2f7Stbbdev     // doesn't compile as expected: TestIteratorAssignment<typename V::iterator>( u.begin() );
37751c0b2f7Stbbdev 
37851c0b2f7Stbbdev     // check for non-existing
37951c0b2f7Stbbdev     CHECK(v.equal_range(MyKey::make(-1)) == std::make_pair(v.end(), v.end()));
38051c0b2f7Stbbdev     CHECK(u.equal_range(MyKey::make(-1)) == std::make_pair(u.end(), u.end()));
38151c0b2f7Stbbdev 
38251c0b2f7Stbbdev     INFO("testing ranges compliance\n");
38351c0b2f7Stbbdev     TestRangeAssignment<test_table_type::const_range_type>( u.range() );
38451c0b2f7Stbbdev     TestRangeAssignment<test_table_type::range_type>( v.range() );
38551c0b2f7Stbbdev     // doesn't compile as expected: TestRangeAssignment<typename V::range_type>( u.range() );
38651c0b2f7Stbbdev 
38751c0b2f7Stbbdev     INFO("testing construction and insertion from iterators range\n");
38851c0b2f7Stbbdev     FillTable( v, 1000 );
38951c0b2f7Stbbdev     other_test_table_type t(v.begin(), v.end());
39051c0b2f7Stbbdev     v.rehash();
39151c0b2f7Stbbdev     CheckTable(t, 1000);
39251c0b2f7Stbbdev     t.insert(v.begin(), v.end()); // do nothing
39351c0b2f7Stbbdev     CheckTable(t, 1000);
39451c0b2f7Stbbdev     t.clear();
39551c0b2f7Stbbdev     t.insert(v.begin(), v.end()); // restore
39651c0b2f7Stbbdev     CheckTable(t, 1000);
39751c0b2f7Stbbdev 
39851c0b2f7Stbbdev     INFO("testing comparison\n");
39951c0b2f7Stbbdev     using test_allocator_type2 = StaticSharedCountingAllocator<std::allocator<std::pair<const MyKey, MyData2>>>;
40049e08aacStbbdev     using YourTable1 = oneapi::tbb::concurrent_hash_map<MyKey,MyData2,YourHashCompare, test_allocator_type2>;
40149e08aacStbbdev     using YourTable2 = oneapi::tbb::concurrent_hash_map<MyKey,MyData2,YourHashCompare>;
40251c0b2f7Stbbdev     YourTable1 t1;
40351c0b2f7Stbbdev     FillTable( t1, 10 );
40451c0b2f7Stbbdev     CheckTable(t1, 10 );
40551c0b2f7Stbbdev     YourTable2 t2(t1.begin(), t1.end());
40651c0b2f7Stbbdev     MyKey key( MyKey::make(-5) ); MyData2 data;
40751c0b2f7Stbbdev     CHECK(t2.erase(key));
40851c0b2f7Stbbdev     YourTable2::accessor a;
40951c0b2f7Stbbdev     CHECK(t2.insert(a, key));
41051c0b2f7Stbbdev     data.set_value(0);   a->second = data;
41151c0b2f7Stbbdev     CHECK(t1 != t2);
41251c0b2f7Stbbdev     data.set_value(5*5); a->second = data;
41351c0b2f7Stbbdev     CHECK(t1 == t2);
41451c0b2f7Stbbdev }
41551c0b2f7Stbbdev 
41651c0b2f7Stbbdev struct test_insert {
41751c0b2f7Stbbdev     template<typename container_type, typename element_type>
testtest_insert41851c0b2f7Stbbdev     static void test( std::initializer_list<element_type> il, container_type const& expected ) {
41951c0b2f7Stbbdev         container_type vd;
42051c0b2f7Stbbdev         vd.insert( il );
42151c0b2f7Stbbdev         REQUIRE_MESSAGE( vd == expected, "inserting with an initializer list failed" );
42251c0b2f7Stbbdev     }
42351c0b2f7Stbbdev };
42451c0b2f7Stbbdev 
425112076d0SIlya Isaev struct ctor_test {
426112076d0SIlya Isaev  template<typename container_type, typename element_type>
testctor_test427112076d0SIlya Isaev     static void test( std::initializer_list<element_type> il, container_type const& expected ) {
428112076d0SIlya Isaev         container_type vd(il, tbb::tbb_allocator<std::pair<element_type, element_type>>{});
429112076d0SIlya Isaev         REQUIRE_MESSAGE( vd == expected, "inserting with an initializer list failed" );
430112076d0SIlya Isaev     }
431112076d0SIlya Isaev };
432112076d0SIlya Isaev 
TestInitList()43351c0b2f7Stbbdev void TestInitList(){
43451c0b2f7Stbbdev     using namespace initializer_list_support_tests;
43551c0b2f7Stbbdev     INFO("testing initializer_list methods \n");
43651c0b2f7Stbbdev 
43749e08aacStbbdev     using ch_map_type = oneapi::tbb::concurrent_hash_map<int,int>;
43851c0b2f7Stbbdev     std::initializer_list<ch_map_type::value_type> pairs_il = {{1,1},{2,2},{3,3},{4,4},{5,5}};
43951c0b2f7Stbbdev 
44051c0b2f7Stbbdev     test_initializer_list_support_without_assign<ch_map_type, test_insert>( pairs_il );
44151c0b2f7Stbbdev     test_initializer_list_support_without_assign<ch_map_type, test_insert>( {} );
442112076d0SIlya Isaev     test_initializer_list_support_without_assign<ch_map_type, ctor_test>(pairs_il);
44351c0b2f7Stbbdev }
44451c0b2f7Stbbdev 
44551c0b2f7Stbbdev template <typename base_alloc_type>
44651c0b2f7Stbbdev class only_node_counting_allocator : public StaticSharedCountingAllocator<base_alloc_type> {
44751c0b2f7Stbbdev     using base_type = StaticSharedCountingAllocator<base_alloc_type>;
44849e08aacStbbdev     using base_traits = oneapi::tbb::detail::allocator_traits<base_alloc_type>;
44951c0b2f7Stbbdev public:
45051c0b2f7Stbbdev     template<typename U>
45151c0b2f7Stbbdev     struct rebind {
45251c0b2f7Stbbdev         using other = only_node_counting_allocator<typename base_traits::template rebind_alloc<U>>;
45351c0b2f7Stbbdev     };
45451c0b2f7Stbbdev 
only_node_counting_allocator()45551c0b2f7Stbbdev     only_node_counting_allocator() : base_type() {}
only_node_counting_allocator(const only_node_counting_allocator & a)45651c0b2f7Stbbdev     only_node_counting_allocator(const only_node_counting_allocator& a) : base_type(a) {}
45751c0b2f7Stbbdev 
45851c0b2f7Stbbdev     template<typename U>
only_node_counting_allocator(const only_node_counting_allocator<U> & a)45951c0b2f7Stbbdev     only_node_counting_allocator(const only_node_counting_allocator<U>& a) : base_type(a) {}
46051c0b2f7Stbbdev 
allocate(const std::size_t n)46151c0b2f7Stbbdev     typename base_type::value_type* allocate(const std::size_t n) {
46251c0b2f7Stbbdev         if ( n > 1) {
46351c0b2f7Stbbdev             return base_alloc_type::allocate(n);
46451c0b2f7Stbbdev         } else {
46551c0b2f7Stbbdev             return base_type::allocate(n);
46651c0b2f7Stbbdev         }
46751c0b2f7Stbbdev     }
46851c0b2f7Stbbdev };
46951c0b2f7Stbbdev 
47051c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
TestExceptions()47151c0b2f7Stbbdev void TestExceptions() {
47249e08aacStbbdev     using allocator_type = only_node_counting_allocator<oneapi::tbb::tbb_allocator<std::pair<const MyKey, MyData2>>>;
47349e08aacStbbdev     using throwing_table = oneapi::tbb::concurrent_hash_map<MyKey, MyData2, MyHashCompare, allocator_type>;
47451c0b2f7Stbbdev     enum methods {
47551c0b2f7Stbbdev         zero_method = 0,
47651c0b2f7Stbbdev         ctor_copy, op_assign, op_insert,
47751c0b2f7Stbbdev         all_methods
47851c0b2f7Stbbdev     };
47951c0b2f7Stbbdev 
48051c0b2f7Stbbdev     INFO("testing exception-safety guarantees\n");
48151c0b2f7Stbbdev     throwing_table src;
48251c0b2f7Stbbdev     FillTable( src, 1000 );
48351c0b2f7Stbbdev     CHECK(MyDataCount==1000);
48451c0b2f7Stbbdev 
48551c0b2f7Stbbdev     try {
48651c0b2f7Stbbdev         for(int t = 0; t < 2; t++) // exception type
48751c0b2f7Stbbdev         for(int m = zero_method+1; m < all_methods; m++)
48851c0b2f7Stbbdev         {
48951c0b2f7Stbbdev             allocator_type a;
49051c0b2f7Stbbdev             allocator_type::init_counters();
49151c0b2f7Stbbdev             if(t) MyDataCountLimit = 101;
49251c0b2f7Stbbdev             else a.set_limits(101);
49351c0b2f7Stbbdev             throwing_table victim(a);
49451c0b2f7Stbbdev             MyDataCount = 0;
49551c0b2f7Stbbdev 
49651c0b2f7Stbbdev             try {
49751c0b2f7Stbbdev                 switch(m) {
49851c0b2f7Stbbdev                 case ctor_copy: {
49951c0b2f7Stbbdev                         throwing_table acopy(src, a);
50051c0b2f7Stbbdev                     } break;
50151c0b2f7Stbbdev                 case op_assign: {
50251c0b2f7Stbbdev                         victim = src;
50351c0b2f7Stbbdev                     } break;
50451c0b2f7Stbbdev                 case op_insert: {
50551c0b2f7Stbbdev                         // Insertion in cpp11 don't make copy constructions
50651c0b2f7Stbbdev                         // during the insertion, so we need to decrement limit
50751c0b2f7Stbbdev                         // to throw an exception in the right place and to prevent
50851c0b2f7Stbbdev                         // successful insertion of one unexpected item
50951c0b2f7Stbbdev                         if (MyDataCountLimit)
51051c0b2f7Stbbdev                             --MyDataCountLimit;
51151c0b2f7Stbbdev                         FillTable( victim, 1000 );
51251c0b2f7Stbbdev                     } break;
51351c0b2f7Stbbdev                 default:;
51451c0b2f7Stbbdev                 }
51551c0b2f7Stbbdev                 REQUIRE_MESSAGE(false, "should throw an exception");
51651c0b2f7Stbbdev             } catch(std::bad_alloc &e) {
51751c0b2f7Stbbdev                 MyDataCountLimit = 0;
51851c0b2f7Stbbdev                 size_t size = victim.size();
51951c0b2f7Stbbdev                 switch(m) {
52051c0b2f7Stbbdev                 case op_assign:
52151c0b2f7Stbbdev                     REQUIRE_MESSAGE( MyDataCount==100, "data leak?" );
52251c0b2f7Stbbdev                     CHECK(size>=100);
52351c0b2f7Stbbdev                     utils_fallthrough;
52451c0b2f7Stbbdev                 case ctor_copy:
52551c0b2f7Stbbdev                     CheckTable(src, 1000);
52651c0b2f7Stbbdev                     break;
52751c0b2f7Stbbdev                 case op_insert:
52851c0b2f7Stbbdev                     CHECK(size==size_t(100-t));
52951c0b2f7Stbbdev                     REQUIRE_MESSAGE( MyDataCount==100-t, "data leak?" );
53051c0b2f7Stbbdev                     CheckTable(victim, 100-t);
53151c0b2f7Stbbdev                     break;
53251c0b2f7Stbbdev 
53351c0b2f7Stbbdev                 default:; // nothing to check here
53451c0b2f7Stbbdev                 }
53551c0b2f7Stbbdev                 INFO("Exception "<< m << " : " << e.what() << "- ok ()");
53651c0b2f7Stbbdev             }
53751c0b2f7Stbbdev             catch ( ... ) {
53851c0b2f7Stbbdev                 REQUIRE_MESSAGE( false, "Unrecognized exception" );
53951c0b2f7Stbbdev             }
54051c0b2f7Stbbdev         }
54151c0b2f7Stbbdev     } catch(...) {
54251c0b2f7Stbbdev         REQUIRE_MESSAGE(false, "unexpected exception");
54351c0b2f7Stbbdev     }
54451c0b2f7Stbbdev     src.clear(); MyDataCount = 0;
54551c0b2f7Stbbdev     allocator_type::max_items = 0;
54651c0b2f7Stbbdev }
54751c0b2f7Stbbdev #endif
54851c0b2f7Stbbdev 
54951c0b2f7Stbbdev struct default_container_traits {
55051c0b2f7Stbbdev     template <typename container_type, typename iterator_type>
construct_containerdefault_container_traits55151c0b2f7Stbbdev     static container_type& construct_container(typename std::aligned_storage<sizeof(container_type)>::type& storage, iterator_type begin, iterator_type end){
55251c0b2f7Stbbdev         container_type* ptr = reinterpret_cast<container_type*>(&storage);
55351c0b2f7Stbbdev         new (ptr) container_type(begin, end);
55451c0b2f7Stbbdev         return *ptr;
55551c0b2f7Stbbdev     }
55651c0b2f7Stbbdev 
55751c0b2f7Stbbdev     template <typename container_type, typename iterator_type, typename allocator_type>
construct_containerdefault_container_traits55851c0b2f7Stbbdev     static container_type& construct_container(typename std::aligned_storage<sizeof(container_type)>::type& storage, iterator_type begin, iterator_type end, allocator_type const& a){
55951c0b2f7Stbbdev         container_type* ptr = reinterpret_cast<container_type*>(&storage);
56051c0b2f7Stbbdev         new (ptr) container_type(begin, end, a);
56151c0b2f7Stbbdev         return *ptr;
56251c0b2f7Stbbdev     }
56351c0b2f7Stbbdev };
56451c0b2f7Stbbdev 
56551c0b2f7Stbbdev struct hash_map_traits : default_container_traits {
56651c0b2f7Stbbdev     enum{ expected_number_of_items_to_allocate_for_steal_move = 0 };
56751c0b2f7Stbbdev 
56851c0b2f7Stbbdev     template<typename T>
56951c0b2f7Stbbdev     struct hash_compare {
equalhash_map_traits::hash_compare57051c0b2f7Stbbdev         bool equal( const T& lhs, const T& rhs ) const {
57151c0b2f7Stbbdev             return lhs==rhs;
57251c0b2f7Stbbdev         }
hashhash_map_traits::hash_compare57351c0b2f7Stbbdev         size_t hash( const T& k ) const {
57451c0b2f7Stbbdev             return my_hash_func(k);
57551c0b2f7Stbbdev         }
57651c0b2f7Stbbdev         std::hash<T> my_hash_func;
57751c0b2f7Stbbdev     };
57851c0b2f7Stbbdev 
57951c0b2f7Stbbdev     template <typename T, typename Allocator>
58049e08aacStbbdev     using container_type = oneapi::tbb::concurrent_hash_map<T, T, hash_compare<T>, Allocator>;
58151c0b2f7Stbbdev 
58251c0b2f7Stbbdev     template <typename T>
58351c0b2f7Stbbdev     using container_value_type = std::pair<const T, T>;
58451c0b2f7Stbbdev 
58551c0b2f7Stbbdev     template<typename element_type, typename allocator_type>
58651c0b2f7Stbbdev     struct apply {
58749e08aacStbbdev         using type = oneapi::tbb::concurrent_hash_map<element_type, element_type, hash_compare<element_type>, allocator_type>;
58851c0b2f7Stbbdev     };
58951c0b2f7Stbbdev 
59051c0b2f7Stbbdev     using init_iterator_type = move_support_tests::FooPairIterator;
59151c0b2f7Stbbdev     template <typename hash_map_type, typename iterator>
equalhash_map_traits59251c0b2f7Stbbdev     static bool equal(hash_map_type const& c, iterator begin, iterator end){
59351c0b2f7Stbbdev         bool equal_sizes = ( static_cast<size_t>(std::distance(begin, end)) == c.size() );
59451c0b2f7Stbbdev         if (!equal_sizes)
59551c0b2f7Stbbdev             return false;
59651c0b2f7Stbbdev 
59751c0b2f7Stbbdev         for (iterator it = begin; it != end; ++it ){
59851c0b2f7Stbbdev             if (c.count( (*it).first) == 0){
59951c0b2f7Stbbdev                 return false;
60051c0b2f7Stbbdev             }
60151c0b2f7Stbbdev         }
60251c0b2f7Stbbdev         return true;
60351c0b2f7Stbbdev     }
60451c0b2f7Stbbdev };
60551c0b2f7Stbbdev 
60649e08aacStbbdev using DataStateTrackedTable = oneapi::tbb::concurrent_hash_map<MyKey, move_support_tests::Foo, MyHashCompare>;
60751c0b2f7Stbbdev 
60851c0b2f7Stbbdev struct RvalueInsert {
applyRvalueInsert60951c0b2f7Stbbdev     static void apply( DataStateTrackedTable& table, int i ) {
61051c0b2f7Stbbdev         DataStateTrackedTable::accessor a;
61151c0b2f7Stbbdev         int next = i + 1;
612ec39c546SAlex         CHECK_FAST_MESSAGE((table.insert( a, std::make_pair(MyKey::make(i), move_support_tests::Foo(next)))),
61351c0b2f7Stbbdev             "already present while should not ?" );
614ec39c546SAlex         CHECK_FAST((*a).second == next);
615ec39c546SAlex         CHECK_FAST((*a).second.state == StateTrackableBase::MoveInitialized);
61651c0b2f7Stbbdev     }
61751c0b2f7Stbbdev };
61851c0b2f7Stbbdev 
61951c0b2f7Stbbdev struct Emplace {
62051c0b2f7Stbbdev     template <typename Accessor>
apply_implEmplace62151c0b2f7Stbbdev     static void apply_impl( DataStateTrackedTable& table, int i) {
62251c0b2f7Stbbdev         Accessor a;
623ec39c546SAlex         CHECK_FAST_MESSAGE((table.emplace( a, MyKey::make(i), (i + 1))),
62451c0b2f7Stbbdev                 "already present while should not ?" );
625ec39c546SAlex         CHECK_FAST((*a).second == i + 1);
626ec39c546SAlex         CHECK_FAST((*a).second.state == StateTrackableBase::DirectInitialized);
62751c0b2f7Stbbdev     }
62851c0b2f7Stbbdev 
applyEmplace62951c0b2f7Stbbdev     static void apply( DataStateTrackedTable& table, int i ) {
63051c0b2f7Stbbdev         // TODO: investigate ability to rewrite apply methods with use apply_imp method.
63151c0b2f7Stbbdev         if (i % 2) {
63251c0b2f7Stbbdev             apply_impl<DataStateTrackedTable::accessor>(table, i);
63351c0b2f7Stbbdev         } else {
63451c0b2f7Stbbdev             apply_impl<DataStateTrackedTable::const_accessor>(table, i);
63551c0b2f7Stbbdev         }
63651c0b2f7Stbbdev     }
63751c0b2f7Stbbdev };
63851c0b2f7Stbbdev 
63951c0b2f7Stbbdev template<typename Op, typename test_table_type>
64051c0b2f7Stbbdev class TableOperation {
64151c0b2f7Stbbdev     test_table_type& my_table;
64251c0b2f7Stbbdev public:
operator ()(const oneapi::tbb::blocked_range<int> & range) const64349e08aacStbbdev     void operator()( const oneapi::tbb::blocked_range<int>& range ) const {
64451c0b2f7Stbbdev         for( int i=range.begin(); i!=range.end(); ++i )
64551c0b2f7Stbbdev             Op::apply(my_table,i);
64651c0b2f7Stbbdev     }
TableOperation(test_table_type & table)64751c0b2f7Stbbdev     TableOperation( test_table_type& table ) : my_table(table) {}
64851c0b2f7Stbbdev };
64951c0b2f7Stbbdev 
UseKey(size_t i)65051c0b2f7Stbbdev bool UseKey( size_t i ) {
65151c0b2f7Stbbdev     return (i&3)!=3;
65251c0b2f7Stbbdev }
65351c0b2f7Stbbdev 
65451c0b2f7Stbbdev struct Insert {
applyInsert65551c0b2f7Stbbdev     static void apply( test_table_type& table, int i ) {
65651c0b2f7Stbbdev         if( UseKey(i) ) {
65751c0b2f7Stbbdev             if( i&4 ) {
65851c0b2f7Stbbdev                 test_table_type::accessor a;
65951c0b2f7Stbbdev                 table.insert( a, MyKey::make(i) );
66051c0b2f7Stbbdev                 if( i&1 )
66151c0b2f7Stbbdev                     (*a).second.set_value(i*i);
66251c0b2f7Stbbdev                 else
66351c0b2f7Stbbdev                     a->second.set_value(i*i);
66451c0b2f7Stbbdev             } else
66551c0b2f7Stbbdev                 if( i&1 ) {
66651c0b2f7Stbbdev                     test_table_type::accessor a;
66751c0b2f7Stbbdev                     table.insert( a, std::make_pair(MyKey::make(i), MyData(i*i)) );
668ec39c546SAlex                     CHECK_FAST((*a).second.value_of()==i*i);
66951c0b2f7Stbbdev                 } else {
67051c0b2f7Stbbdev                     test_table_type::const_accessor ca;
67151c0b2f7Stbbdev                     table.insert( ca, std::make_pair(MyKey::make(i), MyData(i*i)) );
672ec39c546SAlex                     CHECK_FAST(ca->second.value_of()==i*i);
67351c0b2f7Stbbdev                 }
67451c0b2f7Stbbdev         }
67551c0b2f7Stbbdev     }
67651c0b2f7Stbbdev };
67751c0b2f7Stbbdev 
67851c0b2f7Stbbdev struct Find {
applyFind67951c0b2f7Stbbdev     static void apply( test_table_type& table, int i ) {
68051c0b2f7Stbbdev         test_table_type::accessor a;
68151c0b2f7Stbbdev         const test_table_type::accessor& ca = a;
68251c0b2f7Stbbdev         bool b = table.find( a, MyKey::make(i) );
683ec39c546SAlex         CHECK_FAST(b==!a.empty());
68451c0b2f7Stbbdev         if( b ) {
68551c0b2f7Stbbdev             if( !UseKey(i) )
68651c0b2f7Stbbdev                 REPORT("Line %d: unexpected key %d present\n",__LINE__,i);
687ec39c546SAlex             CHECK_FAST(ca->second.value_of()==i*i);
688ec39c546SAlex             CHECK_FAST((*ca).second.value_of()==i*i);
68951c0b2f7Stbbdev             if( i&1 )
69051c0b2f7Stbbdev                 ca->second.set_value( ~ca->second.value_of() );
69151c0b2f7Stbbdev             else
69251c0b2f7Stbbdev                 (*ca).second.set_value( ~ca->second.value_of() );
69351c0b2f7Stbbdev         } else {
69451c0b2f7Stbbdev             if( UseKey(i) )
69551c0b2f7Stbbdev                 REPORT("Line %d: key %d missing\n",__LINE__,i);
69651c0b2f7Stbbdev         }
69751c0b2f7Stbbdev     }
69851c0b2f7Stbbdev };
69951c0b2f7Stbbdev 
70051c0b2f7Stbbdev struct FindConst {
applyFindConst70151c0b2f7Stbbdev     static void apply( const test_table_type& table, int i ) {
70251c0b2f7Stbbdev         test_table_type::const_accessor a;
70351c0b2f7Stbbdev         const test_table_type::const_accessor& ca = a;
70451c0b2f7Stbbdev         bool b = table.find( a, MyKey::make(i) );
705ec39c546SAlex         CHECK_FAST(b==(table.count(MyKey::make(i))>0));
706ec39c546SAlex         CHECK_FAST(b==!a.empty());
707ec39c546SAlex         CHECK_FAST(b==UseKey(i));
70851c0b2f7Stbbdev         if( b ) {
709ec39c546SAlex             CHECK_FAST(ca->second.value_of()==~(i*i));
710ec39c546SAlex             CHECK_FAST((*ca).second.value_of()==~(i*i));
71151c0b2f7Stbbdev         }
71251c0b2f7Stbbdev     }
71351c0b2f7Stbbdev };
71451c0b2f7Stbbdev 
71551c0b2f7Stbbdev struct InsertInitList {
applyInsertInitList71651c0b2f7Stbbdev     static void apply( test_table_type& table, int i ) {
71751c0b2f7Stbbdev         if ( UseKey( i ) ) {
71851c0b2f7Stbbdev             // TODO: investigate why the following sequence causes an additional allocation sometimes:
71951c0b2f7Stbbdev             // table.insert( test_table_type::value_type( MyKey::make( i ), i*i ) );
72051c0b2f7Stbbdev             // table.insert( test_table_type::value_type( MyKey::make( i ), i*i+1 ) );
72151c0b2f7Stbbdev             std::initializer_list<test_table_type::value_type> il = {
72251c0b2f7Stbbdev                 test_table_type::value_type( MyKey::make( i ), i*i )
72351c0b2f7Stbbdev                 /*, test_table_type::value_type( MyKey::make( i ), i*i+1 ) */
72451c0b2f7Stbbdev                                                                     };
72551c0b2f7Stbbdev             table.insert( il );
72651c0b2f7Stbbdev         }
72751c0b2f7Stbbdev     }
72851c0b2f7Stbbdev };
72951c0b2f7Stbbdev 
73051c0b2f7Stbbdev template<typename Op, typename TableType>
DoConcurrentOperations(TableType & table,int n,const char * what,std::size_t nthread)73151c0b2f7Stbbdev void DoConcurrentOperations( TableType& table, int n, const char* what, std::size_t nthread ) {
73251c0b2f7Stbbdev     INFO("testing " << what << " with " << nthread << " threads");
73349e08aacStbbdev     oneapi::tbb::parallel_for(oneapi::tbb::blocked_range<int>(0, n ,100), TableOperation<Op, TableType>(table));
73451c0b2f7Stbbdev }
73551c0b2f7Stbbdev 
73651c0b2f7Stbbdev //! Test traversing the table with an iterator.
TraverseTable(test_table_type & table,size_t n,size_t expected_size)73751c0b2f7Stbbdev void TraverseTable( test_table_type& table, size_t n, size_t expected_size ) {
73851c0b2f7Stbbdev     INFO("testing traversal\n");
73951c0b2f7Stbbdev     size_t actual_size = table.size();
74051c0b2f7Stbbdev     CHECK(actual_size==expected_size);
74151c0b2f7Stbbdev     size_t count = 0;
74251c0b2f7Stbbdev     bool* array = new bool[n];
74351c0b2f7Stbbdev     memset( array, 0, n*sizeof(bool) );
74451c0b2f7Stbbdev     const test_table_type& const_table = table;
74551c0b2f7Stbbdev     test_table_type::const_iterator ci = const_table.begin();
74651c0b2f7Stbbdev     for( test_table_type::iterator i = table.begin(); i!=table.end(); ++i ) {
74751c0b2f7Stbbdev         // Check iterator
74851c0b2f7Stbbdev         int k = i->first.value_of();
749ec39c546SAlex         CHECK_FAST(UseKey(k));
750ec39c546SAlex         CHECK_FAST((*i).first.value_of()==k);
751ec39c546SAlex         CHECK_FAST_MESSAGE((0<=k && size_t(k)<n), "out of bounds key" );
752ec39c546SAlex         CHECK_FAST_MESSAGE( !array[k], "duplicate key" );
75351c0b2f7Stbbdev         array[k] = true;
75451c0b2f7Stbbdev         ++count;
75551c0b2f7Stbbdev 
75651c0b2f7Stbbdev         // Check lower/upper bounds
75751c0b2f7Stbbdev         std::pair<test_table_type::iterator, test_table_type::iterator> er = table.equal_range(i->first);
75851c0b2f7Stbbdev         std::pair<test_table_type::const_iterator, test_table_type::const_iterator> cer = const_table.equal_range(i->first);
759ec39c546SAlex         CHECK_FAST((cer.first == er.first && cer.second == er.second));
760ec39c546SAlex         CHECK_FAST(cer.first == i);
761ec39c546SAlex         CHECK_FAST(std::distance(cer.first, cer.second) == 1);
76251c0b2f7Stbbdev 
76351c0b2f7Stbbdev         // Check const_iterator
76451c0b2f7Stbbdev         test_table_type::const_iterator cic = ci++;
765ec39c546SAlex         CHECK_FAST(cic->first.value_of()==k);
766ec39c546SAlex         CHECK_FAST((*cic).first.value_of()==k);
76751c0b2f7Stbbdev     }
76851c0b2f7Stbbdev     CHECK(ci==const_table.end());
76951c0b2f7Stbbdev     delete[] array;
77051c0b2f7Stbbdev     if (count != expected_size) {
77151c0b2f7Stbbdev         INFO("Line " << __LINE__ << ": count=" << count << " but should be " << expected_size);
77251c0b2f7Stbbdev     }
77351c0b2f7Stbbdev }
77451c0b2f7Stbbdev 
77551c0b2f7Stbbdev std::atomic<int> EraseCount;
77651c0b2f7Stbbdev 
77751c0b2f7Stbbdev struct Erase {
applyErase77851c0b2f7Stbbdev     static void apply( test_table_type& table, int i ) {
77951c0b2f7Stbbdev         bool b;
78051c0b2f7Stbbdev         if(i&4) {
78151c0b2f7Stbbdev             if(i&8) {
78251c0b2f7Stbbdev                 test_table_type::const_accessor a;
78351c0b2f7Stbbdev                 b = table.find( a, MyKey::make(i) ) && table.erase( a );
78451c0b2f7Stbbdev             } else {
78551c0b2f7Stbbdev                 test_table_type::accessor a;
78651c0b2f7Stbbdev                 b = table.find( a, MyKey::make(i) ) && table.erase( a );
78751c0b2f7Stbbdev             }
78851c0b2f7Stbbdev         } else
78951c0b2f7Stbbdev             b = table.erase( MyKey::make(i) );
79051c0b2f7Stbbdev         if( b ) ++EraseCount;
791ec39c546SAlex         CHECK_FAST(table.count(MyKey::make(i)) == 0);
79251c0b2f7Stbbdev     }
79351c0b2f7Stbbdev };
79451c0b2f7Stbbdev 
79549e08aacStbbdev using YourTable = oneapi::tbb::concurrent_hash_map<MyKey, MyData, YourHashCompare>;
79651c0b2f7Stbbdev static const int IE_SIZE = 2;
79751c0b2f7Stbbdev std::atomic<YourTable::size_type> InsertEraseCount[IE_SIZE];
79851c0b2f7Stbbdev 
79951c0b2f7Stbbdev struct InsertErase  {
applyInsertErase80051c0b2f7Stbbdev     static void apply( YourTable& table, int i ) {
80151c0b2f7Stbbdev         if ( i%3 ) {
80251c0b2f7Stbbdev             int key = i%IE_SIZE;
80351c0b2f7Stbbdev             if ( table.insert( std::make_pair(MyKey::make(key), MyData2()) ) )
80451c0b2f7Stbbdev                 ++InsertEraseCount[key];
80551c0b2f7Stbbdev         } else {
80651c0b2f7Stbbdev             int key = i%IE_SIZE;
80751c0b2f7Stbbdev             if( i&1 ) {
80851c0b2f7Stbbdev                 YourTable::accessor res;
80951c0b2f7Stbbdev                 if(table.find( res, MyKey::make(key) ) && table.erase( res ) )
81051c0b2f7Stbbdev                     --InsertEraseCount[key];
81151c0b2f7Stbbdev             } else {
81251c0b2f7Stbbdev                 YourTable::const_accessor res;
81351c0b2f7Stbbdev                 if(table.find( res, MyKey::make(key) ) && table.erase( res ) )
81451c0b2f7Stbbdev                     --InsertEraseCount[key];
81551c0b2f7Stbbdev             }
81651c0b2f7Stbbdev         }
81751c0b2f7Stbbdev     }
81851c0b2f7Stbbdev };
81951c0b2f7Stbbdev 
82051c0b2f7Stbbdev struct InnerInsert {
applyInnerInsert82151c0b2f7Stbbdev     static void apply( YourTable& table, int i ) {
82251c0b2f7Stbbdev         YourTable::accessor a1, a2;
823b15aabb3Stbbdev         if(i&1) utils::yield();
82451c0b2f7Stbbdev         table.insert( a1, MyKey::make(1) );
825b15aabb3Stbbdev         utils::yield();
82651c0b2f7Stbbdev         table.insert( a2, MyKey::make(1 + (1<<30)) ); // the same chain
82751c0b2f7Stbbdev         table.erase( a2 ); // if erase by key it would lead to deadlock for single thread
82851c0b2f7Stbbdev     }
82951c0b2f7Stbbdev };
83051c0b2f7Stbbdev 
83151c0b2f7Stbbdev struct FakeExclusive {
83251c0b2f7Stbbdev     utils::SpinBarrier& barrier;
83351c0b2f7Stbbdev     YourTable& table;
FakeExclusiveFakeExclusive83451c0b2f7Stbbdev     FakeExclusive(utils::SpinBarrier& b, YourTable&t) : barrier(b), table(t) {}
operator ()FakeExclusive83551c0b2f7Stbbdev     void operator()( std::size_t i ) const {
83651c0b2f7Stbbdev         if(i) {
83751c0b2f7Stbbdev             YourTable::const_accessor real_ca;
83851c0b2f7Stbbdev             // const accessor on non-const table acquired as reader (shared)
83951c0b2f7Stbbdev             CHECK(table.find(real_ca,MyKey::make(1)));
84051c0b2f7Stbbdev             barrier.wait(); // item can be erased
84151c0b2f7Stbbdev             std::this_thread::sleep_for(std::chrono::milliseconds(10)); // let it enter the erase
84251c0b2f7Stbbdev             real_ca->second.value_of(); // check the state while holding accessor
84351c0b2f7Stbbdev         } else {
84451c0b2f7Stbbdev             YourTable::accessor fake_ca;
84551c0b2f7Stbbdev             const YourTable &const_table = table;
84651c0b2f7Stbbdev             // non-const accessor on const table acquired as reader (shared)
84751c0b2f7Stbbdev             CHECK(const_table.find(fake_ca,MyKey::make(1)));
84851c0b2f7Stbbdev             barrier.wait(); // readers acquired
84951c0b2f7Stbbdev             // can mistakenly remove the item while other readers still refers to it
85051c0b2f7Stbbdev             table.erase( fake_ca );
85151c0b2f7Stbbdev         }
85251c0b2f7Stbbdev     }
85351c0b2f7Stbbdev };
85451c0b2f7Stbbdev 
85551c0b2f7Stbbdev using AtomicByte = std::atomic<unsigned char>;
85651c0b2f7Stbbdev 
85751c0b2f7Stbbdev template<typename RangeType>
85851c0b2f7Stbbdev struct ParallelTraverseBody {
85951c0b2f7Stbbdev     const size_t n;
86051c0b2f7Stbbdev     AtomicByte* const array;
ParallelTraverseBodyParallelTraverseBody86151c0b2f7Stbbdev     ParallelTraverseBody( AtomicByte array_[], size_t n_ ) :
86251c0b2f7Stbbdev         n(n_),
86351c0b2f7Stbbdev         array(array_)
86451c0b2f7Stbbdev     {}
operator ()ParallelTraverseBody86551c0b2f7Stbbdev     void operator()( const RangeType& range ) const {
86651c0b2f7Stbbdev         for( typename RangeType::iterator i = range.begin(); i!=range.end(); ++i ) {
86751c0b2f7Stbbdev             int k = i->first.value_of();
868ec39c546SAlex             CHECK_FAST((0<=k && size_t(k)<n));
86951c0b2f7Stbbdev             ++array[k];
87051c0b2f7Stbbdev         }
87151c0b2f7Stbbdev     }
87251c0b2f7Stbbdev };
87351c0b2f7Stbbdev 
Check(AtomicByte array[],size_t n,size_t expected_size)87451c0b2f7Stbbdev void Check( AtomicByte array[], size_t n, size_t expected_size ) {
87551c0b2f7Stbbdev     if( expected_size )
87651c0b2f7Stbbdev         for( size_t k=0; k<n; ++k ) {
87751c0b2f7Stbbdev             if( array[k] != int(UseKey(k)) ) {
87851c0b2f7Stbbdev                 REPORT("array[%d]=%d != %d=UseKey(%d)\n",
87951c0b2f7Stbbdev                        int(k), int(array[k]), int(UseKey(k)), int(k));
88051c0b2f7Stbbdev                 CHECK(false);
88151c0b2f7Stbbdev             }
88251c0b2f7Stbbdev         }
88351c0b2f7Stbbdev }
88451c0b2f7Stbbdev 
88551c0b2f7Stbbdev //! Test traversing the table with a parallel range
ParallelTraverseTable(test_table_type & table,size_t n,size_t expected_size)88651c0b2f7Stbbdev void ParallelTraverseTable( test_table_type& table, size_t n, size_t expected_size ) {
88751c0b2f7Stbbdev     INFO("testing parallel traversal\n");
88851c0b2f7Stbbdev     CHECK(table.size()==expected_size);
88951c0b2f7Stbbdev     AtomicByte* array = new AtomicByte[n];
89051c0b2f7Stbbdev 
89151c0b2f7Stbbdev     memset( static_cast<void*>(array), 0, n*sizeof(AtomicByte) );
89251c0b2f7Stbbdev     test_table_type::range_type r = table.range(10);
89349e08aacStbbdev     oneapi::tbb::parallel_for( r, ParallelTraverseBody<test_table_type::range_type>( array, n ));
89451c0b2f7Stbbdev     Check( array, n, expected_size );
89551c0b2f7Stbbdev 
89651c0b2f7Stbbdev     const test_table_type& const_table = table;
89751c0b2f7Stbbdev     memset( static_cast<void*>(array), 0, n*sizeof(AtomicByte) );
89851c0b2f7Stbbdev     test_table_type::const_range_type cr = const_table.range(10);
89949e08aacStbbdev     oneapi::tbb::parallel_for( cr, ParallelTraverseBody<test_table_type::const_range_type>( array, n ));
90051c0b2f7Stbbdev     Check( array, n, expected_size );
90151c0b2f7Stbbdev 
90251c0b2f7Stbbdev     delete[] array;
90351c0b2f7Stbbdev }
90451c0b2f7Stbbdev 
TestInsertFindErase(std::size_t nthread)90551c0b2f7Stbbdev void TestInsertFindErase( std::size_t nthread ) {
90651c0b2f7Stbbdev     int n=250000;
90751c0b2f7Stbbdev 
90851c0b2f7Stbbdev     // compute m = number of unique keys
90951c0b2f7Stbbdev     int m = 0;
91051c0b2f7Stbbdev     for( int i=0; i<n; ++i )
91151c0b2f7Stbbdev         m += UseKey(i);
91251c0b2f7Stbbdev     {
91351c0b2f7Stbbdev         test_allocator_type alloc;
91451c0b2f7Stbbdev         test_allocator_type::init_counters();
91551c0b2f7Stbbdev         CHECK(MyDataCount==0);
91651c0b2f7Stbbdev         test_table_type table(alloc);
91751c0b2f7Stbbdev         TraverseTable(table,n,0);
91851c0b2f7Stbbdev         ParallelTraverseTable(table,n,0);
91951c0b2f7Stbbdev 
92051c0b2f7Stbbdev         for ( int i = 0; i < 2; ++i ) {
92151c0b2f7Stbbdev             if ( i==0 )
92251c0b2f7Stbbdev                 DoConcurrentOperations<InsertInitList, test_table_type>( table, n, "insert(std::initializer_list)", nthread );
92351c0b2f7Stbbdev             else
92451c0b2f7Stbbdev                 DoConcurrentOperations<Insert, test_table_type>( table, n, "insert", nthread );
92551c0b2f7Stbbdev             CHECK(MyDataCount == m);
92651c0b2f7Stbbdev             TraverseTable( table, n, m );
92751c0b2f7Stbbdev             ParallelTraverseTable( table, n, m );
92851c0b2f7Stbbdev 
92951c0b2f7Stbbdev             DoConcurrentOperations<Find, test_table_type>( table, n, "find", nthread );
93051c0b2f7Stbbdev             CHECK(MyDataCount == m);
93151c0b2f7Stbbdev 
93251c0b2f7Stbbdev             DoConcurrentOperations<FindConst, test_table_type>( table, n, "find(const)", nthread );
93351c0b2f7Stbbdev             CHECK(MyDataCount == m);
93451c0b2f7Stbbdev 
93551c0b2f7Stbbdev             EraseCount = 0;
93651c0b2f7Stbbdev             DoConcurrentOperations<Erase, test_table_type>( table, n, "erase", nthread );
93751c0b2f7Stbbdev             CHECK(EraseCount == m);
93851c0b2f7Stbbdev             CHECK(MyDataCount == 0);
93951c0b2f7Stbbdev             TraverseTable( table, n, 0 );
94051c0b2f7Stbbdev 
94151c0b2f7Stbbdev             table.clear();
94251c0b2f7Stbbdev         }
94351c0b2f7Stbbdev 
94451c0b2f7Stbbdev         if( nthread > 1 ) {
94551c0b2f7Stbbdev             YourTable ie_table;
94651c0b2f7Stbbdev             for( int i=0; i<IE_SIZE; ++i )
94751c0b2f7Stbbdev                 InsertEraseCount[i] = 0;
94851c0b2f7Stbbdev             DoConcurrentOperations<InsertErase,YourTable>(ie_table,n/2,"insert_erase",nthread);
94951c0b2f7Stbbdev             for( int i=0; i<IE_SIZE; ++i )
95051c0b2f7Stbbdev                 CHECK(InsertEraseCount[i]==ie_table.count(MyKey::make(i)));
95151c0b2f7Stbbdev 
95251c0b2f7Stbbdev             DoConcurrentOperations<InnerInsert, YourTable>(ie_table,2000,"inner insert",nthread);
95351c0b2f7Stbbdev             utils::SpinBarrier barrier(nthread);
95451c0b2f7Stbbdev             INFO("testing erase on fake exclusive accessor\n");
95551c0b2f7Stbbdev             utils::NativeParallelFor( nthread, FakeExclusive(barrier, ie_table));
95651c0b2f7Stbbdev         }
95751c0b2f7Stbbdev     }
95851c0b2f7Stbbdev     REQUIRE( test_allocator_type::items_constructed == test_allocator_type::items_destroyed );
95951c0b2f7Stbbdev     REQUIRE( test_allocator_type::items_allocated == test_allocator_type::items_freed );
96051c0b2f7Stbbdev     REQUIRE( test_allocator_type::allocations == test_allocator_type::frees );
96151c0b2f7Stbbdev }
96251c0b2f7Stbbdev 
96351c0b2f7Stbbdev std::atomic<int> Counter;
96451c0b2f7Stbbdev 
96551c0b2f7Stbbdev class AddToTable {
96651c0b2f7Stbbdev     test_table_type& my_table;
96751c0b2f7Stbbdev     const std::size_t my_nthread;
96851c0b2f7Stbbdev     const int my_m;
96951c0b2f7Stbbdev public:
AddToTable(test_table_type & table,std::size_t nthread,int m)97051c0b2f7Stbbdev     AddToTable( test_table_type& table, std::size_t nthread, int m ) : my_table(table), my_nthread(nthread), my_m(m) {}
operator ()(std::size_t) const97151c0b2f7Stbbdev     void operator()( std::size_t ) const {
97251c0b2f7Stbbdev         for( int i=0; i<my_m; ++i ) {
97351c0b2f7Stbbdev             // Busy wait to synchronize threads
97451c0b2f7Stbbdev             int j = 0;
97551c0b2f7Stbbdev             while( Counter<i ) {
97651c0b2f7Stbbdev                 if( ++j==1000000 ) {
97751c0b2f7Stbbdev                     // If Counter<i after a million iterations, then we almost surely have
97851c0b2f7Stbbdev                     // more logical threads than physical threads, and should yield in
97951c0b2f7Stbbdev                     // order to let suspended logical threads make progress.
98051c0b2f7Stbbdev                     j = 0;
981b15aabb3Stbbdev                     utils::yield();
98251c0b2f7Stbbdev                 }
98351c0b2f7Stbbdev             }
98451c0b2f7Stbbdev             // Now all threads attempt to simultaneously insert a key.
98551c0b2f7Stbbdev             int k;
98651c0b2f7Stbbdev             {
98751c0b2f7Stbbdev                 test_table_type::accessor a;
98851c0b2f7Stbbdev                 MyKey key = MyKey::make(i);
98951c0b2f7Stbbdev                 if( my_table.insert( a, key ) )
99051c0b2f7Stbbdev                     a->second.set_value( 1 );
99151c0b2f7Stbbdev                 else
99251c0b2f7Stbbdev                     a->second.set_value( a->second.value_of()+1 );
99351c0b2f7Stbbdev                 k = a->second.value_of();
99451c0b2f7Stbbdev             }
99551c0b2f7Stbbdev             if( std::size_t(k) == my_nthread )
99651c0b2f7Stbbdev                 Counter=i+1;
99751c0b2f7Stbbdev         }
99851c0b2f7Stbbdev     }
99951c0b2f7Stbbdev };
100051c0b2f7Stbbdev 
100151c0b2f7Stbbdev class RemoveFromTable {
100251c0b2f7Stbbdev     test_table_type& my_table;
100351c0b2f7Stbbdev     const int my_m;
100451c0b2f7Stbbdev public:
RemoveFromTable(test_table_type & table,int m)100551c0b2f7Stbbdev     RemoveFromTable( test_table_type& table, int m ) : my_table(table), my_m(m) {}
operator ()(std::size_t) const100651c0b2f7Stbbdev     void operator()(std::size_t) const {
100751c0b2f7Stbbdev         for( int i=0; i<my_m; ++i ) {
100851c0b2f7Stbbdev             bool b;
100951c0b2f7Stbbdev             if(i&4) {
101051c0b2f7Stbbdev                 if(i&8) {
101151c0b2f7Stbbdev                     test_table_type::const_accessor a;
101251c0b2f7Stbbdev                     b = my_table.find( a, MyKey::make(i) ) && my_table.erase( a );
101351c0b2f7Stbbdev                 } else {
101451c0b2f7Stbbdev                     test_table_type::accessor a;
101551c0b2f7Stbbdev                     b = my_table.find( a, MyKey::make(i) ) && my_table.erase( a );
101651c0b2f7Stbbdev                 }
101751c0b2f7Stbbdev             } else
101851c0b2f7Stbbdev                 b = my_table.erase( MyKey::make(i) );
101951c0b2f7Stbbdev             if( b ) ++EraseCount;
102051c0b2f7Stbbdev         }
102151c0b2f7Stbbdev     }
102251c0b2f7Stbbdev };
102351c0b2f7Stbbdev 
TestConcurrency(std::size_t nthread)102451c0b2f7Stbbdev void TestConcurrency( std::size_t nthread ) {
102551c0b2f7Stbbdev     INFO("testing multiple insertions/deletions of same key with " << nthread << " threads");
102651c0b2f7Stbbdev     test_allocator_type::init_counters();
102751c0b2f7Stbbdev     {
102851c0b2f7Stbbdev         CHECK( MyDataCount==0);
102951c0b2f7Stbbdev         test_table_type table;
103051c0b2f7Stbbdev         const int m = 1000;
103151c0b2f7Stbbdev         Counter = 0;
103249e08aacStbbdev         oneapi::tbb::tick_count t0 = oneapi::tbb::tick_count::now();
103351c0b2f7Stbbdev         utils::NativeParallelFor( nthread, AddToTable(table,nthread,m) );
103451c0b2f7Stbbdev         REQUIRE_MESSAGE( MyDataCount==m, "memory leak detected" );
103551c0b2f7Stbbdev 
103651c0b2f7Stbbdev         EraseCount = 0;
103749e08aacStbbdev         t0 = oneapi::tbb::tick_count::now();
103851c0b2f7Stbbdev         utils::NativeParallelFor( nthread, RemoveFromTable(table,m) );
103951c0b2f7Stbbdev         REQUIRE_MESSAGE(MyDataCount==0, "memory leak detected");
104051c0b2f7Stbbdev         REQUIRE_MESSAGE(EraseCount==m, "return value of erase() is broken");
104151c0b2f7Stbbdev 
104251c0b2f7Stbbdev     }
104351c0b2f7Stbbdev     REQUIRE( test_allocator_type::items_constructed == test_allocator_type::items_destroyed );
104451c0b2f7Stbbdev     REQUIRE( test_allocator_type::items_allocated == test_allocator_type::items_freed );
104551c0b2f7Stbbdev     REQUIRE( test_allocator_type::allocations == test_allocator_type::frees );
104651c0b2f7Stbbdev     REQUIRE_MESSAGE(MyDataCount==0, "memory leak detected");
104751c0b2f7Stbbdev }
104851c0b2f7Stbbdev 
104951c0b2f7Stbbdev template<typename Key>
105049e08aacStbbdev struct non_default_constructible_hash_compare : oneapi::tbb::detail::d1::tbb_hash_compare<Key> {
non_default_constructible_hash_comparenon_default_constructible_hash_compare105151c0b2f7Stbbdev     non_default_constructible_hash_compare() {
105251c0b2f7Stbbdev         REQUIRE_MESSAGE(false, "Hash compare object must not default construct during the construction of hash_map with compare argument");
105351c0b2f7Stbbdev     }
105451c0b2f7Stbbdev 
non_default_constructible_hash_comparenon_default_constructible_hash_compare105551c0b2f7Stbbdev     non_default_constructible_hash_compare(int) {}
105651c0b2f7Stbbdev };
105751c0b2f7Stbbdev 
TestHashCompareConstructors()105851c0b2f7Stbbdev void TestHashCompareConstructors() {
105951c0b2f7Stbbdev     using key_type = int;
106049e08aacStbbdev     using map_type = oneapi::tbb::concurrent_hash_map<key_type, key_type, non_default_constructible_hash_compare<key_type>>;
106151c0b2f7Stbbdev 
106251c0b2f7Stbbdev     non_default_constructible_hash_compare<key_type> compare(0);
106351c0b2f7Stbbdev     map_type::allocator_type allocator;
106451c0b2f7Stbbdev 
106551c0b2f7Stbbdev     map_type map1(compare);
106651c0b2f7Stbbdev     map_type map2(compare, allocator);
106751c0b2f7Stbbdev 
106851c0b2f7Stbbdev     map_type map3(1, compare);
106951c0b2f7Stbbdev     map_type map4(1, compare, allocator);
107051c0b2f7Stbbdev 
107151c0b2f7Stbbdev     std::vector<map_type::value_type> reference_vector;
107251c0b2f7Stbbdev     map_type map5(reference_vector.begin(), reference_vector.end(), compare);
107351c0b2f7Stbbdev     map_type map6(reference_vector.begin(), reference_vector.end(), compare, allocator);
107451c0b2f7Stbbdev 
107551c0b2f7Stbbdev     map_type map7({}, compare);
107651c0b2f7Stbbdev     map_type map8({}, compare, allocator);
107751c0b2f7Stbbdev }
107851c0b2f7Stbbdev 
107951c0b2f7Stbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
108051c0b2f7Stbbdev template <typename T>
108149e08aacStbbdev struct debug_hash_compare : public oneapi::tbb::detail::d1::tbb_hash_compare<T> {};
108251c0b2f7Stbbdev 
108351c0b2f7Stbbdev template <template <typename...> typename TMap>
TestDeductionGuides()108451c0b2f7Stbbdev void TestDeductionGuides() {
108551c0b2f7Stbbdev     using Key = int;
108651c0b2f7Stbbdev     using Value = std::string;
108751c0b2f7Stbbdev 
108851c0b2f7Stbbdev     using ComplexType = std::pair<Key, Value>;
108951c0b2f7Stbbdev     using ComplexTypeConst = std::pair<const Key, Value>;
109051c0b2f7Stbbdev 
109149e08aacStbbdev     using DefaultCompare = oneapi::tbb::detail::d1::tbb_hash_compare<Key>;
109251c0b2f7Stbbdev     using Compare = debug_hash_compare<Key>;
109349e08aacStbbdev     using DefaultAllocator = oneapi::tbb::tbb_allocator<ComplexTypeConst>;
109451c0b2f7Stbbdev     using Allocator = std::allocator<ComplexTypeConst>;
109551c0b2f7Stbbdev 
109651c0b2f7Stbbdev     std::vector<ComplexType> v;
109751c0b2f7Stbbdev     auto l = { ComplexTypeConst(1, "one"), ComplexTypeConst(2, "two") };
109851c0b2f7Stbbdev     Compare compare;
109951c0b2f7Stbbdev     Allocator allocator;
110051c0b2f7Stbbdev 
110151c0b2f7Stbbdev     // check TMap(InputIterator, InputIterator)
110251c0b2f7Stbbdev     TMap m1(v.begin(), v.end());
110351c0b2f7Stbbdev     static_assert(std::is_same<decltype(m1), TMap<Key, Value, DefaultCompare, DefaultAllocator>>::value);
110451c0b2f7Stbbdev 
110551c0b2f7Stbbdev     // check TMap(InputIterator, InputIterator, HashCompare)
110651c0b2f7Stbbdev     TMap m2(v.begin(), v.end(), compare);
110751c0b2f7Stbbdev     static_assert(std::is_same<decltype(m2), TMap<Key, Value, Compare>>::value);
110851c0b2f7Stbbdev 
110951c0b2f7Stbbdev     // check TMap(InputIterator, InputIterator, HashCompare, Allocator)
111051c0b2f7Stbbdev     TMap m3(v.begin(), v.end(), compare, allocator);
111151c0b2f7Stbbdev     static_assert(std::is_same<decltype(m3), TMap<Key, Value, Compare, Allocator>>::value);
111251c0b2f7Stbbdev 
111351c0b2f7Stbbdev     // check TMap(InputIterator, InputIterator, Allocator)
111451c0b2f7Stbbdev     TMap m4(v.begin(), v.end(), allocator);
111551c0b2f7Stbbdev     static_assert(std::is_same<decltype(m4), TMap<Key, Value, DefaultCompare, Allocator>>::value);
111651c0b2f7Stbbdev 
111751c0b2f7Stbbdev     // check TMap(std::initializer_list)
111851c0b2f7Stbbdev     TMap m5(l);
111951c0b2f7Stbbdev     static_assert(std::is_same<decltype(m5), TMap<Key, Value, DefaultCompare, DefaultAllocator>>::value);
112051c0b2f7Stbbdev 
112151c0b2f7Stbbdev     // check TMap(std::initializer_list, HashCompare)
112251c0b2f7Stbbdev     TMap m6(l, compare);
112351c0b2f7Stbbdev     static_assert(std::is_same<decltype(m6), TMap<Key, Value, Compare, DefaultAllocator>>::value);
112451c0b2f7Stbbdev 
112551c0b2f7Stbbdev     // check TMap(std::initializer_list, HashCompare, Allocator)
112651c0b2f7Stbbdev     TMap m7(l, compare, allocator);
112751c0b2f7Stbbdev     static_assert(std::is_same<decltype(m7), TMap<Key, Value, Compare, Allocator>>::value);
112851c0b2f7Stbbdev 
112951c0b2f7Stbbdev     // check TMap(std::initializer_list, Allocator)
113051c0b2f7Stbbdev     TMap m8(l, allocator);
113151c0b2f7Stbbdev     static_assert(std::is_same<decltype(m8), TMap<Key, Value, DefaultCompare, Allocator>>::value);
113251c0b2f7Stbbdev 
113351c0b2f7Stbbdev     // check TMap(TMap &)
113451c0b2f7Stbbdev     TMap m9(m1);
113551c0b2f7Stbbdev     static_assert(std::is_same<decltype(m9), decltype(m1)>::value);
113651c0b2f7Stbbdev 
113751c0b2f7Stbbdev     // check TMap(TMap &, Allocator)
113851c0b2f7Stbbdev     TMap m10(m4, allocator);
113951c0b2f7Stbbdev     static_assert(std::is_same<decltype(m10), decltype(m4)>::value);
114051c0b2f7Stbbdev 
114151c0b2f7Stbbdev     // check TMap(TMap &&)
114251c0b2f7Stbbdev     TMap m11(std::move(m1));
114351c0b2f7Stbbdev     static_assert(std::is_same<decltype(m11), decltype(m1)>::value);
114451c0b2f7Stbbdev 
114551c0b2f7Stbbdev     // check TMap(TMap &&, Allocator)
114651c0b2f7Stbbdev     TMap m12(std::move(m4), allocator);
114751c0b2f7Stbbdev     static_assert(std::is_same<decltype(m12), decltype(m4)>::value);
114851c0b2f7Stbbdev }
114951c0b2f7Stbbdev #endif // __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
115051c0b2f7Stbbdev 
1151b15aabb3Stbbdev template <typename CHMapType>
test_comparisons_basic()1152b15aabb3Stbbdev void test_comparisons_basic() {
1153b15aabb3Stbbdev     using comparisons_testing::testEqualityComparisons;
1154b15aabb3Stbbdev     CHMapType c1, c2;
1155b15aabb3Stbbdev     testEqualityComparisons</*ExpectEqual = */true>(c1, c2);
1156b15aabb3Stbbdev 
1157b15aabb3Stbbdev     c1.emplace(1, 1);
1158b15aabb3Stbbdev     testEqualityComparisons</*ExpectEqual = */false>(c1, c2);
1159b15aabb3Stbbdev 
1160b15aabb3Stbbdev     c2.emplace(1, 1);
1161b15aabb3Stbbdev     testEqualityComparisons</*ExpectEqual = */true>(c1, c2);
1162b15aabb3Stbbdev }
1163b15aabb3Stbbdev 
1164b15aabb3Stbbdev template <typename TwoWayComparableContainerType>
test_two_way_comparable_chmap()1165b15aabb3Stbbdev void test_two_way_comparable_chmap() {
1166b15aabb3Stbbdev     TwoWayComparableContainerType c1, c2;
1167b15aabb3Stbbdev     c1.emplace(1, 1);
1168b15aabb3Stbbdev     c2.emplace(1, 1);
1169b15aabb3Stbbdev     comparisons_testing::TwoWayComparable::reset();
1170b15aabb3Stbbdev     REQUIRE_MESSAGE(c1 == c2, "Incorrect operator == result");
1171b15aabb3Stbbdev     comparisons_testing::check_equality_comparison();
1172b15aabb3Stbbdev     REQUIRE_MESSAGE(!(c1 != c2), "Incorrect operator != result");
1173b15aabb3Stbbdev     comparisons_testing::check_equality_comparison();
1174b15aabb3Stbbdev }
1175b15aabb3Stbbdev 
TestCHMapComparisons()1176b15aabb3Stbbdev void TestCHMapComparisons() {
1177b15aabb3Stbbdev     using integral_container = oneapi::tbb::concurrent_hash_map<int, int>;
1178b15aabb3Stbbdev     using two_way_comparable_container = oneapi::tbb::concurrent_hash_map<comparisons_testing::TwoWayComparable,
1179b15aabb3Stbbdev                                                                           comparisons_testing::TwoWayComparable>;
1180b15aabb3Stbbdev 
1181b15aabb3Stbbdev     test_comparisons_basic<integral_container>();
1182b15aabb3Stbbdev     test_comparisons_basic<two_way_comparable_container>();
1183b15aabb3Stbbdev     test_two_way_comparable_chmap<two_way_comparable_container>();
1184b15aabb3Stbbdev }
1185b15aabb3Stbbdev 
1186b15aabb3Stbbdev template <typename Iterator, typename CHMapType>
TestCHMapIteratorComparisonsBasic(CHMapType & chmap)1187b15aabb3Stbbdev void TestCHMapIteratorComparisonsBasic( CHMapType& chmap ) {
1188b15aabb3Stbbdev     REQUIRE_MESSAGE(!chmap.empty(), "Incorrect test setup");
1189b15aabb3Stbbdev     using namespace comparisons_testing;
1190b15aabb3Stbbdev     Iterator it1, it2;
1191b15aabb3Stbbdev     testEqualityComparisons</*ExpectEqual = */true>(it1, it2);
1192b15aabb3Stbbdev     it1 = chmap.begin();
1193b15aabb3Stbbdev     testEqualityComparisons</*ExpectEqual = */false>(it1, it2);
1194b15aabb3Stbbdev     it2 = chmap.begin();
1195b15aabb3Stbbdev     testEqualityComparisons</*ExpectEqual = */true>(it1, it2);
1196b15aabb3Stbbdev     it2 = chmap.end();
1197b15aabb3Stbbdev     testEqualityComparisons</*ExpectEqual = */false>(it1, it2);
1198b15aabb3Stbbdev }
1199b15aabb3Stbbdev 
TestCHMapIteratorComparisons()1200b15aabb3Stbbdev void TestCHMapIteratorComparisons() {
1201b15aabb3Stbbdev     using chmap_type = oneapi::tbb::concurrent_hash_map<int, int>;
1202b15aabb3Stbbdev     using value_type = typename chmap_type::value_type;
1203b15aabb3Stbbdev     chmap_type chmap = {value_type{1, 1}, value_type{2, 2}, value_type{3, 3}};
1204b15aabb3Stbbdev     TestCHMapIteratorComparisonsBasic<typename chmap_type::iterator>(chmap);
1205b15aabb3Stbbdev     const chmap_type& cchmap = chmap;
1206b15aabb3Stbbdev     TestCHMapIteratorComparisonsBasic<typename chmap_type::const_iterator>(cchmap);
1207b15aabb3Stbbdev }
1208b15aabb3Stbbdev 
1209fc8717e1Skboyarinov template <bool IsConstructible>
1210fc8717e1Skboyarinov class HeterogeneousKey {
1211fc8717e1Skboyarinov public:
1212fc8717e1Skboyarinov     static std::size_t heterogeneous_keys_count;
1213fc8717e1Skboyarinov 
integer_key() const1214fc8717e1Skboyarinov     int integer_key() const { return my_key; }
1215fc8717e1Skboyarinov 
1216fc8717e1Skboyarinov     template <bool I = IsConstructible, typename = typename std::enable_if<I>::type>
HeterogeneousKey(int key)1217fc8717e1Skboyarinov     HeterogeneousKey(int key) : my_key(key) { ++heterogeneous_keys_count; }
1218fc8717e1Skboyarinov 
1219fc8717e1Skboyarinov     HeterogeneousKey(const HeterogeneousKey&) = delete;
1220fc8717e1Skboyarinov     HeterogeneousKey& operator=(const HeterogeneousKey&) = delete;
1221fc8717e1Skboyarinov 
reset()1222fc8717e1Skboyarinov     static void reset() { heterogeneous_keys_count = 0; }
1223fc8717e1Skboyarinov 
1224fc8717e1Skboyarinov     struct construct_flag {};
1225fc8717e1Skboyarinov 
HeterogeneousKey(construct_flag,int key)1226fc8717e1Skboyarinov     HeterogeneousKey( construct_flag, int key ) : my_key(key) {}
1227fc8717e1Skboyarinov 
1228fc8717e1Skboyarinov private:
1229fc8717e1Skboyarinov     int my_key;
1230fc8717e1Skboyarinov }; // class HeterogeneousKey
1231fc8717e1Skboyarinov 
1232fc8717e1Skboyarinov template <bool IsConstructible>
1233fc8717e1Skboyarinov std::size_t HeterogeneousKey<IsConstructible>::heterogeneous_keys_count = 0;
1234fc8717e1Skboyarinov 
1235fc8717e1Skboyarinov struct HeterogeneousHashCompare {
1236fc8717e1Skboyarinov     using is_transparent = void;
1237fc8717e1Skboyarinov 
1238fc8717e1Skboyarinov     template <bool IsConstructible>
hashHeterogeneousHashCompare1239fc8717e1Skboyarinov     std::size_t hash( const HeterogeneousKey<IsConstructible>& key ) const {
1240fc8717e1Skboyarinov         return my_hash_object(key.integer_key());
1241fc8717e1Skboyarinov     }
1242fc8717e1Skboyarinov 
hashHeterogeneousHashCompare1243fc8717e1Skboyarinov     std::size_t hash( const int& key ) const {
1244fc8717e1Skboyarinov         return my_hash_object(key);
1245fc8717e1Skboyarinov     }
1246fc8717e1Skboyarinov 
equalHeterogeneousHashCompare1247fc8717e1Skboyarinov     bool equal( const int& key1, const int& key2 ) const {
1248fc8717e1Skboyarinov         return key1 == key2;
1249fc8717e1Skboyarinov     }
1250fc8717e1Skboyarinov 
1251fc8717e1Skboyarinov     template <bool IsConstructible>
equalHeterogeneousHashCompare1252fc8717e1Skboyarinov     bool equal( const int& key1, const HeterogeneousKey<IsConstructible>& key2 ) const {
1253fc8717e1Skboyarinov         return key1 == key2.integer_key();
1254fc8717e1Skboyarinov     }
1255fc8717e1Skboyarinov 
1256fc8717e1Skboyarinov     template <bool IsConstructible>
equalHeterogeneousHashCompare1257fc8717e1Skboyarinov     bool equal( const HeterogeneousKey<IsConstructible>& key1, const int& key2 ) const {
1258fc8717e1Skboyarinov         return key1.integer_key() == key2;
1259fc8717e1Skboyarinov     }
1260fc8717e1Skboyarinov 
1261fc8717e1Skboyarinov     template <bool IsConstructible>
equalHeterogeneousHashCompare1262fc8717e1Skboyarinov     bool equal( const HeterogeneousKey<IsConstructible>& key1, const HeterogeneousKey<IsConstructible>& key2 ) const {
1263fc8717e1Skboyarinov         return key1.integer_key() == key2.integer_key();
1264fc8717e1Skboyarinov     }
1265fc8717e1Skboyarinov 
1266fc8717e1Skboyarinov     std::hash<int> my_hash_object;
1267fc8717e1Skboyarinov }; // struct HeterogeneousHashCompare
1268fc8717e1Skboyarinov 
1269fc8717e1Skboyarinov class DefaultConstructibleValue {
1270fc8717e1Skboyarinov public:
DefaultConstructibleValue()1271fc8717e1Skboyarinov     DefaultConstructibleValue() : my_i(default_value) {};
1272fc8717e1Skboyarinov 
value() const1273fc8717e1Skboyarinov     int value() const { return my_i; }
1274fc8717e1Skboyarinov     static constexpr int default_value = -4242;
1275fc8717e1Skboyarinov private:
1276fc8717e1Skboyarinov     int my_i;
1277fc8717e1Skboyarinov }; // class DefaultConstructibleValue
1278fc8717e1Skboyarinov 
1279fc8717e1Skboyarinov constexpr int DefaultConstructibleValue::default_value;
1280fc8717e1Skboyarinov 
test_heterogeneous_find()1281fc8717e1Skboyarinov void test_heterogeneous_find() {
1282fc8717e1Skboyarinov     using key_type = HeterogeneousKey</*IsConstructible = */false>;
1283fc8717e1Skboyarinov     using chmap_type = oneapi::tbb::concurrent_hash_map<key_type, int, HeterogeneousHashCompare>;
1284fc8717e1Skboyarinov 
1285fc8717e1Skboyarinov     chmap_type chmap;
1286fc8717e1Skboyarinov     using const_accessor = typename chmap_type::const_accessor;
1287fc8717e1Skboyarinov     using accessor = typename chmap_type::accessor;
1288fc8717e1Skboyarinov     const_accessor cacc;
1289fc8717e1Skboyarinov     accessor acc;
1290fc8717e1Skboyarinov 
1291fc8717e1Skboyarinov     REQUIRE_MESSAGE(key_type::heterogeneous_keys_count == 0, "Incorrect test setup");
1292fc8717e1Skboyarinov 
1293fc8717e1Skboyarinov     key_type key(key_type::construct_flag{}, 1);
1294fc8717e1Skboyarinov     bool regular_result = chmap.find(cacc, key);
1295fc8717e1Skboyarinov     bool heterogeneous_result = chmap.find(cacc, int(1));
1296fc8717e1Skboyarinov 
1297fc8717e1Skboyarinov     REQUIRE(!regular_result);
1298fc8717e1Skboyarinov     REQUIRE_MESSAGE(regular_result == heterogeneous_result,
1299fc8717e1Skboyarinov                     "Incorrect heterogeneous find result with const_accessor (no element)");
1300fc8717e1Skboyarinov     REQUIRE_MESSAGE(key_type::heterogeneous_keys_count == 0, "Temporary key object was created during find call with const_accessor (no element)");
1301fc8717e1Skboyarinov 
1302fc8717e1Skboyarinov     regular_result = chmap.find(acc, key);
1303fc8717e1Skboyarinov     heterogeneous_result = chmap.find(acc, int(1));
1304fc8717e1Skboyarinov 
1305fc8717e1Skboyarinov     REQUIRE(!regular_result);
1306fc8717e1Skboyarinov     REQUIRE_MESSAGE(regular_result == heterogeneous_result,
1307fc8717e1Skboyarinov                     "Incorrect heterogeneous find result with accessor (no element)");
1308fc8717e1Skboyarinov     REQUIRE_MESSAGE(key_type::heterogeneous_keys_count == 0, "Temporary key object was created during find call with accessor (no element)");
1309fc8717e1Skboyarinov 
1310fc8717e1Skboyarinov     bool tmp_result = chmap.emplace(cacc, std::piecewise_construct,
1311fc8717e1Skboyarinov                                     std::forward_as_tuple(key_type::construct_flag{}, 1), std::forward_as_tuple(100));
1312fc8717e1Skboyarinov     REQUIRE(tmp_result);
1313fc8717e1Skboyarinov 
1314fc8717e1Skboyarinov     regular_result = chmap.find(cacc, key);
1315fc8717e1Skboyarinov     heterogeneous_result = chmap.find(cacc, int(1));
1316fc8717e1Skboyarinov 
1317fc8717e1Skboyarinov     REQUIRE(regular_result);
1318fc8717e1Skboyarinov     REQUIRE_MESSAGE(regular_result == heterogeneous_result, "Incorrect heterogeneous find result with const_accessor (element exists)");
1319fc8717e1Skboyarinov     REQUIRE_MESSAGE(cacc->first.integer_key() == 1, "Incorrect accessor returned");
1320fc8717e1Skboyarinov     REQUIRE_MESSAGE(key_type::heterogeneous_keys_count == 0, "Temporary key object was created during find call with const_accessor (element exists)");
1321fc8717e1Skboyarinov     cacc.release();
1322fc8717e1Skboyarinov 
1323fc8717e1Skboyarinov     regular_result = chmap.find(acc, key);
1324fc8717e1Skboyarinov     heterogeneous_result = chmap.find(acc, int(1));
1325fc8717e1Skboyarinov 
1326fc8717e1Skboyarinov     REQUIRE(regular_result);
1327fc8717e1Skboyarinov     REQUIRE_MESSAGE(regular_result == heterogeneous_result, "Incorrect heterogeneous find result with accessor (element exists)");
1328fc8717e1Skboyarinov     REQUIRE_MESSAGE(acc->first.integer_key() == 1, "Incorrect accessor returned");
1329fc8717e1Skboyarinov     REQUIRE_MESSAGE(key_type::heterogeneous_keys_count == 0, "Temporary key object was created during find call with accessor (element exists)");
1330fc8717e1Skboyarinov     key_type::reset();
1331fc8717e1Skboyarinov }
1332fc8717e1Skboyarinov 
test_heterogeneous_count()1333fc8717e1Skboyarinov void test_heterogeneous_count() {
1334fc8717e1Skboyarinov     using key_type = HeterogeneousKey</*IsConstructible = */false>;
1335fc8717e1Skboyarinov     using chmap_type = oneapi::tbb::concurrent_hash_map<key_type, int, HeterogeneousHashCompare>;
1336fc8717e1Skboyarinov 
1337fc8717e1Skboyarinov     chmap_type chmap;
1338fc8717e1Skboyarinov 
1339fc8717e1Skboyarinov     REQUIRE_MESSAGE(key_type::heterogeneous_keys_count == 0, "Incorrect test setup");
1340fc8717e1Skboyarinov     key_type key(key_type::construct_flag{}, 1);
1341fc8717e1Skboyarinov 
1342fc8717e1Skboyarinov     typename chmap_type::size_type regular_count = chmap.count(key);
1343fc8717e1Skboyarinov     typename chmap_type::size_type heterogeneous_count = chmap.count(int(1));
1344fc8717e1Skboyarinov 
1345fc8717e1Skboyarinov     REQUIRE(regular_count == 0);
1346fc8717e1Skboyarinov     REQUIRE_MESSAGE(regular_count == heterogeneous_count, "Incorrect heterogeneous count result (no element)");
1347fc8717e1Skboyarinov     REQUIRE_MESSAGE(key_type::heterogeneous_keys_count == 0, "Temporary key object was created during count call (no element)");
1348fc8717e1Skboyarinov 
1349fc8717e1Skboyarinov     chmap.emplace(std::piecewise_construct, std::forward_as_tuple(key_type::construct_flag{}, 1), std::forward_as_tuple(100));
1350fc8717e1Skboyarinov 
1351fc8717e1Skboyarinov     regular_count = chmap.count(key);
1352fc8717e1Skboyarinov     heterogeneous_count = chmap.count(int(1));
1353fc8717e1Skboyarinov 
1354fc8717e1Skboyarinov     REQUIRE(regular_count == 1);
1355fc8717e1Skboyarinov     REQUIRE_MESSAGE(regular_count == heterogeneous_count, "Incorrect heterogeneous count result (element exists)");
1356fc8717e1Skboyarinov     REQUIRE_MESSAGE(key_type::heterogeneous_keys_count == 0, "Temporary key object was created during count call (element exists)");
1357fc8717e1Skboyarinov     key_type::reset();
1358fc8717e1Skboyarinov }
1359fc8717e1Skboyarinov 
test_heterogeneous_equal_range()1360fc8717e1Skboyarinov void test_heterogeneous_equal_range() {
1361fc8717e1Skboyarinov     using key_type = HeterogeneousKey</*IsConstructible = */false>;
1362fc8717e1Skboyarinov     using chmap_type = oneapi::tbb::concurrent_hash_map<key_type, int, HeterogeneousHashCompare>;
1363fc8717e1Skboyarinov 
1364fc8717e1Skboyarinov     chmap_type chmap;
1365fc8717e1Skboyarinov     REQUIRE_MESSAGE(key_type::heterogeneous_keys_count == 0, "Incorrect test setup");
1366fc8717e1Skboyarinov 
1367fc8717e1Skboyarinov     using iterator = typename chmap_type::iterator;
1368fc8717e1Skboyarinov     using const_iterator = typename chmap_type::const_iterator;
1369fc8717e1Skboyarinov     using result = std::pair<iterator, iterator>;
1370fc8717e1Skboyarinov     using const_result = std::pair<const_iterator, const_iterator>;
1371fc8717e1Skboyarinov     key_type key(key_type::construct_flag{}, 1);
1372fc8717e1Skboyarinov 
1373fc8717e1Skboyarinov     result regular_result = chmap.equal_range(key);
1374fc8717e1Skboyarinov     result heterogeneous_result = chmap.equal_range(int(1));
1375fc8717e1Skboyarinov 
1376fc8717e1Skboyarinov     REQUIRE(regular_result.first == chmap.end());
1377fc8717e1Skboyarinov     REQUIRE(regular_result.second == chmap.end());
1378fc8717e1Skboyarinov     REQUIRE_MESSAGE(regular_result == heterogeneous_result, "Incorrect heterogeneous equal_range result (non const, no element)");
1379fc8717e1Skboyarinov     REQUIRE_MESSAGE(key_type::heterogeneous_keys_count == 0, "Temporary key object was created during equal_range call (non const, no element)");
1380fc8717e1Skboyarinov 
1381fc8717e1Skboyarinov     const chmap_type& cchmap = chmap;
1382fc8717e1Skboyarinov 
1383fc8717e1Skboyarinov     const_result regular_const_result = cchmap.equal_range(key);
1384fc8717e1Skboyarinov     const_result heterogeneous_const_result = cchmap.equal_range(int(1));
1385fc8717e1Skboyarinov 
1386fc8717e1Skboyarinov     REQUIRE(regular_const_result.first == cchmap.end());
1387fc8717e1Skboyarinov     REQUIRE(regular_const_result.second == cchmap.end());
1388fc8717e1Skboyarinov     REQUIRE_MESSAGE(regular_const_result == heterogeneous_const_result,
1389fc8717e1Skboyarinov                     "Incorrect heterogeneous equal_range result (const, no element)");
1390fc8717e1Skboyarinov     REQUIRE_MESSAGE(key_type::heterogeneous_keys_count == 0, "Temporary key object was created during equal_range call (const, no element)");
1391fc8717e1Skboyarinov 
1392fc8717e1Skboyarinov     chmap.emplace(std::piecewise_construct, std::forward_as_tuple(key_type::construct_flag{}, 1), std::forward_as_tuple(100));
1393fc8717e1Skboyarinov 
1394fc8717e1Skboyarinov     regular_result = chmap.equal_range(key);
1395fc8717e1Skboyarinov     heterogeneous_result = chmap.equal_range(int(1));
1396fc8717e1Skboyarinov 
1397fc8717e1Skboyarinov     REQUIRE(regular_result.first != chmap.end());
1398fc8717e1Skboyarinov     REQUIRE(regular_result.first->first.integer_key() == 1);
1399fc8717e1Skboyarinov     REQUIRE(regular_result.second == chmap.end());
1400fc8717e1Skboyarinov     REQUIRE_MESSAGE(regular_result == heterogeneous_result, "Incorrect heterogeneous equal_range result (non const, element exists)");
1401fc8717e1Skboyarinov     REQUIRE_MESSAGE(key_type::heterogeneous_keys_count == 0, "Temporary key object was created during equal_range call (non const, element exists)");
1402fc8717e1Skboyarinov 
1403fc8717e1Skboyarinov     regular_const_result = cchmap.equal_range(key);
1404fc8717e1Skboyarinov     heterogeneous_const_result = cchmap.equal_range(int(1));
1405fc8717e1Skboyarinov     REQUIRE_MESSAGE(regular_const_result == heterogeneous_const_result,
1406fc8717e1Skboyarinov                     "Incorrect heterogeneous equal_range result (const, element exists)");
1407fc8717e1Skboyarinov     REQUIRE_MESSAGE(key_type::heterogeneous_keys_count == 0, "Temporary key object was created during equal_range call (const, element exists)");
1408fc8717e1Skboyarinov     key_type::reset();
1409fc8717e1Skboyarinov }
1410fc8717e1Skboyarinov 
test_heterogeneous_insert()1411fc8717e1Skboyarinov void test_heterogeneous_insert() {
1412fc8717e1Skboyarinov     using key_type = HeterogeneousKey</*IsConstructible = */true>;
1413fc8717e1Skboyarinov     using chmap_type = oneapi::tbb::concurrent_hash_map<key_type, DefaultConstructibleValue, HeterogeneousHashCompare>;
1414fc8717e1Skboyarinov 
1415fc8717e1Skboyarinov     chmap_type chmap;
1416fc8717e1Skboyarinov     using const_accessor = typename chmap_type::const_accessor;
1417fc8717e1Skboyarinov     using accessor = typename chmap_type::accessor;
1418fc8717e1Skboyarinov     const_accessor cacc;
1419fc8717e1Skboyarinov     accessor acc;
1420fc8717e1Skboyarinov 
1421fc8717e1Skboyarinov     REQUIRE_MESSAGE(key_type::heterogeneous_keys_count == 0, "Incorrect test setup");
1422fc8717e1Skboyarinov 
1423fc8717e1Skboyarinov     bool result = chmap.insert(cacc, int(1));
1424fc8717e1Skboyarinov 
1425fc8717e1Skboyarinov     REQUIRE_MESSAGE(key_type::heterogeneous_keys_count == 1, "Only one heterogeneous key should be created");
1426fc8717e1Skboyarinov     REQUIRE_MESSAGE(result, "Incorrect heterogeneous insert result (const_accessor)");
1427fc8717e1Skboyarinov     REQUIRE_MESSAGE(cacc->first.integer_key() == 1, "Incorrect accessor");
1428fc8717e1Skboyarinov     REQUIRE_MESSAGE(cacc->second.value() == DefaultConstructibleValue::default_value, "Value should be default constructed");
1429fc8717e1Skboyarinov 
1430fc8717e1Skboyarinov     result = chmap.insert(cacc, int(1));
1431fc8717e1Skboyarinov 
1432fc8717e1Skboyarinov     REQUIRE_MESSAGE(key_type::heterogeneous_keys_count == 1, "No extra keys should be created");
1433fc8717e1Skboyarinov     REQUIRE_MESSAGE(!result, "Incorrect heterogeneous insert result (const_accessor)");
1434fc8717e1Skboyarinov     REQUIRE_MESSAGE(cacc->first.integer_key() == 1, "Incorrect accessor");
1435fc8717e1Skboyarinov     REQUIRE_MESSAGE(cacc->second.value() == DefaultConstructibleValue::default_value, "Value should be default constructed");
1436fc8717e1Skboyarinov 
1437fc8717e1Skboyarinov     result = chmap.insert(acc, int(2));
1438fc8717e1Skboyarinov 
1439fc8717e1Skboyarinov     REQUIRE_MESSAGE(key_type::heterogeneous_keys_count == 2, "Only one extra heterogeneous key should be created");
1440fc8717e1Skboyarinov     REQUIRE_MESSAGE(result, "Incorrect heterogeneous insert result (accessor)");
1441fc8717e1Skboyarinov     REQUIRE_MESSAGE(acc->first.integer_key() == 2, "Incorrect accessor");
1442fc8717e1Skboyarinov     REQUIRE_MESSAGE(acc->second.value() == DefaultConstructibleValue::default_value, "Value should be default constructed");
1443fc8717e1Skboyarinov 
1444fc8717e1Skboyarinov     result = chmap.insert(acc, int(2));
1445fc8717e1Skboyarinov 
1446fc8717e1Skboyarinov     REQUIRE_MESSAGE(key_type::heterogeneous_keys_count == 2, "No extra keys should be created");
1447fc8717e1Skboyarinov     REQUIRE_MESSAGE(!result, "Incorrect heterogeneous insert result (accessor)");
1448fc8717e1Skboyarinov     REQUIRE_MESSAGE(acc->first.integer_key() == 2, "Incorrect accessor");
1449fc8717e1Skboyarinov     REQUIRE_MESSAGE(acc->second.value() == DefaultConstructibleValue::default_value, "Value should be default constructed");
1450fc8717e1Skboyarinov 
1451fc8717e1Skboyarinov     key_type::reset();
1452fc8717e1Skboyarinov }
1453fc8717e1Skboyarinov 
test_heterogeneous_erase()1454fc8717e1Skboyarinov void test_heterogeneous_erase() {
1455fc8717e1Skboyarinov     using key_type = HeterogeneousKey</*IsConstructible = */false>;
1456fc8717e1Skboyarinov     using chmap_type = oneapi::tbb::concurrent_hash_map<key_type, int, HeterogeneousHashCompare>;
1457fc8717e1Skboyarinov 
1458fc8717e1Skboyarinov     chmap_type chmap;
1459fc8717e1Skboyarinov 
1460fc8717e1Skboyarinov     REQUIRE_MESSAGE(key_type::heterogeneous_keys_count == 0, "Incorrect test setup");
1461fc8717e1Skboyarinov 
1462fc8717e1Skboyarinov     chmap.emplace(std::piecewise_construct, std::forward_as_tuple(key_type::construct_flag{}, 1), std::forward_as_tuple(100));
1463fc8717e1Skboyarinov     chmap.emplace(std::piecewise_construct, std::forward_as_tuple(key_type::construct_flag{}, 2), std::forward_as_tuple(200));
1464fc8717e1Skboyarinov 
1465fc8717e1Skboyarinov     typename chmap_type::const_accessor cacc;
1466fc8717e1Skboyarinov 
1467fc8717e1Skboyarinov     REQUIRE(chmap.find(cacc, int(1)));
1468fc8717e1Skboyarinov     REQUIRE(chmap.find(cacc, int(2)));
1469fc8717e1Skboyarinov 
1470fc8717e1Skboyarinov     cacc.release();
1471fc8717e1Skboyarinov 
1472fc8717e1Skboyarinov     bool result = chmap.erase(int(1));
1473fc8717e1Skboyarinov     REQUIRE_MESSAGE(result, "Erasure should be successful");
1474fc8717e1Skboyarinov     REQUIRE_MESSAGE(key_type::heterogeneous_keys_count == 0, "No extra keys should be created");
1475fc8717e1Skboyarinov     REQUIRE_MESSAGE(!chmap.find(cacc, int(1)), "Element was not erased");
1476fc8717e1Skboyarinov 
1477fc8717e1Skboyarinov 
1478fc8717e1Skboyarinov     result = chmap.erase(int(1));
1479fc8717e1Skboyarinov     REQUIRE_MESSAGE(!result, "Erasure should fail");
1480fc8717e1Skboyarinov     REQUIRE_MESSAGE(key_type::heterogeneous_keys_count == 0, "No extra keys should be created");
1481fc8717e1Skboyarinov     key_type::reset();
1482fc8717e1Skboyarinov }
1483fc8717e1Skboyarinov 
test_heterogeneous_lookup()1484fc8717e1Skboyarinov void test_heterogeneous_lookup() {
1485fc8717e1Skboyarinov     test_heterogeneous_find();
1486fc8717e1Skboyarinov     test_heterogeneous_count();
1487fc8717e1Skboyarinov     test_heterogeneous_equal_range();
1488fc8717e1Skboyarinov }
1489fc8717e1Skboyarinov 
1490*c4a799dfSJhaShweta1 //! Test construction with hash_compare
149151c0b2f7Stbbdev //! \brief \ref interface \ref requirement
1492*c4a799dfSJhaShweta1 TEST_CASE("testing construction with hash_compare") {
149351c0b2f7Stbbdev     TestHashCompareConstructors();
149451c0b2f7Stbbdev }
149551c0b2f7Stbbdev 
149651c0b2f7Stbbdev //! Test concurrent_hash_map member types
149751c0b2f7Stbbdev //! \brief \ref interface \ref requirement
149851c0b2f7Stbbdev TEST_CASE("test types"){
149949e08aacStbbdev     test_member_types<oneapi::tbb::concurrent_hash_map>();
150051c0b2f7Stbbdev }
150151c0b2f7Stbbdev 
150251c0b2f7Stbbdev //! Test swap and clear operations
150351c0b2f7Stbbdev //! \brief \ref interface \ref requirement
150451c0b2f7Stbbdev TEST_CASE("test copy operations") {
150551c0b2f7Stbbdev     TestCopy();
150651c0b2f7Stbbdev }
150751c0b2f7Stbbdev 
150851c0b2f7Stbbdev //! Test rehash operation
150951c0b2f7Stbbdev //! \brief \ref interface \ref requirement
151051c0b2f7Stbbdev TEST_CASE("test rehash operation") {
151151c0b2f7Stbbdev     TestRehash();
151251c0b2f7Stbbdev }
151351c0b2f7Stbbdev 
151451c0b2f7Stbbdev //! Test assignment operation
151551c0b2f7Stbbdev //! \brief \ref interface \ref requirement
151651c0b2f7Stbbdev TEST_CASE("test assignment operation") {
151751c0b2f7Stbbdev     TestAssignment();
151851c0b2f7Stbbdev }
151951c0b2f7Stbbdev 
152051c0b2f7Stbbdev //! Test iterators and ranges
152151c0b2f7Stbbdev //! \brief \ref interface \ref requirement
152251c0b2f7Stbbdev TEST_CASE("test iterators and ranges") {
152351c0b2f7Stbbdev     TestIteratorsAndRanges();
152451c0b2f7Stbbdev }
152551c0b2f7Stbbdev 
152651c0b2f7Stbbdev //! Test work with initializer_list
152751c0b2f7Stbbdev //! \brief \ref interface \ref requirement
152851c0b2f7Stbbdev TEST_CASE("test work with initializer_list") {
152951c0b2f7Stbbdev     TestInitList();
153051c0b2f7Stbbdev }
153151c0b2f7Stbbdev 
153251c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
153351c0b2f7Stbbdev //! Test exception safety
153451c0b2f7Stbbdev //! \brief \ref requirement
153551c0b2f7Stbbdev TEST_CASE("test exception safety") {
153651c0b2f7Stbbdev     TestExceptions();
153751c0b2f7Stbbdev }
153851c0b2f7Stbbdev 
153951c0b2f7Stbbdev //! Test exceptions safety guarantees for move constructor
154051c0b2f7Stbbdev //! \brief \ref requirement
154151c0b2f7Stbbdev TEST_CASE("test move support with exceptions") {
154251c0b2f7Stbbdev     move_support_tests::test_ex_move_ctor_unequal_allocator_memory_failure<hash_map_traits>();
154351c0b2f7Stbbdev     move_support_tests::test_ex_move_ctor_unequal_allocator_element_ctor_failure<hash_map_traits>();
154451c0b2f7Stbbdev }
154551c0b2f7Stbbdev #endif
154651c0b2f7Stbbdev 
154751c0b2f7Stbbdev //! Test move constructor
154851c0b2f7Stbbdev //! \brief \ref interface \ref requirement
154951c0b2f7Stbbdev TEST_CASE("testing move constructor"){
155051c0b2f7Stbbdev     move_support_tests::test_move_constructor<hash_map_traits>();
155151c0b2f7Stbbdev }
155251c0b2f7Stbbdev 
155351c0b2f7Stbbdev //! Test move assign operator
155451c0b2f7Stbbdev //! \brief \ref interface \ref requirement
155551c0b2f7Stbbdev TEST_CASE("testing move assign operator"){
155651c0b2f7Stbbdev     move_support_tests::test_move_assignment<hash_map_traits>();
155751c0b2f7Stbbdev }
155851c0b2f7Stbbdev 
155951c0b2f7Stbbdev //! Test insert and empace
156051c0b2f7Stbbdev //! \brief \ref interface \ref requirement
15619e15720bStbbdev TEST_CASE("testing concurrent insert and emplace"){
156251c0b2f7Stbbdev     int n=250000;
156351c0b2f7Stbbdev     {
156451c0b2f7Stbbdev         DataStateTrackedTable table;
156551c0b2f7Stbbdev         DoConcurrentOperations<RvalueInsert, DataStateTrackedTable>( table, n, "rvalue ref insert", 1 );
156651c0b2f7Stbbdev     }
156751c0b2f7Stbbdev     {
156851c0b2f7Stbbdev         DataStateTrackedTable table;
156951c0b2f7Stbbdev         DoConcurrentOperations<Emplace, DataStateTrackedTable>( table, n, "emplace", 1 );
157051c0b2f7Stbbdev     }
157151c0b2f7Stbbdev }
157251c0b2f7Stbbdev 
157351c0b2f7Stbbdev //! Test allocator traits
157451c0b2f7Stbbdev //! \brief \ref requirement
157551c0b2f7Stbbdev TEST_CASE("testing allocator traits") {
157651c0b2f7Stbbdev     test_allocator_traits_support<hash_map_traits>();
157751c0b2f7Stbbdev }
157851c0b2f7Stbbdev 
157951c0b2f7Stbbdev //! Test concurrent operations
158051c0b2f7Stbbdev //! \brief \ref requirement
158151c0b2f7Stbbdev TEST_CASE("testing concurrency"){
158251c0b2f7Stbbdev     for (std::size_t p = 1; p <= 4; ++p) {
158349e08aacStbbdev         oneapi::tbb::global_control limit(oneapi::tbb::global_control::max_allowed_parallelism, p);
158451c0b2f7Stbbdev         TestInsertFindErase(p);
158551c0b2f7Stbbdev         TestConcurrency(p);
158651c0b2f7Stbbdev     }
158751c0b2f7Stbbdev }
158851c0b2f7Stbbdev 
158951c0b2f7Stbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
159051c0b2f7Stbbdev //! Test deduction guides
159151c0b2f7Stbbdev //! \brief \ref interface
159251c0b2f7Stbbdev TEST_CASE("testing deduction guides") {
159349e08aacStbbdev     TestDeductionGuides<oneapi::tbb::concurrent_hash_map>();
159451c0b2f7Stbbdev }
159551c0b2f7Stbbdev #endif // __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
1596b15aabb3Stbbdev 
1597b15aabb3Stbbdev //! \brief \ref interface \ref requirement
1598b15aabb3Stbbdev TEST_CASE("concurrent_hash_map comparisons") {
1599b15aabb3Stbbdev     TestCHMapComparisons();
1600b15aabb3Stbbdev }
1601b15aabb3Stbbdev 
1602b15aabb3Stbbdev //! \brief \ref interface \ref requirement
1603b15aabb3Stbbdev TEST_CASE("concurrent_hash_map iterator comparisons") {
1604b15aabb3Stbbdev     TestCHMapIteratorComparisons();
1605b15aabb3Stbbdev }
1606fc8717e1Skboyarinov 
1607fc8717e1Skboyarinov //! \brief \ref interface \ref requirement
1608fc8717e1Skboyarinov TEST_CASE("test concurrent_hash_map heterogeneous lookup") {
1609fc8717e1Skboyarinov     test_heterogeneous_lookup();
1610fc8717e1Skboyarinov }
1611fc8717e1Skboyarinov 
1612fc8717e1Skboyarinov //! \brief \ref interface \ref requirement
1613fc8717e1Skboyarinov TEST_CASE("test concurrent_hash_map heterogeneous insert") {
1614fc8717e1Skboyarinov     test_heterogeneous_insert();
1615fc8717e1Skboyarinov }
1616fc8717e1Skboyarinov 
1617fc8717e1Skboyarinov //! \brief \ref interface \ref requirement
1618fc8717e1Skboyarinov TEST_CASE("test concurrent_hash_map heterogeneous erase") {
1619fc8717e1Skboyarinov     test_heterogeneous_erase();
1620fc8717e1Skboyarinov }
1621