21 #ifndef __TBB_concurrent_hash_map_H 22 #define __TBB_concurrent_hash_map_H 28 #include __TBB_STD_SWAP_HEADER 39 #if __TBB_INITIALIZER_LISTS_PRESENT 40 #include <initializer_list> 42 #if TBB_USE_PERFORMANCE_WARNINGS || __TBB_STATISTICS 51 namespace interface5 {
53 template<
typename Key,
typename T,
typename HashCompare = tbb_hash_compare<Key>,
typename A = tbb_allocator<std::pair<const Key, T> > >
98 static size_type
const embedded_block = 1;
100 static size_type
const embedded_buckets = 1<<embedded_block;
102 static size_type
const first_block = 8;
104 static size_type
const pointers_per_table =
sizeof(segment_index_t) * 8;
108 typedef segment_ptr_t segments_table_t[pointers_per_table];
116 bucket my_embedded_segment[embedded_buckets];
124 std::memset(
this, 0, pointers_per_table*
sizeof(segment_ptr_t)
125 +
sizeof(my_size) +
sizeof(my_mask)
126 + embedded_buckets*
sizeof(
bucket) );
127 for( size_type i = 0; i < embedded_block; i++ )
128 my_table[i] = my_embedded_segment + segment_base(i);
129 my_mask = embedded_buckets - 1;
130 __TBB_ASSERT( embedded_block <= first_block,
"The first block number must include embedded blocks");
133 my_info_restarts = 0;
134 my_info_rehashes = 0;
140 return segment_index_t(
__TBB_Log2( index|1 ) );
145 return (segment_index_t(1)<<k & ~segment_index_t(1));
150 return size_type(1)<<k;
155 return reinterpret_cast<uintptr_t
>(ptr) > uintptr_t(63);
159 static void init_buckets( segment_ptr_t ptr, size_type sz,
bool is_initial ) {
160 if( is_initial ) std::memset( static_cast<void*>(ptr), 0, sz*
sizeof(
bucket) );
161 else for(size_type i = 0; i < sz; i++, ptr++) {
162 *
reinterpret_cast<intptr_t*
>(&ptr->
mutex) = 0;
179 if( my_segment_ptr ) *my_segment_ptr = 0;
189 __TBB_ASSERT( !is_valid(my_table[k]),
"Wrong concurrent assignment");
190 if( k >= first_block ) {
191 sz = segment_size( k );
192 segment_ptr_t ptr = alloc.
allocate( sz );
193 init_buckets( ptr, sz, is_initial );
197 __TBB_ASSERT( k == embedded_block,
"Wrong segment index" );
198 sz = segment_size( first_block );
199 segment_ptr_t ptr = alloc.
allocate( sz - embedded_buckets );
200 init_buckets( ptr, sz - embedded_buckets, is_initial );
201 ptr -= segment_base(embedded_block);
202 for(segment_index_t i = embedded_block; i < first_block; i++)
211 segment_index_t
s = segment_index_of(
h );
212 h -= segment_base(s);
213 segment_ptr_t seg = my_table[
s];
214 __TBB_ASSERT( is_valid(seg),
"hashcode must be cut by valid mask for allocated segments" );
220 segment_index_t
s = segment_index_of(
h );
221 while( segment_ptr_t seg = my_table[++s] )
222 if( seg[
h].node_list == rehash_req ) {
224 mark_rehashed_levels(
h + ((hashcode_t)1<<s) );
231 hashcode_t m_now, m_old = m;
234 return check_rehashing_collision( h, m_old, m = m_now );
241 if( (h & m_old) != (h & m) ) {
244 for( ++m_old; !(h & m_old); m_old <<= 1 )
246 m_old = (m_old<<1) - 1;
262 size_type sz = ++my_size;
263 add_to_bucket( b, n );
266 segment_index_t new_seg =
__TBB_Log2( mask+1 );
267 __TBB_ASSERT( is_valid(my_table[new_seg-1]),
"new allocations must not publish new mask until segment has allocated");
268 static const segment_ptr_t is_allocating = (segment_ptr_t)2;
270 &&
as_atomic(my_table[new_seg]).compare_and_swap(is_allocating, NULL) == NULL )
278 if( !buckets-- )
return;
279 bool is_initial = !my_size;
280 for( size_type m = my_mask; buckets > m; m = my_mask )
281 enable_segment( segment_index_of( m+1 ), is_initial );
288 for(size_type i = 0; i < embedded_buckets; i++)
290 for(size_type i = embedded_block; i < pointers_per_table; i++)
295 template<
typename Iterator>
301 template<
typename Container,
typename Value>
303 :
public std::iterator<std::forward_iterator_tag,Value>
306 typedef typename Container::node
node;
310 template<
typename C,
typename T,
typename U>
313 template<
typename C,
typename T,
typename U>
316 template<
typename C,
typename T,
typename U>
319 template<
typename C,
typename U>
326 size_t k = my_index+1;
327 __TBB_ASSERT( my_bucket,
"advancing an invalid iterator?");
328 while( k <= my_map->my_mask ) {
332 else my_bucket = my_map->get_bucket( k );
333 my_node =
static_cast<node*
>( my_bucket->node_list );
335 my_index = k;
return;
339 my_bucket = 0; my_node = 0; my_index = k;
341 #if !defined(_MSC_VER) || defined(__INTEL_COMPILER) 342 template<
typename Key,
typename T,
typename HashCompare,
typename A>
347 const Container *my_map;
359 hash_map_iterator(
const Container &map,
size_t index,
const bucket *b, node_base *n );
365 my_map(other.my_map),
366 my_index(other.my_index),
367 my_bucket(other.my_bucket),
368 my_node(other.my_node)
372 return my_node->item;
385 template<
typename Container,
typename Value>
390 my_node( static_cast<
node*>(n) )
396 template<
typename Container,
typename Value>
403 template<
typename Container,
typename T,
typename U>
408 template<
typename Container,
typename T,
typename U>
415 template<
typename Iterator>
423 void set_midpoint()
const;
434 bool empty()
const {
return my_begin==my_end;}
438 return my_midpoint!=my_end;
443 my_grainsize(r.my_grainsize)
446 __TBB_ASSERT( !empty(),
"Splitting despite the range is not divisible" );
454 my_begin(r.my_begin),
456 my_midpoint(r.my_midpoint),
457 my_grainsize(r.my_grainsize)
461 my_begin( Iterator( map, 0, map.my_embedded_segment, map.my_embedded_segment->node_list ) ),
462 my_end( Iterator( map, map.my_mask + 1, 0, 0 ) ),
463 my_grainsize( grainsize_ )
465 __TBB_ASSERT( grainsize_>0,
"grainsize must be positive" );
468 const Iterator&
begin()
const {
return my_begin;}
469 const Iterator&
end()
const {
return my_end;}
474 template<
typename Iterator>
477 size_t m = my_end.my_index-my_begin.my_index;
478 if( m > my_grainsize ) {
479 m = my_begin.my_index + m/2u;
481 my_midpoint = Iterator(*my_begin.my_map,m,b,b->
node_list);
483 my_midpoint = my_end;
485 __TBB_ASSERT( my_begin.my_index <= my_midpoint.my_index,
486 "my_begin is after my_midpoint" );
488 "my_midpoint is after my_end" );
489 __TBB_ASSERT( my_begin != my_midpoint || my_begin == my_end,
490 "[my_begin, my_midpoint) range should not be empty" );
496 #if _MSC_VER && !defined(__INTEL_COMPILER) 498 #pragma warning( push ) 499 #pragma warning( disable: 4127 ) 532 template<
typename Key,
typename T,
typename HashCompare,
typename Allocator>
534 template<
typename Container,
typename Value>
535 friend class internal::hash_map_iterator;
538 friend class internal::hash_map_range;
550 typedef internal::hash_map_iterator<concurrent_hash_map,value_type>
iterator;
551 typedef internal::hash_map_iterator<concurrent_hash_map,const value_type>
const_iterator;
563 struct node :
public node_base {
566 node(
const Key &
key,
const T &t ) : item(key, t) {}
567 #if __TBB_CPP11_RVALUE_REF_PRESENT 570 #if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT 571 template<
typename... Args>
572 node( Args&&... args ) : item(
std::forward<Args>(args)...) {}
573 #if __TBB_COPY_FROM_NON_CONST_REF_BROKEN 574 node( value_type& i ) : item(const_cast<const value_type&>(i)) {}
575 #endif //__TBB_COPY_FROM_NON_CONST_REF_BROKEN 576 #endif //__TBB_CPP11_VARIADIC_TEMPLATES_PRESENT 577 #endif //__TBB_CPP11_RVALUE_REF_PRESENT 578 node(
const value_type& i ) : item(i) {}
581 void *
operator new(
size_t , node_allocator_type &a ) {
582 void *ptr = a.allocate(1);
588 void operator delete(
void *ptr, node_allocator_type &a ) { a.deallocate(static_cast<node*>(ptr),1); }
592 my_allocator.destroy( static_cast<node*>(n) );
593 my_allocator.deallocate( static_cast<node*>(n), 1);
597 return new( allocator )
node(key, *t);
600 #if __TBB_CPP11_RVALUE_REF_PRESENT 602 return new( allocator )
node(key,
std::move(*const_cast<T*>(t)));
604 #if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT 605 template<
typename... Args>
607 return new( allocator )
node(std::forward<Args>(args)...);
609 #endif //__TBB_CPP11_VARIADIC_TEMPLATES_PRESENT 610 #endif //__TBB_CPP11_RVALUE_REF_PRESENT 613 return new( allocator )
node(key);
617 __TBB_ASSERT(
false,
"this dummy function should not be called");
623 while( is_valid(n) && !my_hash_compare.equal(key, n->
item.first) )
624 n = static_cast<node*>( n->next );
639 && try_acquire( my_b->mutex,
true ) )
655 __TBB_ASSERT( h > 1,
"The lowermost buckets can't be rehashed" );
664 mask = (mask<<1) | 1;
665 __TBB_ASSERT( (mask&(mask+1))==0 && (h & mask) == h, NULL );
668 hashcode_t c = my_hash_compare.hash( static_cast<node*>(n)->item.first );
670 hashcode_t bmask = h & (mask>>1);
671 bmask = bmask==0? 1 : ( 1u<<(
__TBB_Log2( bmask )+1 ) ) - 1;
672 __TBB_ASSERT( (c & bmask) == (h & bmask),
"hash() function changed for key in table" );
674 if( (c & mask) == h ) {
676 if( !b_old.upgrade_to_writer() ) {
680 add_to_bucket( b_new, n );
761 : internal::hash_map_base(), my_allocator(a)
765 : internal::hash_map_base(), my_allocator(a), my_hash_compare(compare)
770 : internal::hash_map_base(), my_allocator(a)
775 concurrent_hash_map( size_type n,
const HashCompare& compare,
const allocator_type& a = allocator_type() )
776 : internal::hash_map_base(), my_allocator(a), my_hash_compare(compare)
783 : internal::hash_map_base(), my_allocator(a)
786 internal_copy(table);
790 #if __TBB_CPP11_RVALUE_REF_PRESENT 800 : internal::hash_map_base(), my_allocator(a)
806 internal_copy(std::make_move_iterator(table.
begin()), std::make_move_iterator(table.
end()), table.
size());
810 #endif //__TBB_CPP11_RVALUE_REF_PRESENT 815 : internal::hash_map_base(), my_allocator(a)
818 internal_copy(first, last, std::distance(first, last));
823 concurrent_hash_map( I first, I last,
const HashCompare& compare,
const allocator_type& a = allocator_type() )
824 : internal::hash_map_base(), my_allocator(a), my_hash_compare(compare)
827 internal_copy(first, last, std::distance(first, last));
831 #if __TBB_INITIALIZER_LISTS_PRESENT 832 concurrent_hash_map( std::initializer_list<value_type> il,
const allocator_type &a = allocator_type() )
834 : internal::hash_map_base(), my_allocator(a)
837 internal_copy(il.begin(), il.end(), il.size());
841 concurrent_hash_map( std::initializer_list<value_type> il,
const HashCompare& compare,
const allocator_type& a = allocator_type() )
842 : internal::hash_map_base(), my_allocator(a), my_hash_compare(compare)
845 internal_copy(il.begin(), il.end(), il.size());
849 #endif //__TBB_INITIALIZER_LISTS_PRESENT 855 internal_copy(table);
860 #if __TBB_CPP11_RVALUE_REF_PRESENT 872 this->
swap(moved_copy);
877 #endif //__TBB_CPP11_RVALUE_REF_PRESENT 879 #if __TBB_INITIALIZER_LISTS_PRESENT 883 internal_copy(il.begin(), il.end(), il.size());
886 #endif //__TBB_INITIALIZER_LISTS_PRESENT 892 void rehash(size_type n = 0);
903 range_type
range( size_type grainsize=1 ) {
904 return range_type( *
this, grainsize );
906 const_range_type
range( size_type grainsize=1 )
const {
907 return const_range_type( *
this, grainsize );
913 iterator
begin() {
return iterator( *
this, 0, my_embedded_segment, my_embedded_segment->
node_list ); }
914 iterator
end() {
return iterator( *
this, 0, 0, 0 ); }
915 const_iterator
begin()
const {
return const_iterator( *
this, 0, my_embedded_segment, my_embedded_segment->
node_list ); }
916 const_iterator
end()
const {
return const_iterator( *
this, 0, 0, 0 ); }
917 std::pair<iterator, iterator>
equal_range(
const Key&
key ) {
return internal_equal_range( key,
end() ); }
918 std::pair<const_iterator, const_iterator>
equal_range(
const Key&
key )
const {
return internal_equal_range( key,
end() ); }
921 size_type
size()
const {
return my_size; }
924 bool empty()
const {
return my_size == 0; }
944 return const_cast<concurrent_hash_map*
>(
this)->lookup(
false, key, NULL, NULL,
false, &do_not_allocate_node );
951 return const_cast<concurrent_hash_map*
>(
this)->lookup(
false, key, NULL, &result,
false, &do_not_allocate_node );
958 return lookup(
false, key, NULL, &result,
true, &do_not_allocate_node );
965 return lookup(
true, key, NULL, &result,
false, &allocate_node_default_construct );
972 return lookup(
true, key, NULL, &result,
true, &allocate_node_default_construct );
979 return lookup(
true, value.first, &value.second, &result,
false, &allocate_node_copy_construct );
986 return lookup(
true, value.first, &value.second, &result,
true, &allocate_node_copy_construct );
992 return lookup(
true, value.first, &value.second, NULL,
false, &allocate_node_copy_construct );
995 #if __TBB_CPP11_RVALUE_REF_PRESENT 1014 #if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT 1017 template<
typename... Args>
1019 return generic_emplace(result, std::forward<Args>(args)...);
1024 template<
typename... Args>
1026 return generic_emplace(result, std::forward<Args>(args)...);
1031 template<
typename... Args>
1035 #endif //__TBB_CPP11_VARIADIC_TEMPLATES_PRESENT 1036 #endif //__TBB_CPP11_RVALUE_REF_PRESENT 1039 template<
typename I>
1045 #if __TBB_INITIALIZER_LISTS_PRESENT 1046 void insert( std::initializer_list<value_type> il ) {
1048 insert( il.begin(), il.end() );
1050 #endif //__TBB_INITIALIZER_LISTS_PRESENT 1054 bool erase(
const Key&
key );
1059 return exclude( item_accessor );
1065 return exclude( item_accessor );
1070 bool lookup(
bool op_insert,
const Key &key,
const T *t,
const_accessor *result,
bool write,
node* (*allocate_node)(node_allocator_type& ,
const Key &,
const T * ),
node *tmp_n = 0 ) ;
1080 #if __TBB_CPP11_RVALUE_REF_PRESENT 1081 template<
typename Accessor>
1084 return lookup(
true,
value.first, &
value.second, accessor_location(result), is_write_access_needed(result), &allocate_node_move_construct );
1087 #if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT 1088 template<
typename Accessor,
typename... Args>
1091 node * node_ptr = allocate_node_emplace_construct(my_allocator, std::forward<Args>(args)...);
1092 return lookup(
true, node_ptr->
item.first, NULL, accessor_location(result), is_write_access_needed(result), &do_not_allocate_node, node_ptr );
1094 #endif //__TBB_CPP11_VARIADIC_TEMPLATES_PRESENT 1095 #endif //__TBB_CPP11_RVALUE_REF_PRESENT 1101 template<
typename I>
1102 std::pair<I, I> internal_equal_range(
const Key& key, I
end )
const;
1107 template<
typename I>
1108 void internal_copy( I first, I last, size_type reserve_size );
1114 hashcode_t
h = my_hash_compare.hash( key );
1118 __TBB_ASSERT((m&(m+1))==0,
"data structure is invalid");
1119 bucket *b = get_bucket( h & m );
1124 if( lock.try_acquire( b->
mutex,
true ) ) {
1128 else lock.acquire( b->
mutex,
false );
1131 n = search_bucket( key, b );
1134 else if( check_mask_race( h, m ) )
1140 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT 1141 namespace internal {
1144 template<
template<
typename...>
typename Map,
typename Key,
typename T,
typename... Args>
1145 using hash_map_t = Map<
1147 std::conditional_t< (
sizeof...(Args)>0) && !is_allocator_v< pack_element_t<0, Args...> >,
1149 std::conditional_t< (
sizeof...(Args)>0) && is_allocator_v< pack_element_t<
sizeof...(Args)-1, Args...> >,
1155 template<
typename I,
typename... Args>
1157 -> internal::hash_map_t<concurrent_hash_map, internal::iterator_key_t<I>,internal::iterator_mapped_t<I>, Args...>;
1161 template<
typename Key,
typename T,
typename CompareOrAllocator>
1163 -> internal::hash_map_t<concurrent_hash_map, Key, T, CompareOrAllocator>;
1167 template<
typename Key,
typename T,
typename HashCompare,
typename A>
1168 bool concurrent_hash_map<Key,T,HashCompare,A>::lookup(
bool op_insert,
const Key &
key,
const T *t,
const_accessor *result,
bool write,
node* (*allocate_node)(node_allocator_type& ,
const Key&,
const T*),
node *tmp_n ) {
1171 hashcode_t
const h = my_hash_compare.hash( key );
1173 segment_index_t grow_segment = 0;
1177 __TBB_ASSERT((m&(m+1))==0,
"data structure is invalid");
1178 return_value =
false;
1183 n = search_bucket( key, b() );
1188 tmp_n = allocate_node(my_allocator, key, t);
1190 if( !b.
is_writer() && !b.upgrade_to_writer() ) {
1192 n = search_bucket( key, b() );
1194 b.downgrade_to_reader();
1198 if( check_mask_race(h, m) )
1201 grow_segment = insert_new_node( b(), n = tmp_n, m );
1203 return_value =
true;
1207 if( check_mask_race( h, m ) )
1211 return_value =
true;
1214 if( !result )
goto check_growth;
1217 if( !result->try_acquire( n->mutex, write ) ) {
1219 if( result->try_acquire( n->mutex, write ) )
break;
1220 if( !backoff.bounded_pause() ) {
1223 __TBB_ASSERT( !op_insert || !return_value,
"Can't acquire new item in locked bucket?" );
1235 if( grow_segment ) {
1236 #if __TBB_STATISTICS 1239 enable_segment( grow_segment );
1242 delete_node( tmp_n );
1243 return return_value;
1246 template<
typename Key,
typename T,
typename HashCompare,
typename A>
1247 template<
typename I>
1249 hashcode_t
h = my_hash_compare.hash( key );
1250 hashcode_t m = my_mask;
1251 __TBB_ASSERT((m&(m+1))==0,
"data structure is invalid");
1253 bucket *b = get_bucket( h );
1256 b = get_bucket( h &= m );
1258 node *n = search_bucket( key, b );
1260 return std::make_pair(end_, end_);
1261 iterator lower(*
this, h, b, n), upper(lower);
1262 return std::make_pair(lower, ++upper);
1265 template<
typename Key,
typename T,
typename HashCompare,
typename A>
1268 node_base *
const n = item_accessor.
my_node;
1269 hashcode_t
const h = item_accessor.
my_hash;
1274 node_base **
p = &b()->node_list;
1275 while( *p && *p != n )
1278 if( check_mask_race( h, m ) )
1289 item_accessor.upgrade_to_writer();
1295 template<
typename Key,
typename T,
typename HashCompare,
typename A>
1298 hashcode_t
const h = my_hash_compare.hash( key );
1305 node_base **
p = &b()->node_list;
1307 while( is_valid(n) && !my_hash_compare.equal(key, static_cast<node*>(n)->item.first ) ) {
1312 if( check_mask_race( h, m ) )
1316 else if( !b.
is_writer() && !b.upgrade_to_writer() ) {
1317 if( check_mask_race( h, m ) )
1325 typename node::scoped_t item_locker( n->
mutex,
true );
1332 template<
typename Key,
typename T,
typename HashCompare,
typename A>
1338 internal_swap(table);
1341 template<
typename Key,
typename T,
typename HashCompare,
typename A>
1344 hashcode_t
mask = my_mask;
1345 hashcode_t b = (mask+1)>>1;
1347 bucket *bp = get_bucket( b );
1348 for(; b <=
mask; b++, bp++ ) {
1351 __TBB_ASSERT( *reinterpret_cast<intptr_t*>(&bp->
mutex) == 0,
"concurrent or unexpectedly terminated operation during rehash() execution" );
1353 hashcode_t
h = b;
bucket *b_old = bp;
1355 __TBB_ASSERT( h > 1,
"The lowermost buckets can't be rehashed" );
1357 b_old = get_bucket( h &= m );
1360 mark_rehashed_levels( h );
1361 for( node_base **
p = &b_old->
node_list, *q = *
p; is_valid(q); q = *
p ) {
1362 hashcode_t c = my_hash_compare.hash( static_cast<node*>(q)->item.first );
1363 if( (c & mask) != h ) {
1365 bucket *b_new = get_bucket( c & mask );
1367 add_to_bucket( b_new, q );
1368 }
else p = &q->next;
1372 #if TBB_USE_PERFORMANCE_WARNINGS 1373 int current_size =
int(my_size), buckets =
int(mask)+1, empty_buckets = 0, overpopulated_buckets = 0;
1374 static bool reported =
false;
1376 #if TBB_USE_ASSERT || TBB_USE_PERFORMANCE_WARNINGS 1377 for( b = 0; b <=
mask; b++ ) {
1378 if( b & (b-2) ) ++bp;
1379 else bp = get_bucket( b );
1381 __TBB_ASSERT( *reinterpret_cast<intptr_t*>(&bp->
mutex) == 0,
"concurrent or unexpectedly terminated operation during rehash() execution" );
1383 #if TBB_USE_PERFORMANCE_WARNINGS 1385 else if( n->
next ) overpopulated_buckets++;
1388 for( ; is_valid(n); n = n->
next ) {
1389 hashcode_t
h = my_hash_compare.hash( static_cast<node*>(n)->item.first ) &
mask;
1390 __TBB_ASSERT( h == b,
"hash() function changed for key in table or internal error" );
1394 #endif // TBB_USE_ASSERT || TBB_USE_PERFORMANCE_WARNINGS 1395 #if TBB_USE_PERFORMANCE_WARNINGS 1396 if( buckets > current_size) empty_buckets -= buckets - current_size;
1397 else overpopulated_buckets -= current_size - buckets;
1398 if( !reported && buckets >= 512 && ( 2*empty_buckets > current_size || 2*overpopulated_buckets > current_size ) ) {
1400 "Performance is not optimal because the hash function produces bad randomness in lower bits in %s.\nSize: %d Empties: %d Overlaps: %d",
1402 typeid(*this).name(),
1404 "concurrent_hash_map",
1406 current_size, empty_buckets, overpopulated_buckets );
1412 template<
typename Key,
typename T,
typename HashCompare,
typename A>
1414 hashcode_t m = my_mask;
1415 __TBB_ASSERT((m&(m+1))==0,
"data structure is invalid");
1416 #if TBB_USE_ASSERT || TBB_USE_PERFORMANCE_WARNINGS || __TBB_STATISTICS 1417 #if TBB_USE_PERFORMANCE_WARNINGS || __TBB_STATISTICS 1418 int current_size =
int(my_size), buckets =
int(m)+1, empty_buckets = 0, overpopulated_buckets = 0;
1419 static bool reported =
false;
1423 for( segment_index_t b = 0; b <= m; b++ ) {
1424 if( b & (b-2) ) ++bp;
1425 else bp = get_bucket( b );
1428 __TBB_ASSERT( *reinterpret_cast<intptr_t*>(&bp->
mutex) == 0,
"concurrent or unexpectedly terminated operation during clear() execution" );
1429 #if TBB_USE_PERFORMANCE_WARNINGS || __TBB_STATISTICS 1432 else if( n->
next ) overpopulated_buckets++;
1434 #if __TBB_EXTRA_DEBUG 1435 for(; is_valid(n); n = n->
next ) {
1436 hashcode_t
h = my_hash_compare.hash( static_cast<node*>(n)->item.first );
1442 #if TBB_USE_PERFORMANCE_WARNINGS || __TBB_STATISTICS 1443 #if __TBB_STATISTICS 1444 printf(
"items=%d buckets: capacity=%d rehashed=%d empty=%d overpopulated=%d" 1445 " concurrent: resizes=%u rehashes=%u restarts=%u\n",
1446 current_size,
int(m+1), buckets, empty_buckets, overpopulated_buckets,
1447 unsigned(my_info_resizes),
unsigned(my_info_rehashes),
unsigned(my_info_restarts) );
1448 my_info_resizes = 0;
1449 my_info_restarts = 0;
1450 my_info_rehashes = 0;
1452 if( buckets > current_size) empty_buckets -= buckets - current_size;
1453 else overpopulated_buckets -= current_size - buckets;
1454 if( !reported && buckets >= 512 && ( 2*empty_buckets > current_size || 2*overpopulated_buckets > current_size ) ) {
1456 "Performance is not optimal because the hash function produces bad randomness in lower bits in %s.\nSize: %d Empties: %d Overlaps: %d",
1458 typeid(*this).name(),
1460 "concurrent_hash_map",
1462 current_size, empty_buckets, overpopulated_buckets );
1466 #endif // TBB_USE_ASSERT || TBB_USE_PERFORMANCE_WARNINGS || __TBB_STATISTICS 1468 segment_index_t
s = segment_index_of( m );
1469 __TBB_ASSERT( s+1 == pointers_per_table || !my_table[s+1],
"wrong mask or concurrent grow" );
1472 __TBB_ASSERT( is_valid( my_table[s] ),
"wrong mask or concurrent grow" );
1473 segment_ptr_t buckets_ptr = my_table[
s];
1474 size_type sz = segment_size( s ? s : 1 );
1475 for( segment_index_t i = 0; i < sz; i++ )
1476 for( node_base *n = buckets_ptr[i].node_list; is_valid(n); n = buckets_ptr[i].
node_list ) {
1480 if( s >= first_block)
1482 else if( s == embedded_block && embedded_block != first_block )
1483 alloc.
deallocate( buckets_ptr, segment_size(first_block)-embedded_buckets );
1484 if( s >= embedded_block ) my_table[
s] = 0;
1486 my_mask = embedded_buckets - 1;
1489 template<
typename Key,
typename T,
typename HashCompare,
typename A>
1492 if( my_mask == mask ) {
1494 bucket *dst = 0, *src = 0;
1495 bool rehash_required =
false;
1496 for( hashcode_t k = 0; k <=
mask; k++ ) {
1497 if( k & (k-2) ) ++dst,src++;
1498 else { dst = get_bucket( k ); src = source.
get_bucket( k ); }
1500 node *n =
static_cast<node*
>( src->node_list );
1502 rehash_required =
true;
1504 }
else for(; n; n =
static_cast<node*
>( n->next ) ) {
1505 add_to_bucket( dst,
new( my_allocator )
node(n->
item.first, n->
item.second) );
1509 if( rehash_required ) rehash();
1513 template<
typename Key,
typename T,
typename HashCompare,
typename A>
1514 template<
typename I>
1516 reserve( reserve_size );
1517 hashcode_t m = my_mask;
1519 hashcode_t
h = my_hash_compare.hash( (*first).first );
1520 bucket *b = get_bucket( h & m );
1522 node *n =
new( my_allocator )
node(*first);
1523 add_to_bucket( b, n );
1533 template<
typename Key,
typename T,
typename HashCompare,
typename A1,
typename A2>
1535 if(a.
size() != b.
size())
return false;
1538 for(; i != i_end; ++i) {
1540 if( j == j_end || !(i->second == j->second) )
return false;
1545 template<
typename Key,
typename T,
typename HashCompare,
typename A1,
typename A2>
1547 {
return !(a == b); }
1549 template<
typename Key,
typename T,
typename HashCompare,
typename A>
1553 #if _MSC_VER && !defined(__INTEL_COMPILER) 1554 #pragma warning( pop ) 1555 #endif // warning 4127 is back static node * allocate_node_default_construct(node_allocator_type &allocator, const Key &key, const T *)
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d void ITT_FORMAT p void ITT_FORMAT p __itt_model_site __itt_model_site_instance ITT_FORMAT p __itt_model_task __itt_model_task_instance ITT_FORMAT p void ITT_FORMAT p void ITT_FORMAT p void size_t ITT_FORMAT d void ITT_FORMAT p const wchar_t ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s no args void ITT_FORMAT p size_t ITT_FORMAT d no args const wchar_t const wchar_t ITT_FORMAT s __itt_heap_function void size_t int ITT_FORMAT d __itt_heap_function void ITT_FORMAT p __itt_heap_function void void size_t int ITT_FORMAT d no args no args unsigned int ITT_FORMAT u const __itt_domain __itt_id ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain __itt_id ITT_FORMAT p const __itt_domain __itt_id __itt_timestamp __itt_timestamp end
T itt_hide_load_word(const T &src)
void __TBB_EXPORTED_FUNC runtime_warning(const char *format,...)
Report a runtime warning.
node(const value_type &i)
Class that implements exponential backoff.
bool erase(accessor &item_accessor)
Erase item by accessor.
bool insert(const value_type &value)
Insert item by copying if there is no such key present already.
Combines data access, locking, and garbage collection.
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d void ITT_FORMAT p void ITT_FORMAT p __itt_model_site __itt_model_site_instance ITT_FORMAT p __itt_model_task __itt_model_task_instance ITT_FORMAT p void ITT_FORMAT p void ITT_FORMAT p void size_t ITT_FORMAT d void ITT_FORMAT p const wchar_t ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s no args void ITT_FORMAT p size_t ITT_FORMAT d no args const wchar_t const wchar_t ITT_FORMAT s __itt_heap_function h
hash_map_iterator(const hash_map_iterator< Container, typename Container::value_type > &other)
~enable_segment_failsafe()
node * search_bucket(const key_type &key, bucket *b) const
bool empty() const
True if size()==0.
bucket * segment_ptr_t
Segment pointer.
friend bool is_write_access_needed(accessor const &)
hash_map_range(const map_type &map, size_type grainsize_=1)
Init range with container and grainsize specified.
static node * do_not_allocate_node(node_allocator_type &, const Key &, const T *)
T __TBB_load_with_acquire(const volatile T &location)
pointer operator->() const
Return pointer to associated value in hash table.
internal::hash_map_iterator< concurrent_hash_map, value_type > iterator
hash_map_base::bucket bucket
std::pair< const Key, T > value_type
bool generic_move_insert(Accessor &&result, value_type &&value)
bool insert(accessor &result, const value_type &value)
Insert item by copying if there is no such key present already and acquire a write lock on the item...
void set_midpoint() const
Set my_midpoint to point approximately half way between my_begin and my_end.
bool emplace(accessor &result, Args &&... args)
Insert item by copying if there is no such key present already and acquire a write lock on the item...
size_t size_type
Size type.
#define __TBB_ASSERT(predicate, comment)
No-op version of __TBB_ASSERT.
std::size_t size_type
Type for size of a range.
void internal_copy(const concurrent_hash_map &source)
Copy "source" to *this, where *this must start out empty.
spin_rw_mutex mutex_t
Mutex type.
enable_segment_failsafe(segments_table_t &table, segment_index_t k)
tick_count::interval_t operator-(const tick_count &t1, const tick_count &t0)
~concurrent_hash_map()
Clear table and destroy it.
const Container * my_map
concurrent_hash_map over which we are iterating.
internal::hash_map_range< iterator > range_type
void itt_store_word_with_release(tbb::atomic< T > &dst, U src)
~const_accessor()
Destroy result after releasing the underlying reference.
auto first(Container &c) -> decltype(begin(c))
const bucket * my_bucket
Pointer to bucket.
Value & operator*() const
void swap(concurrent_hash_map &table)
swap two instances. Iterators are invalidated
internal::hash_map_iterator< concurrent_hash_map, const value_type > const_iterator
static void add_to_bucket(bucket *b, node_base *n)
Add node.
Iterator::difference_type difference_type
concurrent_hash_map(const allocator_type &a=allocator_type())
Construct empty table.
bool find(const_accessor &result, const Key &key) const
Find item and acquire a read lock on the item.
Iterator::reference reference
void rehash_bucket(bucket *b_new, const hashcode_t h)
Meets "allocator" requirements of ISO C++ Standard, Section 20.1.5.
const_pointer internal_fast_find(const Key &key) const
Fast find when no concurrent erasure is used. For internal use inside TBB only!
size_type count(const Key &key) const
Return count of items (0 or 1)
friend bool is_write_access_needed(accessor_not_used const &)
hash_compare that is default argument for concurrent_hash_map
const value_type * const_pointer
node_allocator_type my_allocator
segment_ptr_t * my_segment_ptr
bool exclude(const_accessor &item_accessor)
delete item by accessor
T itt_load_word_with_acquire(const tbb::atomic< T > &src)
Meets requirements of a forward iterator for STL */.
static size_type segment_size(segment_index_t k)
bool empty() const
True if range is empty.
hash_map_iterator & operator++()
void rehash(size_type n=0)
Rehashes and optionally resizes the whole table.
concurrent_hash_map(I first, I last, const allocator_type &a=allocator_type())
Construction with copying iteration range and given allocator instance.
void advance_to_next_bucket()
Unordered map from Key to T.
void internal_swap(hash_map_base &table)
Swap hash_map_bases.
const value_type & const_reference
static bool is_valid(void *ptr)
const_iterator begin() const
bool insert(value_type &&value)
Insert item by copying if there is no such key present already.
hash_map_iterator()
Construct undefined iterator.
const_reference operator*() const
Return reference to associated value in hash table.
void const char const char int ITT_FORMAT __itt_group_sync p
bool empty() const
True if result is empty.
const_range_type range(size_type grainsize=1) const
const Iterator & begin() const
bool insert(accessor &result, value_type &&value)
Insert item by copying if there is no such key present already and acquire a write lock on the item...
static node * allocate_node_emplace_construct(node_allocator_type &allocator, Args &&... args)
std::pair< const_iterator, const_iterator > equal_range(const Key &key) const
void throw_exception(exception_id eid)
Versionless convenience wrapper for throw_exception_v4()
The scoped locking pattern.
concurrent_hash_map(concurrent_hash_map &&table, const allocator_type &a)
Move constructor.
bucket accessor is to find, rehash, acquire a lock, and access a bucket
allocator_type get_allocator() const
return allocator object
call_clear_on_leave(concurrent_hash_map *a_ch_map)
mutex_t::scoped_lock scoped_t
Scoped lock type for mutex.
std::pair< iterator, iterator > equal_range(const Key &key)
Range class used with concurrent_hash_map.
auto last(Container &c) -> decltype(begin(c))
intptr_t __TBB_Log2(uintptr_t x)
bool find(accessor &result, const Key &key)
Find item and acquire a write lock on the item.
hash_map_base::node_base node_base
friend bool operator!=(const hash_map_iterator< C, T > &i, const hash_map_iterator< C, U > &j)
void deallocate(pointer p, size_type)
Free block of memory that starts on a cache line.
static hash_map_node_base *const empty_rehashed
Rehashed empty bucket flag.
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d void ITT_FORMAT p void ITT_FORMAT p __itt_model_site __itt_model_site_instance ITT_FORMAT p __itt_model_task __itt_model_task_instance ITT_FORMAT p void * lock
tbb::internal::allocator_rebind< Allocator, node >::type node_allocator_type
concurrent_hash_map * my_ch_map
bool is_writer()
check whether bucket is locked for write
atomic< size_type > my_size
Size of container in stored items.
Value * operator->() const
bool emplace(const_accessor &result, Args &&... args)
Insert item by copying if there is no such key present already and acquire a read lock on the item...
Iterator::map_type map_type
std::pair< I, I > internal_equal_range(const Key &key, I end) const
Returns an iterator for an item defined by the key, or for the next item after it (if upper==true) ...
void release()
Set to null.
hash_map_node_base node_base
Node base type.
void swap(concurrent_hash_map< Key, T, HashCompare, A > &a, concurrent_hash_map< Key, T, HashCompare, A > &b)
void reserve(size_type buckets)
Prepare enough segments for number of buckets.
reference operator*() const
Return reference to associated value in hash table.
size_t my_index
Index in hash table for current item.
node(const Key &key, const T &t)
bool erase(const Key &key)
Erase item.
void acquire(spin_rw_mutex &m, bool write=true)
Acquire lock on given mutex.
bool operator==(const hash_map_iterator< Container, T > &i, const hash_map_iterator< Container, U > &j)
hash_map_node_base * next
Next node in chain.
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d void ITT_FORMAT p void ITT_FORMAT p __itt_model_site __itt_model_site_instance ITT_FORMAT p __itt_model_task __itt_model_task_instance ITT_FORMAT p void ITT_FORMAT p void ITT_FORMAT p void size_t ITT_FORMAT d void ITT_FORMAT p const wchar_t ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s no args void ITT_FORMAT p size_t ITT_FORMAT d no args const wchar_t const wchar_t ITT_FORMAT s __itt_heap_function void size_t int ITT_FORMAT d __itt_heap_function void ITT_FORMAT p __itt_heap_function void void size_t int ITT_FORMAT d no args no args unsigned int ITT_FORMAT u const __itt_domain __itt_id ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain __itt_id ITT_FORMAT p const __itt_domain __itt_id __itt_timestamp __itt_timestamp ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain ITT_FORMAT p const __itt_domain __itt_string_handle unsigned long long value
size_t segment_index_t
Segment index type.
bucket my_embedded_segment[embedded_buckets]
Zero segment.
bool generic_emplace(Accessor &&result, Args &&... args)
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d void ITT_FORMAT p void ITT_FORMAT p __itt_model_site __itt_model_site_instance ITT_FORMAT p __itt_model_task __itt_model_task_instance ITT_FORMAT p void ITT_FORMAT p void ITT_FORMAT p void size_t ITT_FORMAT d void ITT_FORMAT p const wchar_t ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s no args void ITT_FORMAT p size_t ITT_FORMAT d no args const wchar_t const wchar_t ITT_FORMAT s __itt_heap_function void size_t int ITT_FORMAT d __itt_heap_function void ITT_FORMAT p __itt_heap_function void void size_t int ITT_FORMAT d no args no args unsigned int ITT_FORMAT u const __itt_domain __itt_id ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain __itt_id ITT_FORMAT p const __itt_domain __itt_id __itt_timestamp __itt_timestamp ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain ITT_FORMAT p const __itt_domain __itt_string_handle unsigned long long ITT_FORMAT lu const __itt_domain __itt_id __itt_string_handle * key
atomic< hashcode_t > my_mask
Hash mask = sum of allocated segment sizes - 1.
bool operator!=(const hash_map_iterator< Container, T > &i, const hash_map_iterator< Container, U > &j)
concurrent_hash_map(const HashCompare &compare, const allocator_type &a=allocator_type())
HashCompare my_hash_compare
static hash_map_node_base *const rehash_req
Incompleteness flag value.
const_accessor()
Create empty result.
static void init_buckets(segment_ptr_t ptr, size_type sz, bool is_initial)
Initialize buckets.
segment_index_t insert_new_node(bucket *b, node_base *n, hashcode_t mask)
Insert a node and check for load factor.
segments_table_t my_table
Segment pointers table. Also prevents false sharing between my_mask and my_size.
allocator_traits< Alloc >::template rebind_alloc< T >::other type
friend bool is_write_access_needed(const_accessor const &)
void delete_node(node_base *n)
void move(tbb_thread &t1, tbb_thread &t2)
spin_rw_mutex mutex_t
Mutex type for buckets.
bool insert(const_accessor &result, value_type &&value)
Insert item by copying if there is no such key present already and acquire a read lock on the item...
Iterator::value_type value_type
static node * allocate_node_move_construct(node_allocator_type &allocator, const Key &key, const T *t)
hash_map_iterator operator++(int)
Post increment.
node(const Key &key, T &&t)
void const char const char int ITT_FORMAT __itt_group_sync s
void itt_hide_store_word(T &dst, T src)
#define __TBB_USE_OPTIONAL_RTTI
bucket * get_bucket(hashcode_t h) const
Get bucket by (masked) hashcode.
bool check_mask_race(const hashcode_t h, hashcode_t &m) const
Check for mask race.
concurrent_hash_map(size_type n, const allocator_type &a=allocator_type())
Construct empty table with n preallocated buckets. This number serves also as initial concurrency lev...
bool is_writer
If mutex!=NULL, then is_writer is true if holding a writer lock, false if holding a reader lock...
size_type max_size() const
Upper bound on size.
concurrent_hash_map(const concurrent_hash_map &table, const allocator_type &a=allocator_type())
Copy constructor.
hash_map_range(hash_map_range< U > &r)
type conversion
mutex_t::scoped_lock scoped_t
Scoped lock type for mutex.
bool insert(const_accessor &result, const value_type &value)
Insert item by copying if there is no such key present already and acquire a read lock on the item...
bool is_divisible() const
True if range can be partitioned into two subranges.
const Iterator & end() const
void mark_rehashed_levels(hashcode_t h)
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d void ITT_FORMAT p void ITT_FORMAT p __itt_model_site __itt_model_site_instance ITT_FORMAT p __itt_model_task __itt_model_task_instance ITT_FORMAT p void ITT_FORMAT p void ITT_FORMAT p void size_t ITT_FORMAT d void ITT_FORMAT p const wchar_t ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s no args void ITT_FORMAT p size_t ITT_FORMAT d no args const wchar_t const wchar_t ITT_FORMAT s __itt_heap_function void size_t int ITT_FORMAT d __itt_heap_function void ITT_FORMAT p __itt_heap_function void void size_t int ITT_FORMAT d no args no args unsigned int ITT_FORMAT u const __itt_domain __itt_id ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain __itt_id ITT_FORMAT p const __itt_domain __itt_id __itt_timestamp __itt_timestamp ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain ITT_FORMAT p const __itt_domain __itt_string_handle unsigned long long ITT_FORMAT lu const __itt_domain __itt_id __itt_string_handle __itt_metadata_type size_t void ITT_FORMAT p const __itt_domain __itt_id __itt_string_handle const wchar_t size_t ITT_FORMAT lu const __itt_domain __itt_id __itt_relation __itt_id ITT_FORMAT p const wchar_t int ITT_FORMAT __itt_group_mark d int
friend bool operator==(const hash_map_iterator< C, T > &i, const hash_map_iterator< C, U > &j)
friend const_accessor * accessor_location(const_accessor &a)
hash_map_base::size_type size_type
bool emplace(Args &&... args)
Insert item by copying if there is no such key present already.
concurrent_hash_map(std::initializer_list< value_type > il, const HashCompare &compare, const allocator_type &a=allocator_type())
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int mask
concurrent_hash_map(I first, I last, const HashCompare &compare, const allocator_type &a=allocator_type())
hash_map_range(hash_map_range &r, split)
Split range.
static segment_index_t segment_base(segment_index_t k)
bucket_accessor(concurrent_hash_map *base, const hashcode_t h, bool writer=false)
Dummy type that distinguishes splitting constructor from copy constructor.
internal::hash_map_range< const_iterator > const_range_type
void insert(I first, I last)
Insert range [first, last)
static node * allocate_node_copy_construct(node_allocator_type &allocator, const Key &key, const T *t)
concurrent_hash_map::value_type value_type
Type of value.
pointer allocate(size_type n, const void *hint=0)
Allocate space for n objects, starting on a cache/sector line.
concurrent_hash_map(size_type n, const HashCompare &compare, const allocator_type &a=allocator_type())
Base class for types that should not be copied or assigned.
Identifiers declared inside namespace internal should never be used directly by client code...
base class of concurrent_hash_map
Allows write access to elements and combines data access, locking, and garbage collection.
size_type grainsize() const
The grain size for this range.
range_type range(size_type grainsize=1)
static segment_index_t segment_index_of(size_type index)
const concurrent_hash_map::value_type value_type
Type of value.
void enable_segment(segment_index_t k, bool is_initial=false)
Enable segment.
size_type size() const
Number of items in table.
const_pointer operator->() const
Return pointer to associated value in hash table.
bool insert(accessor &result, const Key &key)
Insert item (if not already present) and acquire a write lock on the item.
concurrent_hash_map & operator=(const concurrent_hash_map &table)
Assignment.
const_iterator end() const
void __TBB_store_with_release(volatile T &location, V value)
node * my_node
Pointer to node that has current item.
atomic< T > & as_atomic(T &t)
bool insert(const_accessor &result, const Key &key)
Insert item (if not already present) and acquire a read lock on the item.
void acquire(concurrent_hash_map *base, const hashcode_t h, bool writer=false)
find a bucket by masked hashcode, optionally rehash, and acquire the lock
size_type bucket_count() const
Returns the current number of buckets.
size_t hashcode_t
Type of a hash code.
bool erase(const_accessor &item_accessor)
Erase item by const_accessor.
bool lookup(bool op_insert, const Key &key, const T *t, const_accessor *result, bool write, node *(*allocate_node)(node_allocator_type &, const Key &, const T *), node *tmp_n=0)
Insert or find item and optionally acquire a lock on the item.
friend const_accessor * accessor_location(accessor_not_used const &)
ptrdiff_t difference_type
Fast, unfair, spinning reader-writer lock with backoff and writer-preference.
size_t hashcode_t
Type of a hash code.
bool check_rehashing_collision(const hashcode_t h, hashcode_t m_old, hashcode_t m) const
Process mask race, check for rehashing collision.
Meets "allocator" requirements of ISO C++ Standard, Section 20.1.5.