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