1 /*
2 Copyright (c) 2005-2020 Intel Corporation
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17 #ifndef TBB_USE_PERFORMANCE_WARNINGS
18 #define TBB_USE_PERFORMANCE_WARNINGS 1
19 #endif
20
21 // Our tests usually include the header under test first. But this test needs
22 // to use the preprocessor to edit the identifier runtime_warning in concurrent_hash_map.h.
23 // Hence we include a few other headers before doing the abusive edit.
24 #include "tbb/tbb_stddef.h" /* Defines runtime_warning */
25 #include "harness_assert.h" /* Prerequisite for defining hooked_warning */
26
27 // The symbol internal::runtime_warning is normally an entry point into the TBB library.
28 // Here for sake of testing, we define it to be hooked_warning, a routine peculiar to this unit test.
29 #define runtime_warning hooked_warning
30
31 static bool bad_hashing = false;
32
33 namespace tbb {
34 namespace internal {
hooked_warning(const char *,...)35 static void hooked_warning( const char* /*format*/, ... ) {
36 ASSERT(bad_hashing, "unexpected runtime_warning: bad hashing");
37 }
38 } // namespace internal
39 } // namespace tbb
40 #define __TBB_EXTRA_DEBUG 1 // enables additional checks
41 #include "tbb/concurrent_hash_map.h"
42
43 // Restore runtime_warning as an entry point into the TBB library.
44 #undef runtime_warning
45
46 namespace Jungle {
47 struct Tiger {};
tbb_hasher(const Tiger &)48 size_t tbb_hasher( const Tiger& ) {return 0;}
49 }
50
51 #if !defined(_MSC_VER) || _MSC_VER>=1400 || __INTEL_COMPILER
test_ADL()52 void test_ADL() {
53 tbb::tbb_hash_compare<Jungle::Tiger>::hash(Jungle::Tiger()); // Instantiation chain finds tbb_hasher via Argument Dependent Lookup
54 }
55 #endif
56
57 struct UserDefinedKeyType {
58 };
59
60 namespace tbb {
61 // Test whether tbb_hash_compare can be partially specialized as stated in Reference manual.
62 template<> struct tbb_hash_compare<UserDefinedKeyType> {
hashtbb::tbb_hash_compare63 size_t hash( UserDefinedKeyType ) const {return 0;}
equaltbb::tbb_hash_compare64 bool equal( UserDefinedKeyType /*x*/, UserDefinedKeyType /*y*/ ) {return true;}
65 };
66 }
67
68 #include "harness_runtime_loader.h"
69
70 tbb::concurrent_hash_map<UserDefinedKeyType,int> TestInstantiationWithUserDefinedKeyType;
71
72 // Test whether a sufficient set of headers were included to instantiate a concurrent_hash_map. OSS Bug #120 (& #130):
73 // http://www.threadingbuildingblocks.org/bug_desc.php?id=120
74 tbb::concurrent_hash_map<std::pair<std::pair<int,std::string>,const char*>,int> TestInstantiation;
75
76 #include "tbb/parallel_for.h"
77 #include "tbb/blocked_range.h"
78 #include "tbb/atomic.h"
79 #include "tbb/tick_count.h"
80 #include "harness.h"
81 #include "harness_allocator.h"
82
83 class MyException : public std::bad_alloc {
84 public:
what() const85 virtual const char *what() const throw() __TBB_override { return "out of items limit"; }
~MyException()86 virtual ~MyException() throw() {}
87 };
88
89 /** Has tightly controlled interface so that we can verify
90 that concurrent_hash_map uses only the required interface. */
91 class MyKey {
92 private:
93 void operator=( const MyKey& ); // Deny access
94 int key;
95 friend class MyHashCompare;
96 friend class YourHashCompare;
97 public:
make(int i)98 static MyKey make( int i ) {
99 MyKey result;
100 result.key = i;
101 return result;
102 }
value_of() const103 int value_of() const {return key;}
104 };
105 //TODO: unify with Harness::Foo ?
106 tbb::atomic<long> MyDataCount;
107 long MyDataCountLimit = 0;
108
109 class MyData {
110 protected:
111 friend class MyData2;
112 int data;
113 enum state_t {
114 LIVE=0x1234,
115 DEAD=0x5678
116 } my_state;
117 void operator=( const MyData& ); // Deny access
118 public:
MyData(int i=0)119 MyData(int i = 0) {
120 my_state = LIVE;
121 data = i;
122 if(MyDataCountLimit && MyDataCount + 1 >= MyDataCountLimit)
123 __TBB_THROW( MyException() );
124 ++MyDataCount;
125 }
MyData(const MyData & other)126 MyData( const MyData& other ) {
127 ASSERT( other.my_state==LIVE, NULL );
128 my_state = LIVE;
129 data = other.data;
130 if(MyDataCountLimit && MyDataCount + 1 >= MyDataCountLimit)
131 __TBB_THROW( MyException() );
132 ++MyDataCount;
133 }
~MyData()134 ~MyData() {
135 --MyDataCount;
136 my_state = DEAD;
137 }
make(int i)138 static MyData make( int i ) {
139 MyData result;
140 result.data = i;
141 return result;
142 }
value_of() const143 int value_of() const {
144 ASSERT( my_state==LIVE, NULL );
145 return data;
146 }
set_value(int i)147 void set_value( int i ) {
148 ASSERT( my_state==LIVE, NULL );
149 data = i;
150 }
operator ==(const MyData & other) const151 bool operator==( const MyData& other ) const {
152 ASSERT( other.my_state==LIVE, NULL );
153 ASSERT( my_state==LIVE, NULL );
154 return data == other.data;
155 }
156 };
157
158 class MyData2 : public MyData {
159 public:
MyData2()160 MyData2( ) {}
MyData2(const MyData & other)161 MyData2( const MyData& other ) {
162 ASSERT( other.my_state==LIVE, NULL );
163 ASSERT( my_state==LIVE, NULL );
164 data = other.data;
165 }
operator =(const MyData & other)166 void operator=( const MyData& other ) {
167 ASSERT( other.my_state==LIVE, NULL );
168 ASSERT( my_state==LIVE, NULL );
169 data = other.data;
170 }
operator =(const MyData2 & other)171 void operator=( const MyData2& other ) {
172 ASSERT( other.my_state==LIVE, NULL );
173 ASSERT( my_state==LIVE, NULL );
174 data = other.data;
175 }
operator ==(const MyData2 & other) const176 bool operator==( const MyData2& other ) const {
177 ASSERT( other.my_state==LIVE, NULL );
178 ASSERT( my_state==LIVE, NULL );
179 return data == other.data;
180 }
181 };
182
183 class MyHashCompare {
184 public:
equal(const MyKey & j,const MyKey & k) const185 bool equal( const MyKey& j, const MyKey& k ) const {
186 return j.key==k.key;
187 }
hash(const MyKey & k) const188 unsigned long hash( const MyKey& k ) const {
189 return k.key;
190 }
191 };
192
193 class YourHashCompare {
194 public:
equal(const MyKey & j,const MyKey & k) const195 bool equal( const MyKey& j, const MyKey& k ) const {
196 return j.key==k.key;
197 }
hash(const MyKey &) const198 unsigned long hash( const MyKey& ) const {
199 return 1;
200 }
201 };
202
203 typedef local_counting_allocator<std::allocator<MyData> > MyAllocator;
204 typedef tbb::concurrent_hash_map<MyKey,MyData,MyHashCompare,MyAllocator> MyTable;
205 typedef tbb::concurrent_hash_map<MyKey,MyData2,MyHashCompare> MyTable2;
206 typedef tbb::concurrent_hash_map<MyKey,MyData,YourHashCompare> YourTable;
207
208 template<typename MyTable>
CheckAllocator(MyTable & table,size_t expected_allocs,size_t expected_frees,bool exact=true)209 inline void CheckAllocator(MyTable &table, size_t expected_allocs, size_t expected_frees, bool exact = true) {
210 size_t items_allocated = table.get_allocator().items_allocated, items_freed = table.get_allocator().items_freed;
211 size_t allocations = table.get_allocator().allocations, frees = table.get_allocator().frees;
212 REMARK("checking allocators: items %u/%u, allocs %u/%u\n",
213 unsigned(items_allocated), unsigned(items_freed), unsigned(allocations), unsigned(frees) );
214 ASSERT( items_allocated == allocations, NULL); ASSERT( items_freed == frees, NULL);
215 if(exact) {
216 ASSERT( allocations == expected_allocs, NULL); ASSERT( frees == expected_frees, NULL);
217 } else {
218 ASSERT( allocations >= expected_allocs, NULL); ASSERT( frees >= expected_frees, NULL);
219 ASSERT( allocations - frees == expected_allocs - expected_frees, NULL );
220 }
221 }
222
UseKey(size_t i)223 inline bool UseKey( size_t i ) {
224 return (i&3)!=3;
225 }
226
227 struct Insert {
applyInsert228 static void apply( MyTable& table, int i ) {
229 if( UseKey(i) ) {
230 if( i&4 ) {
231 MyTable::accessor a;
232 table.insert( a, MyKey::make(i) );
233 if( i&1 )
234 (*a).second.set_value(i*i);
235 else
236 a->second.set_value(i*i);
237 } else
238 if( i&1 ) {
239 MyTable::accessor a;
240 table.insert( a, std::make_pair(MyKey::make(i), MyData(i*i)) );
241 ASSERT( (*a).second.value_of()==i*i, NULL );
242 } else {
243 MyTable::const_accessor ca;
244 table.insert( ca, std::make_pair(MyKey::make(i), MyData(i*i)) );
245 ASSERT( ca->second.value_of()==i*i, NULL );
246 }
247 }
248 }
249 };
250
251 #if __TBB_CPP11_RVALUE_REF_PRESENT
252 #include "test_container_move_support.h"
253 typedef tbb::concurrent_hash_map<MyKey,Foo,MyHashCompare> DataStateTrackedTable;
254
255 struct RvalueInsert {
applyRvalueInsert256 static void apply( DataStateTrackedTable& table, int i ) {
257 DataStateTrackedTable::accessor a;
258 ASSERT( (table.insert( a, std::make_pair(MyKey::make(i), Foo(i + 1)))),"already present while should not ?" );
259 ASSERT( (*a).second == i + 1, NULL );
260 ASSERT( (*a).second.state == Harness::StateTrackableBase::MoveInitialized, "");
261 }
262 };
263
264 #if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
265 struct Emplace {
applyEmplace266 static void apply( DataStateTrackedTable& table, int i ) {
267 DataStateTrackedTable::accessor a;
268 ASSERT( (table.emplace( a, MyKey::make(i), (i + 1))),"already present while should not ?" );
269 ASSERT( (*a).second == i + 1, NULL );
270 ASSERT( (*a).second.state == Harness::StateTrackableBase::DirectInitialized, "");
271 }
272 };
273 #endif // __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
274 #endif // __TBB_CPP11_RVALUE_REF_PRESENT
275
276 #if __TBB_INITIALIZER_LISTS_PRESENT
277 struct InsertInitList {
applyInsertInitList278 static void apply( MyTable& table, int i ) {
279 if ( UseKey( i ) ) {
280 // TODO: investigate why the following sequence causes an additional allocation sometimes:
281 // table.insert( MyTable::value_type( MyKey::make( i ), i*i ) );
282 // table.insert( MyTable::value_type( MyKey::make( i ), i*i+1 ) );
283 std::initializer_list<MyTable::value_type> il = { MyTable::value_type( MyKey::make( i ), i*i )/*, MyTable::value_type( MyKey::make( i ), i*i+1 ) */ };
284 table.insert( il );
285 }
286 }
287 };
288 #endif /* __TBB_INITIALIZER_LISTS_PRESENT */
289
290 struct Find {
applyFind291 static void apply( MyTable& table, int i ) {
292 MyTable::accessor a;
293 const MyTable::accessor& ca = a;
294 bool b = table.find( a, MyKey::make(i) );
295 ASSERT( b==!a.empty(), NULL );
296 if( b ) {
297 if( !UseKey(i) )
298 REPORT("Line %d: unexpected key %d present\n",__LINE__,i);
299 AssertSameType( &*a, static_cast<MyTable::value_type*>(0) );
300 ASSERT( ca->second.value_of()==i*i, NULL );
301 ASSERT( (*ca).second.value_of()==i*i, NULL );
302 if( i&1 )
303 ca->second.set_value( ~ca->second.value_of() );
304 else
305 (*ca).second.set_value( ~ca->second.value_of() );
306 } else {
307 if( UseKey(i) )
308 REPORT("Line %d: key %d missing\n",__LINE__,i);
309 }
310 }
311 };
312
313 struct FindConst {
applyFindConst314 static void apply( const MyTable& table, int i ) {
315 MyTable::const_accessor a;
316 const MyTable::const_accessor& ca = a;
317 bool b = table.find( a, MyKey::make(i) );
318 ASSERT( b==(table.count(MyKey::make(i))>0), NULL );
319 ASSERT( b==!a.empty(), NULL );
320 ASSERT( b==UseKey(i), NULL );
321 if( b ) {
322 AssertSameType( &*ca, static_cast<const MyTable::value_type*>(0) );
323 ASSERT( ca->second.value_of()==~(i*i), NULL );
324 ASSERT( (*ca).second.value_of()==~(i*i), NULL );
325 }
326 }
327 };
328
329 tbb::atomic<int> EraseCount;
330
331 struct Erase {
applyErase332 static void apply( MyTable& table, int i ) {
333 bool b;
334 if(i&4) {
335 if(i&8) {
336 MyTable::const_accessor a;
337 b = table.find( a, MyKey::make(i) ) && table.erase( a );
338 } else {
339 MyTable::accessor a;
340 b = table.find( a, MyKey::make(i) ) && table.erase( a );
341 }
342 } else
343 b = table.erase( MyKey::make(i) );
344 if( b ) ++EraseCount;
345 ASSERT( table.count(MyKey::make(i)) == 0, NULL );
346 }
347 };
348
349 static const int IE_SIZE = 2;
350 tbb::atomic<YourTable::size_type> InsertEraseCount[IE_SIZE];
351
352 struct InsertErase {
applyInsertErase353 static void apply( YourTable& table, int i ) {
354 if ( i%3 ) {
355 int key = i%IE_SIZE;
356 if ( table.insert( std::make_pair(MyKey::make(key), MyData2()) ) )
357 ++InsertEraseCount[key];
358 } else {
359 int key = i%IE_SIZE;
360 if( i&1 ) {
361 YourTable::accessor res;
362 if(table.find( res, MyKey::make(key) ) && table.erase( res ) )
363 --InsertEraseCount[key];
364 } else {
365 YourTable::const_accessor res;
366 if(table.find( res, MyKey::make(key) ) && table.erase( res ) )
367 --InsertEraseCount[key];
368 }
369 }
370 }
371 };
372
373 // Test for the deadlock discussed at:
374 // http://softwarecommunity.intel.com/isn/Community/en-US/forums/permalink/30253302/30253302/ShowThread.aspx#30253302
375 struct InnerInsert {
applyInnerInsert376 static void apply( YourTable& table, int i ) {
377 YourTable::accessor a1, a2;
378 if(i&1) __TBB_Yield();
379 table.insert( a1, MyKey::make(1) );
380 __TBB_Yield();
381 table.insert( a2, MyKey::make(1 + (1<<30)) ); // the same chain
382 table.erase( a2 ); // if erase by key it would lead to deadlock for single thread
383 }
384 };
385
386 #include "harness_barrier.h"
387 // Test for the misuse of constness
388 struct FakeExclusive : NoAssign {
389 Harness::SpinBarrier& barrier;
390 YourTable& table;
FakeExclusiveFakeExclusive391 FakeExclusive(Harness::SpinBarrier& b, YourTable&t) : barrier(b), table(t) {}
operator ()FakeExclusive392 void operator()( int i ) const {
393 if(i) {
394 YourTable::const_accessor real_ca;
395 // const accessor on non-const table acquired as reader (shared)
396 ASSERT( table.find(real_ca,MyKey::make(1)), NULL );
397 barrier.wait(); // item can be erased
398 Harness::Sleep(10); // let it enter the erase
399 real_ca->second.value_of(); // check the state while holding accessor
400 } else {
401 YourTable::accessor fake_ca;
402 const YourTable &const_table = table;
403 // non-const accessor on const table acquired as reader (shared)
404 ASSERT( const_table.find(fake_ca,MyKey::make(1)), NULL );
405 barrier.wait(); // readers acquired
406 // can mistakenly remove the item while other readers still refers to it
407 table.erase( fake_ca );
408 }
409 }
410 };
411
412 template<typename Op, typename MyTable>
413 class TableOperation: NoAssign {
414 MyTable& my_table;
415 public:
operator ()(const tbb::blocked_range<int> & range) const416 void operator()( const tbb::blocked_range<int>& range ) const {
417 for( int i=range.begin(); i!=range.end(); ++i )
418 Op::apply(my_table,i);
419 }
TableOperation(MyTable & table)420 TableOperation( MyTable& table ) : my_table(table) {}
421 };
422
423 template<typename Op, typename TableType>
DoConcurrentOperations(TableType & table,int n,const char * what,int nthread)424 void DoConcurrentOperations( TableType& table, int n, const char* what, int nthread ) {
425 REMARK("testing %s with %d threads\n",what,nthread);
426 tbb::tick_count t0 = tbb::tick_count::now();
427 tbb::parallel_for( tbb::blocked_range<int>(0,n,100), TableOperation<Op,TableType>(table) );
428 tbb::tick_count t1 = tbb::tick_count::now();
429 REMARK("time for %s = %g with %d threads\n",what,(t1-t0).seconds(),nthread);
430 }
431
432 //! Test traversing the table with an iterator.
TraverseTable(MyTable & table,size_t n,size_t expected_size)433 void TraverseTable( MyTable& table, size_t n, size_t expected_size ) {
434 REMARK("testing traversal\n");
435 size_t actual_size = table.size();
436 ASSERT( actual_size==expected_size, NULL );
437 size_t count = 0;
438 bool* array = new bool[n];
439 memset( array, 0, n*sizeof(bool) );
440 const MyTable& const_table = table;
441 MyTable::const_iterator ci = const_table.begin();
442 for( MyTable::iterator i = table.begin(); i!=table.end(); ++i ) {
443 // Check iterator
444 int k = i->first.value_of();
445 ASSERT( UseKey(k), NULL );
446 ASSERT( (*i).first.value_of()==k, NULL );
447 ASSERT( 0<=k && size_t(k)<n, "out of bounds key" );
448 ASSERT( !array[k], "duplicate key" );
449 array[k] = true;
450 ++count;
451
452 // Check lower/upper bounds
453 std::pair<MyTable::iterator, MyTable::iterator> er = table.equal_range(i->first);
454 std::pair<MyTable::const_iterator, MyTable::const_iterator> cer = const_table.equal_range(i->first);
455 ASSERT(cer.first == er.first && cer.second == er.second, NULL);
456 ASSERT(cer.first == i, NULL);
457 ASSERT(std::distance(cer.first, cer.second) == 1, NULL);
458
459 // Check const_iterator
460 MyTable::const_iterator cic = ci++;
461 ASSERT( cic->first.value_of()==k, NULL );
462 ASSERT( (*cic).first.value_of()==k, NULL );
463 }
464 ASSERT( ci==const_table.end(), NULL );
465 delete[] array;
466 if( count!=expected_size ) {
467 REPORT("Line %d: count=%ld but should be %ld\n",__LINE__,long(count),long(expected_size));
468 }
469 }
470
471 typedef tbb::atomic<unsigned char> AtomicByte;
472
473 template<typename RangeType>
474 struct ParallelTraverseBody: NoAssign {
475 const size_t n;
476 AtomicByte* const array;
ParallelTraverseBodyParallelTraverseBody477 ParallelTraverseBody( AtomicByte array_[], size_t n_ ) :
478 n(n_),
479 array(array_)
480 {}
operator ()ParallelTraverseBody481 void operator()( const RangeType& range ) const {
482 for( typename RangeType::iterator i = range.begin(); i!=range.end(); ++i ) {
483 int k = i->first.value_of();
484 ASSERT( 0<=k && size_t(k)<n, NULL );
485 ++array[k];
486 }
487 }
488 };
489
Check(AtomicByte array[],size_t n,size_t expected_size)490 void Check( AtomicByte array[], size_t n, size_t expected_size ) {
491 if( expected_size )
492 for( size_t k=0; k<n; ++k ) {
493 if( array[k] != int(UseKey(k)) ) {
494 REPORT("array[%d]=%d != %d=UseKey(%d)\n",
495 int(k), int(array[k]), int(UseKey(k)), int(k));
496 ASSERT(false,NULL);
497 }
498 }
499 }
500
501 //! Test traversing the table with a parallel range
ParallelTraverseTable(MyTable & table,size_t n,size_t expected_size)502 void ParallelTraverseTable( MyTable& table, size_t n, size_t expected_size ) {
503 REMARK("testing parallel traversal\n");
504 ASSERT( table.size()==expected_size, NULL );
505 AtomicByte* array = new AtomicByte[n];
506
507 memset( static_cast<void*>(array), 0, n*sizeof(AtomicByte) );
508 MyTable::range_type r = table.range(10);
509 tbb::parallel_for( r, ParallelTraverseBody<MyTable::range_type>( array, n ));
510 Check( array, n, expected_size );
511
512 const MyTable& const_table = table;
513 memset( static_cast<void*>(array), 0, n*sizeof(AtomicByte) );
514 MyTable::const_range_type cr = const_table.range(10);
515 tbb::parallel_for( cr, ParallelTraverseBody<MyTable::const_range_type>( array, n ));
516 Check( array, n, expected_size );
517
518 delete[] array;
519 }
520
TestInsertFindErase(int nthread)521 void TestInsertFindErase( int nthread ) {
522 int n=250000;
523
524 // compute m = number of unique keys
525 int m = 0;
526 for( int i=0; i<n; ++i )
527 m += UseKey(i);
528
529 MyAllocator a; a.items_freed = a.frees = 100;
530 ASSERT( MyDataCount==0, NULL );
531 MyTable table(a);
532 TraverseTable(table,n,0);
533 ParallelTraverseTable(table,n,0);
534 CheckAllocator(table, 0, 100);
535
536 int expected_allocs = 0, expected_frees = 100;
537 #if __TBB_INITIALIZER_LISTS_PRESENT
538 for ( int i = 0; i < 2; ++i ) {
539 if ( i==0 )
540 DoConcurrentOperations<InsertInitList, MyTable>( table, n, "insert(std::initializer_list)", nthread );
541 else
542 #endif
543 DoConcurrentOperations<Insert, MyTable>( table, n, "insert", nthread );
544 ASSERT( MyDataCount == m, NULL );
545 TraverseTable( table, n, m );
546 ParallelTraverseTable( table, n, m );
547 expected_allocs += m;
548 CheckAllocator( table, expected_allocs, expected_frees );
549
550 DoConcurrentOperations<Find, MyTable>( table, n, "find", nthread );
551 ASSERT( MyDataCount == m, NULL );
552 CheckAllocator( table, expected_allocs, expected_frees );
553
554 DoConcurrentOperations<FindConst, MyTable>( table, n, "find(const)", nthread );
555 ASSERT( MyDataCount == m, NULL );
556 CheckAllocator( table, expected_allocs, expected_frees );
557
558 EraseCount = 0;
559 DoConcurrentOperations<Erase, MyTable>( table, n, "erase", nthread );
560 ASSERT( EraseCount == m, NULL );
561 ASSERT( MyDataCount == 0, NULL );
562 TraverseTable( table, n, 0 );
563 expected_frees += m;
564 CheckAllocator( table, expected_allocs, expected_frees );
565
566 bad_hashing = true;
567 table.clear();
568 bad_hashing = false;
569 #if __TBB_INITIALIZER_LISTS_PRESENT
570 }
571 #endif
572
573 if(nthread > 1) {
574 YourTable ie_table;
575 for( int i=0; i<IE_SIZE; ++i )
576 InsertEraseCount[i] = 0;
577 DoConcurrentOperations<InsertErase,YourTable>(ie_table,n/2,"insert_erase",nthread);
578 for( int i=0; i<IE_SIZE; ++i )
579 ASSERT( InsertEraseCount[i]==ie_table.count(MyKey::make(i)), NULL );
580
581 DoConcurrentOperations<InnerInsert,YourTable>(ie_table,2000,"inner insert",nthread);
582 Harness::SpinBarrier barrier(nthread);
583 REMARK("testing erase on fake exclusive accessor\n");
584 NativeParallelFor( nthread, FakeExclusive(barrier, ie_table));
585 }
586 }
587
588 volatile int Counter;
589
590 class AddToTable: NoAssign {
591 MyTable& my_table;
592 const int my_nthread;
593 const int my_m;
594 public:
AddToTable(MyTable & table,int nthread,int m)595 AddToTable( MyTable& table, int nthread, int m ) : my_table(table), my_nthread(nthread), my_m(m) {}
operator ()(int) const596 void operator()( int ) const {
597 for( int i=0; i<my_m; ++i ) {
598 // Busy wait to synchronize threads
599 int j = 0;
600 while( Counter<i ) {
601 if( ++j==1000000 ) {
602 // If Counter<i after a million iterations, then we almost surely have
603 // more logical threads than physical threads, and should yield in
604 // order to let suspended logical threads make progress.
605 j = 0;
606 __TBB_Yield();
607 }
608 }
609 // Now all threads attempt to simultaneously insert a key.
610 int k;
611 {
612 MyTable::accessor a;
613 MyKey key = MyKey::make(i);
614 if( my_table.insert( a, key ) )
615 a->second.set_value( 1 );
616 else
617 a->second.set_value( a->second.value_of()+1 );
618 k = a->second.value_of();
619 }
620 if( k==my_nthread )
621 Counter=i+1;
622 }
623 }
624 };
625
626 class RemoveFromTable: NoAssign {
627 MyTable& my_table;
628 const int my_m;
629 public:
RemoveFromTable(MyTable & table,int m)630 RemoveFromTable( MyTable& table, int m ) : my_table(table), my_m(m) {}
operator ()(int) const631 void operator()(int) const {
632 for( int i=0; i<my_m; ++i ) {
633 bool b;
634 if(i&4) {
635 if(i&8) {
636 MyTable::const_accessor a;
637 b = my_table.find( a, MyKey::make(i) ) && my_table.erase( a );
638 } else {
639 MyTable::accessor a;
640 b = my_table.find( a, MyKey::make(i) ) && my_table.erase( a );
641 }
642 } else
643 b = my_table.erase( MyKey::make(i) );
644 if( b ) ++EraseCount;
645 }
646 }
647 };
648
649 //! Test for memory leak in concurrent_hash_map (TR #153).
TestConcurrency(int nthread)650 void TestConcurrency( int nthread ) {
651 REMARK("testing multiple insertions/deletions of same key with %d threads\n", nthread);
652 {
653 ASSERT( MyDataCount==0, NULL );
654 MyTable table;
655 const int m = 1000;
656 Counter = 0;
657 tbb::tick_count t0 = tbb::tick_count::now();
658 NativeParallelFor( nthread, AddToTable(table,nthread,m) );
659 tbb::tick_count t1 = tbb::tick_count::now();
660 REMARK("time for %u insertions = %g with %d threads\n",unsigned(MyDataCount),(t1-t0).seconds(),nthread);
661 ASSERT( MyDataCount==m, "memory leak detected" );
662
663 EraseCount = 0;
664 t0 = tbb::tick_count::now();
665 NativeParallelFor( nthread, RemoveFromTable(table,m) );
666 t1 = tbb::tick_count::now();
667 REMARK("time for %u deletions = %g with %d threads\n",unsigned(EraseCount),(t1-t0).seconds(),nthread);
668 ASSERT( MyDataCount==0, "memory leak detected" );
669 ASSERT( EraseCount==m, "return value of erase() is broken" );
670
671 CheckAllocator(table, m, m, /*exact*/nthread <= 1);
672 }
673 ASSERT( MyDataCount==0, "memory leak detected" );
674 }
675
TestTypes()676 void TestTypes() {
677 AssertSameType( static_cast<MyTable::key_type*>(0), static_cast<MyKey*>(0) );
678 AssertSameType( static_cast<MyTable::mapped_type*>(0), static_cast<MyData*>(0) );
679 AssertSameType( static_cast<MyTable::value_type*>(0), static_cast<std::pair<const MyKey,MyData>*>(0) );
680 AssertSameType( static_cast<MyTable::accessor::value_type*>(0), static_cast<MyTable::value_type*>(0) );
681 AssertSameType( static_cast<MyTable::const_accessor::value_type*>(0), static_cast<const MyTable::value_type*>(0) );
682 AssertSameType( static_cast<MyTable::size_type*>(0), static_cast<size_t*>(0) );
683 AssertSameType( static_cast<MyTable::difference_type*>(0), static_cast<ptrdiff_t*>(0) );
684 }
685
686 template<typename Iterator, typename T>
TestIteratorTraits()687 void TestIteratorTraits() {
688 AssertSameType( static_cast<typename Iterator::difference_type*>(0), static_cast<ptrdiff_t*>(0) );
689 AssertSameType( static_cast<typename Iterator::value_type*>(0), static_cast<T*>(0) );
690 AssertSameType( static_cast<typename Iterator::pointer*>(0), static_cast<T**>(0) );
691 AssertSameType( static_cast<typename Iterator::iterator_category*>(0), static_cast<std::forward_iterator_tag*>(0) );
692 T x;
693 typename Iterator::reference xr = x;
694 typename Iterator::pointer xp = &x;
695 ASSERT( &xr==xp, NULL );
696 }
697
698 template<typename Iterator1, typename Iterator2>
TestIteratorAssignment(Iterator2 j)699 void TestIteratorAssignment( Iterator2 j ) {
700 Iterator1 i(j), k;
701 ASSERT( i==j, NULL ); ASSERT( !(i!=j), NULL );
702 k = j;
703 ASSERT( k==j, NULL ); ASSERT( !(k!=j), NULL );
704 }
705
706 template<typename Range1, typename Range2>
TestRangeAssignment(Range2 r2)707 void TestRangeAssignment( Range2 r2 ) {
708 Range1 r1(r2); r1 = r2;
709 }
710 //------------------------------------------------------------------------
711 // Test for copy constructor and assignment
712 //------------------------------------------------------------------------
713
714 template<typename MyTable>
FillTable(MyTable & x,int n)715 static void FillTable( MyTable& x, int n ) {
716 for( int i=1; i<=n; ++i ) {
717 MyKey key( MyKey::make(-i) ); // hash values must not be specified in direct order
718 typename MyTable::accessor a;
719 bool b = x.insert(a,key);
720 ASSERT(b, NULL);
721 a->second.set_value( i*i );
722 }
723 }
724
725 template<typename MyTable>
CheckTable(const MyTable & x,int n)726 static void CheckTable( const MyTable& x, int n ) {
727 ASSERT( x.size()==size_t(n), "table is different size than expected" );
728 ASSERT( x.empty()==(n==0), NULL );
729 ASSERT( x.size()<=x.max_size(), NULL );
730 for( int i=1; i<=n; ++i ) {
731 MyKey key( MyKey::make(-i) );
732 typename MyTable::const_accessor a;
733 bool b = x.find(a,key);
734 ASSERT( b, NULL );
735 ASSERT( a->second.value_of()==i*i, NULL );
736 }
737 int count = 0;
738 int key_sum = 0;
739 for( typename MyTable::const_iterator i(x.begin()); i!=x.end(); ++i ) {
740 ++count;
741 key_sum += -i->first.value_of();
742 }
743 ASSERT( count==n, NULL );
744 ASSERT( key_sum==n*(n+1)/2, NULL );
745 }
746
TestCopy()747 static void TestCopy() {
748 REMARK("testing copy\n");
749 MyTable t1;
750 for( int i=0; i<10000; i=(i<100 ? i+1 : i*3) ) {
751 MyDataCount = 0;
752
753 FillTable(t1,i);
754 // Do not call CheckTable(t1,i) before copying, it enforces rehashing
755
756 MyTable t2(t1);
757 // Check that copy constructor did not mangle source table.
758 CheckTable(t1,i);
759 swap(t1, t2);
760 CheckTable(t1,i);
761 ASSERT( !(t1 != t2), NULL );
762
763 // Clear original table
764 t2.clear();
765 swap(t2, t1);
766 CheckTable(t1,0);
767
768 // Verify that copy of t1 is correct, even after t1 is cleared.
769 CheckTable(t2,i);
770 t2.clear();
771 t1.swap( t2 );
772 CheckTable(t1,0);
773 CheckTable(t2,0);
774 ASSERT( MyDataCount==0, "data leak?" );
775 }
776 }
777
TestAssignment()778 void TestAssignment() {
779 REMARK("testing assignment\n");
780 for( int i=0; i<1000; i=(i<30 ? i+1 : i*5) ) {
781 for( int j=0; j<1000; j=(j<30 ? j+1 : j*7) ) {
782 MyTable t1;
783 MyTable t2;
784 FillTable(t1,i);
785 FillTable(t2,j);
786 ASSERT( (t1 == t2) == (i == j), NULL );
787 CheckTable(t2,j);
788
789 MyTable& tref = t2=t1;
790 ASSERT( &tref==&t2, NULL );
791 ASSERT( t1 == t2, NULL );
792 CheckTable(t1,i);
793 CheckTable(t2,i);
794
795 t1.clear();
796 CheckTable(t1,0);
797 CheckTable(t2,i);
798 ASSERT( MyDataCount==i, "data leak?" );
799
800 t2.clear();
801 CheckTable(t1,0);
802 CheckTable(t2,0);
803 ASSERT( MyDataCount==0, "data leak?" );
804 }
805 }
806 }
807
TestIteratorsAndRanges()808 void TestIteratorsAndRanges() {
809 REMARK("testing iterators compliance\n");
810 TestIteratorTraits<MyTable::iterator,MyTable::value_type>();
811 TestIteratorTraits<MyTable::const_iterator,const MyTable::value_type>();
812
813 MyTable v;
814 MyTable const &u = v;
815
816 TestIteratorAssignment<MyTable::const_iterator>( u.begin() );
817 TestIteratorAssignment<MyTable::const_iterator>( v.begin() );
818 TestIteratorAssignment<MyTable::iterator>( v.begin() );
819 // doesn't compile as expected: TestIteratorAssignment<typename V::iterator>( u.begin() );
820
821 // check for non-existing
822 ASSERT(v.equal_range(MyKey::make(-1)) == std::make_pair(v.end(), v.end()), NULL);
823 ASSERT(u.equal_range(MyKey::make(-1)) == std::make_pair(u.end(), u.end()), NULL);
824
825 REMARK("testing ranges compliance\n");
826 TestRangeAssignment<MyTable::const_range_type>( u.range() );
827 TestRangeAssignment<MyTable::const_range_type>( v.range() );
828 TestRangeAssignment<MyTable::range_type>( v.range() );
829 // doesn't compile as expected: TestRangeAssignment<typename V::range_type>( u.range() );
830
831 REMARK("testing construction and insertion from iterators range\n");
832 FillTable( v, 1000 );
833 MyTable2 t(v.begin(), v.end());
834 v.rehash();
835 CheckTable(t, 1000);
836 t.insert(v.begin(), v.end()); // do nothing
837 CheckTable(t, 1000);
838 t.clear();
839 t.insert(v.begin(), v.end()); // restore
840 CheckTable(t, 1000);
841
842 REMARK("testing comparison\n");
843 typedef tbb::concurrent_hash_map<MyKey,MyData2,YourHashCompare,MyAllocator> YourTable1;
844 typedef tbb::concurrent_hash_map<MyKey,MyData2,YourHashCompare> YourTable2;
845 YourTable1 t1;
846 FillTable( t1, 10 );
847 CheckTable(t1, 10 );
848 YourTable2 t2(t1.begin(), t1.end());
849 MyKey key( MyKey::make(-5) ); MyData2 data;
850 ASSERT(t2.erase(key), NULL);
851 YourTable2::accessor a;
852 ASSERT(t2.insert(a, key), NULL);
853 data.set_value(0); a->second = data;
854 ASSERT( t1 != t2, NULL);
855 data.set_value(5*5); a->second = data;
856 ASSERT( t1 == t2, NULL);
857 }
858
TestRehash()859 void TestRehash() {
860 REMARK("testing rehashing\n");
861 MyTable w;
862 w.insert( std::make_pair(MyKey::make(-5), MyData()) );
863 w.rehash(); // without this, assertion will fail
864 MyTable::iterator it = w.begin();
865 int i = 0; // check for non-rehashed buckets
866 for( ; it != w.end(); i++ )
867 w.count( (it++)->first );
868 ASSERT( i == 1, NULL );
869 for( i=0; i<1000; i=(i<29 ? i+1 : i*2) ) {
870 for( int j=max(256+i, i*2); j<10000; j*=3 ) {
871 MyTable v;
872 FillTable( v, i );
873 ASSERT(int(v.size()) == i, NULL);
874 ASSERT(int(v.bucket_count()) <= j, NULL);
875 v.rehash( j );
876 ASSERT(int(v.bucket_count()) >= j, NULL);
877 CheckTable( v, i );
878 }
879 }
880 }
881
882 template<typename base_alloc_t, typename count_t = tbb::atomic<size_t> >
883 class only_node_counting_allocator : public local_counting_allocator<base_alloc_t, count_t> {
884 typedef local_counting_allocator<base_alloc_t, count_t> base_type;
885 public:
886 template<typename U>
887 struct rebind {
888 typedef only_node_counting_allocator<typename base_alloc_t::template rebind<U>::other,count_t> other;
889 };
890
only_node_counting_allocator()891 only_node_counting_allocator() : base_type() {}
only_node_counting_allocator(const only_node_counting_allocator & a)892 only_node_counting_allocator(const only_node_counting_allocator& a) : base_type(a) {}
893
894 template<typename U>
only_node_counting_allocator(const only_node_counting_allocator<U> & a)895 only_node_counting_allocator(const only_node_counting_allocator<U>& a) : base_type(a) {}
896
allocate(const typename base_type::size_type n)897 typename base_type::pointer allocate(const typename base_type::size_type n) {
898 if ( n > 1) {
899 return base_alloc_t::allocate(n);
900 } else {
901 return base_type::allocate(n);
902 }
903 }
904 };
905
906 #if TBB_USE_EXCEPTIONS
TestExceptions()907 void TestExceptions() {
908 typedef only_node_counting_allocator<tbb::tbb_allocator<MyData2> > allocator_t;
909 typedef tbb::concurrent_hash_map<MyKey,MyData2,MyHashCompare,allocator_t> ThrowingTable;
910 enum methods {
911 zero_method = 0,
912 ctor_copy, op_assign, op_insert,
913 all_methods
914 };
915 REMARK("testing exception-safety guarantees\n");
916 ThrowingTable src;
917 FillTable( src, 1000 );
918 ASSERT( MyDataCount==1000, NULL );
919
920 try {
921 for(int t = 0; t < 2; t++) // exception type
922 for(int m = zero_method+1; m < all_methods; m++)
923 {
924 allocator_t a;
925 if(t) MyDataCountLimit = 101;
926 else a.set_limits(101);
927 ThrowingTable victim(a);
928 MyDataCount = 0;
929
930 try {
931 switch(m) {
932 case ctor_copy: {
933 ThrowingTable acopy(src, a);
934 } break;
935 case op_assign: {
936 victim = src;
937 } break;
938 case op_insert: {
939 #if __TBB_CPP11_RVALUE_REF_PRESENT && __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT && __TBB_CPP11_TUPLE_PRESENT
940 // Insertion in cpp11 don't make copy constructions
941 // during the insertion, so we need to decrement limit
942 // to throw an exception in the right place and to prevent
943 // successful insertion of one unexpected item
944 if (MyDataCountLimit)
945 --MyDataCountLimit;
946 #endif
947 FillTable( victim, 1000 );
948 } break;
949 default:;
950 }
951 ASSERT(false, "should throw an exception");
952 } catch(std::bad_alloc &e) {
953 MyDataCountLimit = 0;
954 size_t size = victim.size();
955 switch(m) {
956 case op_assign:
957 ASSERT( MyDataCount==100, "data leak?" );
958 ASSERT( size>=100, NULL );
959 CheckAllocator(victim, 100+t, t);
960 __TBB_fallthrough;
961 case ctor_copy:
962 CheckTable(src, 1000);
963 break;
964 case op_insert:
965 ASSERT( size==size_t(100-t), NULL );
966 ASSERT( MyDataCount==100-t, "data leak?" );
967 CheckTable(victim, 100-t);
968 CheckAllocator(victim, 100, t);
969 break;
970
971 default:; // nothing to check here
972 }
973 REMARK("Exception %d: %s\t- ok ()\n", m, e.what());
974 }
975 catch ( ... ) {
976 ASSERT ( __TBB_EXCEPTION_TYPE_INFO_BROKEN, "Unrecognized exception" );
977 }
978 }
979 } catch(...) {
980 ASSERT(false, "unexpected exception");
981 }
982 src.clear(); MyDataCount = 0;
983 }
984 #endif /* TBB_USE_EXCEPTIONS */
985
986
987 #if __TBB_INITIALIZER_LISTS_PRESENT
988 #include "test_initializer_list.h"
989
990 struct test_insert {
991 template<typename container_type, typename element_type>
do_testtest_insert992 static void do_test( std::initializer_list<element_type> il, container_type const& expected ) {
993 container_type vd;
994 vd.insert( il );
995 ASSERT( vd == expected, "inserting with an initializer list failed" );
996 }
997 };
998
TestInitList()999 void TestInitList(){
1000 using namespace initializer_list_support_tests;
1001 REMARK("testing initializer_list methods \n");
1002
1003 typedef tbb::concurrent_hash_map<int,int> ch_map_type;
1004 std::initializer_list<ch_map_type::value_type> pairs_il = {{1,1},{2,2},{3,3},{4,4},{5,5}};
1005
1006 TestInitListSupportWithoutAssign<ch_map_type, test_insert>( pairs_il );
1007 TestInitListSupportWithoutAssign<ch_map_type, test_insert>( {} );
1008 }
1009 #endif //if __TBB_INITIALIZER_LISTS_PRESENT
1010
1011 #if __TBB_RANGE_BASED_FOR_PRESENT
1012 #include "test_range_based_for.h"
1013
TestRangeBasedFor()1014 void TestRangeBasedFor(){
1015 using namespace range_based_for_support_tests;
1016
1017 REMARK("testing range based for loop compatibility \n");
1018 typedef tbb::concurrent_hash_map<int,int> ch_map;
1019 ch_map a_ch_map;
1020
1021 const int sequence_length = 100;
1022 for (int i = 1; i <= sequence_length; ++i){
1023 a_ch_map.insert(ch_map::value_type(i,i));
1024 }
1025
1026 ASSERT( range_based_for_accumulate(a_ch_map, pair_second_summer(), 0) == gauss_summ_of_int_sequence(sequence_length), "incorrect accumulated value generated via range based for ?");
1027 }
1028 #endif //if __TBB_RANGE_BASED_FOR_PRESENT
1029
1030 #include "harness_defs.h"
1031
1032 // The helper to run a test only when a default construction is present.
1033 template <bool default_construction_present> struct do_default_construction_test {
operator ()do_default_construction_test1034 template<typename FuncType> void operator() ( FuncType func ) const { func(); }
1035 };
1036 template <> struct do_default_construction_test<false> {
operator ()do_default_construction_test1037 template<typename FuncType> void operator()( FuncType ) const {}
1038 };
1039
1040 template <typename Table>
1041 class test_insert_by_key : NoAssign {
1042 typedef typename Table::value_type value_type;
1043 Table &my_c;
1044 const value_type &my_value;
1045 public:
test_insert_by_key(Table & c,const value_type & value)1046 test_insert_by_key( Table &c, const value_type &value ) : my_c(c), my_value(value) {}
operator ()() const1047 void operator()() const {
1048 {
1049 typename Table::accessor a;
1050 ASSERT( my_c.insert( a, my_value.first ), NULL );
1051 ASSERT( Harness::IsEqual()(a->first, my_value.first), NULL );
1052 a->second = my_value.second;
1053 } {
1054 typename Table::const_accessor ca;
1055 ASSERT( !my_c.insert( ca, my_value.first ), NULL );
1056 ASSERT( Harness::IsEqual()(ca->first, my_value.first), NULL);
1057 ASSERT( Harness::IsEqual()(ca->second, my_value.second), NULL);
1058 }
1059 }
1060 };
1061
1062 #include <vector>
1063 #include <list>
1064 #include <algorithm>
1065 #if __TBB_CPP11_REFERENCE_WRAPPER_PRESENT
1066 #include <functional>
1067 #endif
1068
1069 template <typename Table, typename Iterator, typename Range = typename Table::range_type>
1070 class test_range : NoAssign {
1071 typedef typename Table::value_type value_type;
1072 Table &my_c;
1073 const std::list<value_type> &my_lst;
1074 std::vector< tbb::atomic<bool> >& my_marks;
1075 public:
test_range(Table & c,const std::list<value_type> & lst,std::vector<tbb::atomic<bool>> & marks)1076 test_range( Table &c, const std::list<value_type> &lst, std::vector< tbb::atomic<bool> > &marks ) : my_c(c), my_lst(lst), my_marks(marks) {
1077 std::fill( my_marks.begin(), my_marks.end(), false );
1078 }
operator ()(const Range & r) const1079 void operator()( const Range &r ) const { do_test_range( r.begin(), r.end() ); }
do_test_range(Iterator i,Iterator j) const1080 void do_test_range( Iterator i, Iterator j ) const {
1081 for ( Iterator it = i; it != j; ) {
1082 Iterator it_prev = it++;
1083 typename std::list<value_type>::const_iterator it2 = std::search( my_lst.begin(), my_lst.end(), it_prev, it, Harness::IsEqual() );
1084 ASSERT( it2 != my_lst.end(), NULL );
1085 typename std::list<value_type>::difference_type dist = std::distance( my_lst.begin(), it2 );
1086 ASSERT( !my_marks[dist], NULL );
1087 my_marks[dist] = true;
1088 }
1089 }
1090 };
1091
1092 template <bool default_construction_present, typename Table>
1093 class check_value : NoAssign {
1094 typedef typename Table::const_iterator const_iterator;
1095 typedef typename Table::iterator iterator;
1096 typedef typename Table::size_type size_type;
1097 Table &my_c;
1098 public:
check_value(Table & c)1099 check_value( Table &c ) : my_c(c) {}
operator ()(const typename Table::value_type & value)1100 void operator()(const typename Table::value_type &value ) {
1101 const Table &const_c = my_c;
1102 ASSERT( my_c.count( value.first ) == 1, NULL );
1103 { // tests with a const accessor.
1104 typename Table::const_accessor ca;
1105 // find
1106 ASSERT( my_c.find( ca, value.first ), NULL);
1107 ASSERT( !ca.empty() , NULL);
1108 ASSERT( Harness::IsEqual()(ca->first, value.first), NULL );
1109 ASSERT( Harness::IsEqual()(ca->second, value.second), NULL );
1110 // erase
1111 ASSERT( my_c.erase( ca ), NULL );
1112 ASSERT( my_c.count( value.first ) == 0, NULL );
1113 // insert (pair)
1114 ASSERT( my_c.insert( ca, value ), NULL);
1115 ASSERT( Harness::IsEqual()(ca->first, value.first), NULL );
1116 ASSERT( Harness::IsEqual()(ca->second, value.second), NULL );
1117 } { // tests with a non-const accessor.
1118 typename Table::accessor a;
1119 // find
1120 ASSERT( my_c.find( a, value.first ), NULL);
1121 ASSERT( !a.empty() , NULL);
1122 ASSERT( Harness::IsEqual()(a->first, value.first), NULL );
1123 ASSERT( Harness::IsEqual()(a->second, value.second), NULL );
1124 // erase
1125 ASSERT( my_c.erase( a ), NULL );
1126 ASSERT( my_c.count( value.first ) == 0, NULL );
1127 // insert
1128 ASSERT( my_c.insert( a, value ), NULL);
1129 ASSERT( Harness::IsEqual()(a->first, value.first), NULL );
1130 ASSERT( Harness::IsEqual()(a->second, value.second), NULL );
1131 }
1132 // erase by key
1133 ASSERT( my_c.erase( value.first ), NULL );
1134 ASSERT( my_c.count( value.first ) == 0, NULL );
1135 do_default_construction_test<default_construction_present>()(test_insert_by_key<Table>( my_c, value ));
1136 // insert by value
1137 ASSERT( my_c.insert( value ) != default_construction_present, NULL );
1138 // equal_range
1139 std::pair<iterator,iterator> r1 = my_c.equal_range( value.first );
1140 iterator r1_first_prev = r1.first++;
1141 ASSERT( Harness::IsEqual()( *r1_first_prev, value ) && Harness::IsEqual()( r1.first, r1.second ), NULL );
1142 std::pair<const_iterator,const_iterator> r2 = const_c.equal_range( value.first );
1143 const_iterator r2_first_prev = r2.first++;
1144 ASSERT( Harness::IsEqual()( *r2_first_prev, value ) && Harness::IsEqual()( r2.first, r2.second ), NULL );
1145 }
1146 };
1147
1148 #include "tbb/task_scheduler_init.h"
1149
1150 template <typename Value, typename U = Value>
1151 struct CompareTables {
1152 template <typename T>
IsEqualCompareTables1153 static bool IsEqual( const T& t1, const T& t2 ) {
1154 return (t1 == t2) && !(t1 != t2);
1155 }
1156 };
1157
1158 #if __TBB_CPP11_SMART_POINTERS_PRESENT
1159 template <typename U>
1160 struct CompareTables< std::pair<const std::weak_ptr<U>, std::weak_ptr<U> > > {
1161 template <typename T>
IsEqualCompareTables1162 static bool IsEqual( const T&, const T& ) {
1163 /* do nothing for std::weak_ptr */
1164 return true;
1165 }
1166 };
1167 #endif /* __TBB_CPP11_SMART_POINTERS_PRESENT */
1168
1169 template <bool default_construction_present, typename Table>
Examine(Table c,const std::list<typename Table::value_type> & lst)1170 void Examine( Table c, const std::list<typename Table::value_type> &lst) {
1171 typedef const Table const_table;
1172 typedef typename Table::const_iterator const_iterator;
1173 typedef typename Table::iterator iterator;
1174 typedef typename Table::value_type value_type;
1175 typedef typename Table::size_type size_type;
1176
1177 ASSERT( !c.empty(), NULL );
1178 ASSERT( c.size() == lst.size(), NULL );
1179 ASSERT( c.max_size() >= c.size(), NULL );
1180
1181 const check_value<default_construction_present,Table> cv(c);
1182 std::for_each( lst.begin(), lst.end(), cv );
1183
1184 std::vector< tbb::atomic<bool> > marks( lst.size() );
1185
1186 test_range<Table,iterator>( c, lst, marks ).do_test_range( c.begin(), c.end() );
1187 ASSERT( std::find( marks.begin(), marks.end(), false ) == marks.end(), NULL );
1188
1189 test_range<const_table,const_iterator>( c, lst, marks ).do_test_range( c.begin(), c.end() );
1190 ASSERT( std::find( marks.begin(), marks.end(), false ) == marks.end(), NULL );
1191
1192 tbb::task_scheduler_init init;
1193
1194 typedef typename Table::range_type range_type;
1195 tbb::parallel_for( c.range(), test_range<Table,typename range_type::iterator,range_type>( c, lst, marks ) );
1196 ASSERT( std::find( marks.begin(), marks.end(), false ) == marks.end(), NULL );
1197
1198 const_table const_c = c;
1199 ASSERT( CompareTables<value_type>::IsEqual( c, const_c ), NULL );
1200
1201 typedef typename const_table::const_range_type const_range_type;
1202 tbb::parallel_for( c.range(), test_range<const_table,typename const_range_type::iterator,const_range_type>( const_c, lst, marks ) );
1203 ASSERT( std::find( marks.begin(), marks.end(), false ) == marks.end(), NULL );
1204
1205 const size_type new_bucket_count = 2*c.bucket_count();
1206 c.rehash( new_bucket_count );
1207 ASSERT( c.bucket_count() >= new_bucket_count, NULL );
1208
1209 Table c2;
1210 typename std::list<value_type>::const_iterator begin5 = lst.begin();
1211 std::advance( begin5, 5 );
1212 c2.insert( lst.begin(), begin5 );
1213 std::for_each( lst.begin(), begin5, check_value<default_construction_present, Table>( c2 ) );
1214
1215 c2.swap( c );
1216 ASSERT( CompareTables<value_type>::IsEqual( c2, const_c ), NULL );
1217 ASSERT( c.size() == 5, NULL );
1218 std::for_each( lst.begin(), lst.end(), check_value<default_construction_present,Table>(c2) );
1219
1220 tbb::swap( c, c2 );
1221 ASSERT( CompareTables<value_type>::IsEqual( c, const_c ), NULL );
1222 ASSERT( c2.size() == 5, NULL );
1223
1224 c2.clear();
1225 ASSERT( CompareTables<value_type>::IsEqual( c2, Table() ), NULL );
1226
1227 typename Table::allocator_type a = c.get_allocator();
1228 value_type *ptr = a.allocate(1);
1229 ASSERT( ptr, NULL );
1230 a.deallocate( ptr, 1 );
1231 }
1232
1233 template<typename T>
1234 struct debug_hash_compare : tbb::tbb_hash_compare<T> {};
1235
1236 template <bool default_construction_present, typename Value>
TypeTester(const std::list<Value> & lst)1237 void TypeTester( const std::list<Value> &lst ) {
1238 __TBB_ASSERT( lst.size() >= 5, "Array should have at least 5 elements" );
1239 typedef typename Value::first_type first_type;
1240 typedef typename Value::second_type second_type;
1241 typedef tbb::concurrent_hash_map<first_type,second_type> ch_map;
1242 debug_hash_compare<first_type> compare;
1243 // Construct an empty hash map.
1244 ch_map c1;
1245 c1.insert( lst.begin(), lst.end() );
1246 Examine<default_construction_present>( c1, lst );
1247 #if __TBB_INITIALIZER_LISTS_PRESENT && !__TBB_CPP11_INIT_LIST_TEMP_OBJS_LIFETIME_BROKEN
1248 // Constructor from initializer_list.
1249 typename std::list<Value>::const_iterator it = lst.begin();
1250 std::initializer_list<Value> il = { *it++, *it++, *it++ };
1251 ch_map c2( il );
1252 c2.insert( it, lst.end() );
1253 Examine<default_construction_present>( c2, lst );
1254
1255 // Constructor from initializer_list and compare object
1256 ch_map c3( il, compare);
1257 c3.insert( it, lst.end() );
1258 Examine<default_construction_present>( c3, lst );
1259
1260 // Constructor from initializer_list, compare object and allocator
1261 ch_map c4( il, compare, typename ch_map::allocator_type());
1262 c4.insert( it, lst.end());
1263 Examine<default_construction_present>( c4, lst );
1264 #endif
1265 // Copying constructor.
1266 ch_map c5(c1);
1267 Examine<default_construction_present>( c5, lst );
1268 // Construct with non-default allocator
1269 typedef tbb::concurrent_hash_map< first_type,second_type,tbb::tbb_hash_compare<first_type>,debug_allocator<Value> > ch_map_debug_alloc;
1270 ch_map_debug_alloc c6;
1271 c6.insert( lst.begin(), lst.end() );
1272 Examine<default_construction_present>( c6, lst );
1273 // Copying constructor
1274 ch_map_debug_alloc c7(c6);
1275 Examine<default_construction_present>( c7, lst );
1276 // Construction empty table with n preallocated buckets.
1277 ch_map c8( lst.size() );
1278 c8.insert( lst.begin(), lst.end() );
1279 Examine<default_construction_present>( c8, lst );
1280 ch_map_debug_alloc c9( lst.size() );
1281 c9.insert( lst.begin(), lst.end() );
1282 Examine<default_construction_present>( c9, lst );
1283 // Construction with copying iteration range.
1284 ch_map c10( c1.begin(), c1.end() );
1285 Examine<default_construction_present>( c10, lst );
1286 // Construction with copying iteration range and given allocator instance.
1287 debug_allocator<Value> allocator;
1288 ch_map_debug_alloc c11( lst.begin(), lst.end(), allocator );
1289 Examine<default_construction_present>( c11, lst );
1290
1291 typedef tbb::concurrent_hash_map< first_type,second_type,debug_hash_compare<first_type>,typename ch_map::allocator_type> ch_map_debug_hash;
1292
1293 // Constructor with two iterators and hash_compare
1294 ch_map_debug_hash c12(c1.begin(), c1.end(), compare);
1295 Examine<default_construction_present>( c12, lst );
1296
1297 ch_map_debug_hash c13(c1.begin(), c1.end(), compare, typename ch_map::allocator_type());
1298 Examine<default_construction_present>( c13, lst );
1299 }
1300
1301 #if __TBB_CPP11_SMART_POINTERS_PRESENT
1302 namespace tbb {
1303 template<> struct tbb_hash_compare< const std::shared_ptr<int> > {
hashtbb::tbb_hash_compare1304 static size_t hash( const std::shared_ptr<int>& ptr ) { return static_cast<size_t>( *ptr ) * interface5::internal::hash_multiplier; }
equaltbb::tbb_hash_compare1305 static bool equal( const std::shared_ptr<int>& ptr1, const std::shared_ptr<int>& ptr2 ) { return ptr1 == ptr2; }
1306 };
1307 template<> struct tbb_hash_compare< const std::weak_ptr<int> > {
hashtbb::tbb_hash_compare1308 static size_t hash( const std::weak_ptr<int>& ptr ) { return static_cast<size_t>( *ptr.lock() ) * interface5::internal::hash_multiplier; }
equaltbb::tbb_hash_compare1309 static bool equal( const std::weak_ptr<int>& ptr1, const std::weak_ptr<int>& ptr2 ) { return ptr1.lock() == ptr2.lock(); }
1310 };
1311 }
1312 #endif /* __TBB_CPP11_SMART_POINTERS_PRESENT */
1313
TestCPP11Types()1314 void TestCPP11Types() {
1315 const int NUMBER = 10;
1316
1317 typedef std::pair<const int, int> int_int_t;
1318 std::list<int_int_t> arrIntInt;
1319 for ( int i=0; i<NUMBER; ++i ) arrIntInt.push_back( int_int_t(i, NUMBER-i) );
1320 TypeTester</*default_construction_present = */true>( arrIntInt );
1321
1322 #if __TBB_CPP11_REFERENCE_WRAPPER_PRESENT && !__TBB_REFERENCE_WRAPPER_COMPILATION_BROKEN
1323 typedef std::pair<const std::reference_wrapper<const int>, int> ref_int_t;
1324 std::list<ref_int_t> arrRefInt;
1325 for ( std::list<int_int_t>::iterator it = arrIntInt.begin(); it != arrIntInt.end(); ++it )
1326 arrRefInt.push_back( ref_int_t( it->first, it->second ) );
1327 TypeTester</*default_construction_present = */true>( arrRefInt );
1328
1329 typedef std::pair< const int, std::reference_wrapper<int> > int_ref_t;
1330 std::list<int_ref_t> arrIntRef;
1331 for ( std::list<int_int_t>::iterator it = arrIntInt.begin(); it != arrIntInt.end(); ++it )
1332 arrIntRef.push_back( int_ref_t( it->first, it->second ) );
1333 TypeTester</*default_construction_present = */false>( arrIntRef );
1334 #else
1335 REPORT("Known issue: C++11 reference wrapper tests are skipped.\n");
1336 #endif /* __TBB_CPP11_REFERENCE_WRAPPER_PRESENT && !__TBB_REFERENCE_WRAPPER_COMPILATION_BROKEN*/
1337
1338 typedef std::pair< const int, tbb::atomic<int> > int_tbb_t;
1339 std::list<int_tbb_t> arrIntTbb;
1340 for ( int i=0; i<NUMBER; ++i ) {
1341 tbb::atomic<int> b;
1342 b = NUMBER-i;
1343 arrIntTbb.push_back( int_tbb_t(i, b) );
1344 }
1345 TypeTester</*default_construction_present = */true>( arrIntTbb );
1346
1347 #if __TBB_CPP11_SMART_POINTERS_PRESENT
1348 typedef std::pair< const std::shared_ptr<int>, std::shared_ptr<int> > shr_shr_t;
1349 std::list<shr_shr_t> arrShrShr;
1350 for ( int i=0; i<NUMBER; ++i ) {
1351 const int NUMBER_minus_i = NUMBER - i;
1352 arrShrShr.push_back( shr_shr_t( std::make_shared<int>(i), std::make_shared<int>(NUMBER_minus_i) ) );
1353 }
1354 TypeTester< /*default_construction_present = */true>( arrShrShr );
1355
1356 typedef std::pair< const std::weak_ptr<int>, std::weak_ptr<int> > wk_wk_t;
1357 std::list< wk_wk_t > arrWkWk;
1358 std::copy( arrShrShr.begin(), arrShrShr.end(), std::back_inserter(arrWkWk) );
1359 TypeTester< /*default_construction_present = */true>( arrWkWk );
1360 #else
1361 REPORT("Known issue: C++11 smart pointer tests are skipped.\n");
1362 #endif /* __TBB_CPP11_SMART_POINTERS_PRESENT */
1363 }
1364
1365 #if __TBB_CPP11_RVALUE_REF_PRESENT
1366
1367 struct hash_map_move_traits : default_container_traits {
1368 enum{ expected_number_of_items_to_allocate_for_steal_move = 0 };
1369
1370 template<typename T>
1371 struct hash_compare {
equalhash_map_move_traits::hash_compare1372 bool equal( const T& lhs, const T& rhs ) const {
1373 return lhs==rhs;
1374 }
hashhash_map_move_traits::hash_compare1375 size_t hash( const T& k ) const {
1376 return tbb::tbb_hasher(k);
1377 }
1378 };
1379 template<typename element_type, typename allocator_type>
1380 struct apply {
1381 typedef tbb::concurrent_hash_map<element_type, element_type, hash_compare<element_type>, allocator_type > type;
1382 };
1383
1384 typedef FooPairIterator init_iterator_type;
1385 template<typename hash_map_type, typename iterator>
equalhash_map_move_traits1386 static bool equal(hash_map_type const& c, iterator begin, iterator end){
1387 bool equal_sizes = ( static_cast<size_t>(std::distance(begin, end)) == c.size() );
1388 if (!equal_sizes)
1389 return false;
1390
1391 for (iterator it = begin; it != end; ++it ){
1392 if (c.count( (*it).first) == 0){
1393 return false;
1394 }
1395 }
1396 return true;
1397 }
1398 };
1399
TestMoveSupport()1400 void TestMoveSupport(){
1401 TestMoveConstructor<hash_map_move_traits>();
1402 TestConstructorWithMoveIterators<hash_map_move_traits>();
1403 TestMoveAssignOperator<hash_map_move_traits>();
1404 #if TBB_USE_EXCEPTIONS
1405 TestExceptionSafetyGuaranteesMoveConstructorWithUnEqualAllocatorMemoryFailure<hash_map_move_traits>();
1406 TestExceptionSafetyGuaranteesMoveConstructorWithUnEqualAllocatorExceptionInElementCtor<hash_map_move_traits>();
1407 #else
1408 REPORT("Known issue: exception safety tests for C++11 move semantics support are skipped.\n");
1409 #endif //TBB_USE_EXCEPTIONS
1410 }
1411 #else
TestMoveSupport()1412 void TestMoveSupport(){
1413 REPORT("Known issue: tests for C++11 move semantics support are skipped.\n");
1414 }
1415 #endif //__TBB_CPP11_RVALUE_REF_PRESENT
1416
1417 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
1418
1419 template <template <typename...> typename TMap>
TestDeductionGuides()1420 void TestDeductionGuides() {
1421 using Key = int;
1422 using Value = std::string;
1423
1424 using ComplexType = std::pair<Key, Value>;
1425 using ComplexTypeConst = std::pair<const Key, Value>;
1426
1427 using DefaultCompare = tbb::tbb_hash_compare<Key>;
1428 using Compare = debug_hash_compare<Key>;
1429 using DefaultAllocator = tbb::tbb_allocator<ComplexTypeConst>;
1430 using Allocator = std::allocator<ComplexType>;
1431
1432 std::vector<ComplexType> v;
1433 auto l = { ComplexTypeConst(1, "one"), ComplexTypeConst(2, "two") };
1434 Compare compare;
1435 Allocator allocator;
1436
1437 // check TMap(InputIterator, InputIterator)
1438 TMap m1(v.begin(), v.end());
1439 static_assert(std::is_same<decltype(m1), TMap<Key, Value, DefaultCompare, DefaultAllocator>>::value);
1440
1441 // check TMap(InputIterator, InputIterator, HashCompare)
1442 TMap m2(v.begin(), v.end(), compare);
1443 static_assert(std::is_same<decltype(m2), TMap<Key, Value, Compare>>::value);
1444
1445 // check TMap(InputIterator, InputIterator, HashCompare, Allocator)
1446 TMap m3(v.begin(), v.end(), compare, allocator);
1447 static_assert(std::is_same<decltype(m3), TMap<Key, Value, Compare, Allocator>>::value);
1448
1449 // check TMap(InputIterator, InputIterator, Allocator)
1450 TMap m4(v.begin(), v.end(), allocator);
1451 static_assert(std::is_same<decltype(m4), TMap<Key, Value, DefaultCompare, Allocator>>::value);
1452
1453 // check TMap(std::initializer_list)
1454 TMap m5(l);
1455 static_assert(std::is_same<decltype(m5), TMap<Key, Value, DefaultCompare, DefaultAllocator>>::value);
1456
1457 // check TMap(std::initializer_list, HashCompare)
1458 TMap m6(l, compare);
1459 static_assert(std::is_same<decltype(m6), TMap<Key, Value, Compare, DefaultAllocator>>::value);
1460
1461 // check TMap(std::initializer_list, HashCompare, Allocator)
1462 TMap m7(l, compare, allocator);
1463 static_assert(std::is_same<decltype(m7), TMap<Key, Value, Compare, Allocator>>::value);
1464
1465 // check TMap(std::initializer_list, Allocator)
1466 TMap m8(l, allocator);
1467 static_assert(std::is_same<decltype(m8), TMap<Key, Value, DefaultCompare, Allocator>>::value);
1468
1469 // check TMap(TMap &)
1470 TMap m9(m1);
1471 static_assert(std::is_same<decltype(m9), decltype(m1)>::value);
1472
1473 // check TMap(TMap &, Allocator)
1474 TMap m10(m4, allocator);
1475 static_assert(std::is_same<decltype(m10), decltype(m4)>::value);
1476
1477 // check TMap(TMap &&)
1478 TMap m11(std::move(m1));
1479 static_assert(std::is_same<decltype(m11), decltype(m1)>::value);
1480
1481 // check TMap(TMap &&, Allocator)
1482 TMap m12(std::move(m4), allocator);
1483 static_assert(std::is_same<decltype(m12), decltype(m4)>::value);
1484 }
1485 #endif // __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
1486
1487 template<typename Key>
1488 struct non_default_constructible_hash_compare : tbb::tbb_hash_compare<Key> {
non_default_constructible_hash_comparenon_default_constructible_hash_compare1489 non_default_constructible_hash_compare() {
1490 ASSERT(false, "Hash compare object must not default construct during the construction of hash_map with compare argument");
1491 }
1492
non_default_constructible_hash_comparenon_default_constructible_hash_compare1493 non_default_constructible_hash_compare(int) {}
1494 };
1495
TestHashCompareConstructors()1496 void TestHashCompareConstructors() {
1497 typedef int key_type;
1498 typedef tbb::concurrent_hash_map<key_type, key_type, non_default_constructible_hash_compare<key_type> > map_type;
1499
1500 non_default_constructible_hash_compare<key_type> compare(0);
1501 map_type::allocator_type allocator;
1502
1503 map_type map1(compare);
1504 map_type map2(compare, allocator);
1505
1506 map_type map3(1, compare);
1507 map_type map4(1, compare, allocator);
1508
1509 std::vector<map_type::value_type> reference_vector;
1510 map_type map5(reference_vector.begin(), reference_vector.end(), compare);
1511 map_type map6(reference_vector.begin(), reference_vector.end(), compare, allocator);
1512
1513 #if __TBB_INITIALIZER_LISTS_PRESENT
1514 map_type map7({}, compare);
1515 map_type map8({}, compare, allocator);
1516 #endif
1517 }
1518
1519 #if __TBB_CPP11_RVALUE_REF_PRESENT && __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT && !__TBB_SCOPED_ALLOCATOR_BROKEN
1520 #include <scoped_allocator>
1521
1522 struct custom_hash_compare {
1523 template<typename Allocator>
hashcustom_hash_compare1524 static size_t hash(const allocator_aware_data<Allocator>& key) {
1525 return tbb::tbb_hash_compare<int>::hash(key.value());
1526 }
1527
1528 template<typename Allocator>
equalcustom_hash_compare1529 static bool equal(const allocator_aware_data<Allocator>& key1, const allocator_aware_data<Allocator>& key2) {
1530 return tbb::tbb_hash_compare<int>::equal(key1.value(), key2.value());
1531 }
1532 };
1533
TestScopedAllocator()1534 void TestScopedAllocator() {
1535 typedef allocator_aware_data<std::scoped_allocator_adaptor<tbb::tbb_allocator<int>>> allocator_data_type;
1536 typedef std::scoped_allocator_adaptor<tbb::tbb_allocator<allocator_data_type>> allocator_type;
1537 typedef tbb::concurrent_hash_map<allocator_data_type, allocator_data_type,
1538 custom_hash_compare, allocator_type> hash_map_type;
1539
1540 allocator_type allocator;
1541 allocator_data_type key1(1, allocator), key2(2, allocator);
1542 allocator_data_type data1(1, allocator), data2(data1, allocator);
1543 hash_map_type map1(allocator), map2(allocator);
1544
1545 hash_map_type::value_type v1(key1, data1), v2(key2, data2);
1546
1547 auto init_list = { v1, v2 };
1548
1549 allocator_data_type::assert_on_constructions = true;
1550 map1.emplace(key1, data1);
1551 map2.emplace(key2, std::move(data2));
1552
1553 map1.clear();
1554 map2.clear();
1555
1556 map1.insert(v1);
1557 map2.insert(std::move(v2));
1558
1559 map1.clear();
1560 map2.clear();
1561
1562 map1.insert(init_list);
1563
1564 map1.clear();
1565 map2.clear();
1566
1567 hash_map_type::accessor a;
1568 map2.insert(a, allocator_data_type(3));
1569 a.release();
1570
1571 map1 = map2;
1572 map2 = std::move(map1);
1573
1574 hash_map_type map3(allocator);
1575 map3.rehash(1000);
1576 map3 = map2;
1577 }
1578 #endif
1579
1580 #if __TBB_ALLOCATOR_TRAITS_PRESENT
TestAllocatorTraits()1581 void TestAllocatorTraits() {
1582 using namespace propagating_allocators;
1583 typedef int key;
1584 typedef int mapped;
1585 typedef tbb::tbb_hash_compare<key> compare;
1586
1587 typedef tbb::concurrent_hash_map<key, mapped, compare, always_propagating_allocator> always_propagating_map;
1588 typedef tbb::concurrent_hash_map<key, mapped, compare, never_propagating_allocator> never_propagating_map;
1589 typedef tbb::concurrent_hash_map<key, mapped, compare, pocma_allocator> pocma_map;
1590 typedef tbb::concurrent_hash_map<key, mapped, compare, pocca_allocator> pocca_map;
1591 typedef tbb::concurrent_hash_map<key, mapped, compare, pocs_allocator> pocs_map;
1592
1593 test_allocator_traits_support<always_propagating_map>();
1594 test_allocator_traits_support<never_propagating_map>();
1595 test_allocator_traits_support<pocma_map>();
1596 test_allocator_traits_support<pocca_map>();
1597 test_allocator_traits_support<pocs_map>();
1598
1599 #if __TBB_CPP11_RVALUE_REF_PRESENT
1600 test_allocator_traits_with_non_movable_value_type<pocma_map>();
1601 #endif
1602 }
1603 #endif // __TBB_ALLOCATOR_TRAITS_PRESENT
1604
1605 // A test for undocumented member function internal_fast_find
1606 // which is declared protected in concurrent_hash_map for internal TBB use
TestInternalFastFind()1607 void TestInternalFastFind() {
1608 typedef tbb::concurrent_hash_map<int, int> basic_chmap_type;
1609 typedef basic_chmap_type::const_pointer const_pointer;
1610
1611 class chmap : public basic_chmap_type {
1612 public:
1613 chmap() : basic_chmap_type() {}
1614
1615 using basic_chmap_type::internal_fast_find;
1616 };
1617
1618 chmap m;
1619 int sz = 100;
1620
1621 for (int i = 0; i != sz; ++i) {
1622 m.insert(std::make_pair(i, i * i));
1623 }
1624 ASSERT(m.size() == 100, "Incorrect concurrent_hash_map size");
1625
1626 for (int i = 0; i != sz; ++i) {
1627 const_pointer res = m.internal_fast_find(i);
1628 ASSERT(res != NULL, "Incorrect internal_fast_find return value for existing key");
1629 basic_chmap_type::value_type val = *res;
1630 ASSERT(val.first == i, "Incorrect key in internal_fast_find return value");
1631 ASSERT(val.second == i * i, "Incorrect mapped in internal_fast_find return value");
1632 }
1633
1634 for (int i = sz; i != 2 * sz; ++i) {
1635 const_pointer res = m.internal_fast_find(i);
1636 ASSERT(res == NULL, "Incorrect internal_fast_find return value for not existing key");
1637 }
1638 }
1639
1640 //------------------------------------------------------------------------
1641 // Test driver
1642 //------------------------------------------------------------------------
TestMain()1643 int TestMain () {
1644 if( MinThread<0 ) {
1645 REPORT("ERROR: must use at least one thread\n");
1646 exit(1);
1647 }
1648 if( MaxThread<2 ) MaxThread=2;
1649
1650 // Do serial tests
1651 TestTypes();
1652 TestCopy();
1653 TestRehash();
1654 TestAssignment();
1655 TestIteratorsAndRanges();
1656 #if __TBB_INITIALIZER_LISTS_PRESENT
1657 TestInitList();
1658 #endif //__TBB_INITIALIZER_LISTS_PRESENT
1659
1660 #if __TBB_RANGE_BASED_FOR_PRESENT
1661 TestRangeBasedFor();
1662 #endif //#if __TBB_RANGE_BASED_FOR_PRESENT
1663
1664 #if TBB_USE_EXCEPTIONS
1665 TestExceptions();
1666 #endif /* TBB_USE_EXCEPTIONS */
1667
1668 TestMoveSupport();
1669 {
1670 #if __TBB_CPP11_RVALUE_REF_PRESENT
1671 tbb::task_scheduler_init init( 1 );
1672 int n=250000;
1673 {
1674 DataStateTrackedTable table;
1675 DoConcurrentOperations<RvalueInsert, DataStateTrackedTable>( table, n, "rvalue ref insert", 1 );
1676 }
1677 #if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
1678 {
1679 DataStateTrackedTable table;
1680 DoConcurrentOperations<Emplace, DataStateTrackedTable>( table, n, "emplace", 1 );
1681 }
1682 #endif //__TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
1683 #endif // __TBB_CPP11_RVALUE_REF_PRESENT
1684 }
1685
1686 // Do concurrency tests.
1687 for( int nthread=MinThread; nthread<=MaxThread; ++nthread ) {
1688 tbb::task_scheduler_init init( nthread );
1689 TestInsertFindErase( nthread );
1690 TestConcurrency( nthread );
1691 }
1692 // check linking
1693 if(bad_hashing) { //should be false
1694 tbb::internal::runtime_warning("none\nERROR: it must not be executed");
1695 }
1696
1697 TestCPP11Types();
1698 TestHashCompareConstructors();
1699
1700 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
1701 TestDeductionGuides<tbb::concurrent_hash_map>();
1702 #endif
1703 #if __TBB_CPP11_RVALUE_REF_PRESENT && __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT && !__TBB_SCOPED_ALLOCATOR_BROKEN
1704 TestScopedAllocator();
1705 #endif
1706
1707 #if __TBB_ALLOCATOR_TRAITS_PRESENT
1708 TestAllocatorTraits();
1709 #endif
1710
1711 TestInternalFastFind();
1712 return Harness::Done;
1713 }
1714