17 #ifndef __TBB_concurrent_hash_map_H    18 #define __TBB_concurrent_hash_map_H    20 #define __TBB_concurrent_hash_map_H_include_area    27 #include __TBB_STD_SWAP_HEADER    38 #if __TBB_INITIALIZER_LISTS_PRESENT    39 #include <initializer_list>    41 #if TBB_USE_PERFORMANCE_WARNINGS || __TBB_STATISTICS    47 #if __TBB_CPP11_RVALUE_REF_PRESENT && __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT && __TBB_CPP11_TUPLE_PRESENT    55 namespace interface5 {
    57     template<
typename Key, 
typename T, 
typename HashCompare = tbb_hash_compare<Key>, 
typename A = tbb_allocator<std::pair<const Key, T> > >
   104         static size_type const embedded_buckets = 1<<embedded_block;
   120         bucket my_embedded_segment[embedded_buckets];
   122         atomic<unsigned> my_info_resizes; 
   123         mutable atomic<unsigned> my_info_restarts; 
   124         atomic<unsigned> my_info_rehashes;  
   128             std::memset(my_table, 0, 
sizeof(my_table));
   131             std::memset(my_embedded_segment, 0, 
sizeof(my_embedded_segment));
   132             for( 
size_type i = 0; i < embedded_block; i++ ) 
   133                 my_table[i] = my_embedded_segment + segment_base(i);
   134             my_mask = embedded_buckets - 1;
   135             __TBB_ASSERT( embedded_block <= first_block, 
"The first block number must include embedded blocks");
   138             my_info_restarts = 0; 
   139             my_info_rehashes = 0;  
   160             return reinterpret_cast<uintptr_t>(ptr) > uintptr_t(63);
   165             if( is_initial ) std::memset( static_cast<void*>(ptr), 0, sz*
sizeof(
bucket) );
   166             else for(
size_type i = 0; i < sz; i++, ptr++) {
   167                 *reinterpret_cast<intptr_t*>(&ptr->
mutex) = 0;
   184                 if( my_segment_ptr ) *my_segment_ptr = 0; 
   189         template<
typename Allocator>
   193             bucket_allocator_type bucket_allocator(allocator);
   197             __TBB_ASSERT( !is_valid(my_table[k]), 
"Wrong concurrent assignment");
   198             if( k >= first_block ) {
   199                 sz = segment_size( k );
   200                 segment_ptr_t ptr = bucket_allocator_traits::allocate(bucket_allocator, sz);
   201                 init_buckets( ptr, sz, is_initial );
   205                 __TBB_ASSERT( k == embedded_block, 
"Wrong segment index" );
   206                 sz = segment_size( first_block );
   207                 segment_ptr_t ptr = bucket_allocator_traits::allocate(bucket_allocator, sz - embedded_buckets);
   208                 init_buckets( ptr, sz - embedded_buckets, is_initial );
   209                 ptr -= segment_base(embedded_block);
   217         template<
typename Allocator>
   221             bucket_allocator_type bucket_allocator(allocator);
   225             if( 
s >= first_block) 
   226                 bucket_allocator_traits::deallocate(bucket_allocator, buckets_ptr, sz);
   227             else if( 
s == embedded_block && embedded_block != first_block )
   228                 bucket_allocator_traits::deallocate(bucket_allocator, buckets_ptr,
   229                                                     segment_size(first_block) - embedded_buckets);
   230             if( 
s >= embedded_block ) my_table[
s] = 0;
   236             h -= segment_base(
s);
   238             __TBB_ASSERT( is_valid(seg), 
"hashcode must be cut by valid mask for allocated segments" );
   258                 return check_rehashing_collision( 
h, m_old, m = m_now );
   265             if( (
h & m_old) != (
h & m) ) { 
   268                 for( ++m_old; !(
h & m_old); m_old <<= 1 ) 
   270                 m_old = (m_old<<1) - 1; 
   287             add_to_bucket( b, n );
   291                 __TBB_ASSERT( is_valid(my_table[new_seg-1]), 
"new allocations must not publish new mask until segment has allocated");
   294                   && 
as_atomic(my_table[new_seg]).compare_and_swap(is_allocating, NULL) == NULL )
   301         template<
typename Allocator>
   303             if( !buckets-- ) 
return;
   304             bool is_initial = !my_size;
   305             for( 
size_type m = my_mask; buckets > m; m = my_mask )
   306                 enable_segment( segment_index_of( m+1 ), allocator, is_initial );
   313             for(
size_type i = 0; i < embedded_buckets; i++)
   315             for(
size_type i = embedded_block; i < pointers_per_table; i++)
   319 #if __TBB_CPP11_RVALUE_REF_PRESENT   321             my_mask = other.my_mask;
   322             other.my_mask = embedded_buckets - 1;
   323             my_size = other.my_size;
   326             for(
size_type i = 0; i < embedded_buckets; ++i) {
   327                 my_embedded_segment[i].
node_list = other.my_embedded_segment[i].node_list;
   328                 other.my_embedded_segment[i].node_list = NULL;
   331             for(
size_type i = embedded_block; i < pointers_per_table; ++i) {
   332                 my_table[i] = other.my_table[i];
   333                 other.my_table[i] = NULL;
   336 #endif // __TBB_CPP11_RVALUE_REF_PRESENT   339     template<
typename Iterator>
   345     template<
typename Container, 
typename Value>
   347         : 
public std::iterator<std::forward_iterator_tag,Value>
   350         typedef typename Container::node 
node;
   354         template<
typename C, 
typename T, 
typename U>
   357         template<
typename C, 
typename T, 
typename U>
   360         template<
typename C, 
typename T, 
typename U>
   363         template<
typename C, 
typename U>
   370             size_t k = my_index+1;
   371             __TBB_ASSERT( my_bucket, 
"advancing an invalid iterator?");
   372             while( k <= my_map->my_mask ) {
   376                 else my_bucket = my_map->get_bucket( k );
   377                 my_node = static_cast<node*>( my_bucket->node_list );
   379                     my_index = k; 
return;
   383             my_bucket = 0; my_node = 0; my_index = k; 
   385 #if !defined(_MSC_VER) || defined(__INTEL_COMPILER)   386         template<
typename Key, 
typename T, 
typename HashCompare, 
typename A>
   391         const Container *my_map;
   409             my_map(other.my_map),
   410             my_index(other.my_index),
   411             my_bucket(other.my_bucket),
   412             my_node(other.my_node)
   422         Value& operator*()
 const {
   424             return my_node->value();
   437     template<
typename Container, 
typename Value>
   442         my_node( static_cast<
node*>(n) )
   448     template<
typename Container, 
typename Value>
   450         my_node = static_cast<node*>( my_node->next );
   451         if( !my_node ) advance_to_next_bucket();
   455     template<
typename Container, 
typename T, 
typename U>
   460     template<
typename Container, 
typename T, 
typename U>
   467     template<
typename Iterator>
   468     class hash_map_range {
   497             r.my_end = 
my_begin = r.my_midpoint;
   499             __TBB_ASSERT( !r.empty(), 
"Splitting despite the range is not divisible" );
   513             my_begin( Iterator( map, 0, map.my_embedded_segment, map.my_embedded_segment->node_list ) ),
   514             my_end( Iterator( map, map.my_mask + 1, 0, 0 ) ),
   517             __TBB_ASSERT( grainsize_>0, 
"grainsize must be positive" );
   526     template<
typename Iterator>
   529         size_t m = my_end.my_index-my_begin.my_index;
   530         if( m > my_grainsize ) {
   531             m = my_begin.my_index + m/2u;
   533             my_midpoint = Iterator(*my_begin.my_map,m,b,b->
node_list);
   535             my_midpoint = my_end;
   537         __TBB_ASSERT( my_begin.my_index <= my_midpoint.my_index,
   538             "my_begin is after my_midpoint" );
   540             "my_midpoint is after my_end" );
   541         __TBB_ASSERT( my_begin != my_midpoint || my_begin == my_end,
   542             "[my_begin, my_midpoint) range should not be empty" );
   548 #if _MSC_VER && !defined(__INTEL_COMPILER)   550     #pragma warning( push )   551     #pragma warning( disable: 4127 )   584 template<
typename Key, 
typename T, 
typename HashCompare, 
typename Allocator>
   586     template<
typename Container, 
typename Value>
   643 #if __TBB_CPP11_RVALUE_REF_PRESENT && __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT   644     template<
typename... Args>
   647     template<
typename Arg1, 
typename Arg2>
   654 #if __TBB_CPP11_RVALUE_REF_PRESENT && __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT   667 #if __TBB_CPP11_RVALUE_REF_PRESENT   674 #if __TBB_CPP11_RVALUE_REF_PRESENT && __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT && __TBB_CPP11_TUPLE_PRESENT   676         return create_node(allocator, std::piecewise_construct,
   677                            std::forward_as_tuple(
key), std::forward_as_tuple());
   687         __TBB_ASSERT(
false,
"this dummy function should not be called");
   694             n = static_cast<node*>( n->next );
   717         bool is_writer() { 
return bucket::scoped_t::is_writer; }
   725         __TBB_ASSERT( 
h > 1, 
"The lowermost buckets can't be rehashed" );
   741             bmask = bmask==0? 1 : ( 1u<<(
__TBB_Log2( bmask )+1 ) ) - 1; 
   742             __TBB_ASSERT( (c & bmask) == (
h & bmask), 
"hash() function changed for key in table" );
   744             if( (c & 
mask) == 
h ) {
   746                     if( !b_old.upgrade_to_writer() ) {
   869 #if __TBB_CPP11_RVALUE_REF_PRESENT   881         if (a == table.get_allocator()){
   885             internal_copy(std::make_move_iterator(table.begin()), std::make_move_iterator(table.end()), table.size());
   889 #endif //__TBB_CPP11_RVALUE_REF_PRESENT   910 #if __TBB_INITIALIZER_LISTS_PRESENT   915         call_clear_on_leave scope_guard(
this);
   917         scope_guard.dismiss();
   928 #endif //__TBB_INITIALIZER_LISTS_PRESENT   941 #if __TBB_CPP11_RVALUE_REF_PRESENT   950 #endif //__TBB_CPP11_RVALUE_REF_PRESENT   952 #if __TBB_INITIALIZER_LISTS_PRESENT   959 #endif //__TBB_INITIALIZER_LISTS_PRESENT  1024         return const_cast<concurrent_hash_map*>(
this)->lookup(
false, 
key, NULL, &result, 
false, &
do_not_allocate_node );
  1068 #if __TBB_CPP11_RVALUE_REF_PRESENT  1087 #if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT  1090     template<
typename... Args>
  1097     template<
typename... Args>
  1104     template<
typename... Args>
  1108 #endif //__TBB_CPP11_VARIADIC_TEMPLATES_PRESENT  1109 #endif //__TBB_CPP11_RVALUE_REF_PRESENT  1112     template<
typename I>
  1118 #if __TBB_INITIALIZER_LISTS_PRESENT  1119     void insert( std::initializer_list<value_type> il ) {
  1121         insert( il.begin(), il.end() );
  1123 #endif //__TBB_INITIALIZER_LISTS_PRESENT  1132         return exclude( item_accessor );
  1138         return exclude( item_accessor );
  1153 #if __TBB_CPP11_RVALUE_REF_PRESENT  1154     template<
typename Accessor>
  1160 #if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT  1161     template<
typename Accessor, 
typename... Args>
  1167 #endif //__TBB_CPP11_VARIADIC_TEMPLATES_PRESENT  1168 #endif //__TBB_CPP11_RVALUE_REF_PRESENT  1174     template<
typename I>
  1180     template<
typename I>
  1183 #if __TBB_CPP11_RVALUE_REF_PRESENT  1191         if (this->my_allocator == other.my_allocator) {
  1195             internal_copy(std::make_move_iterator(other.begin()), std::make_move_iterator(other.end()), other.size());
  1208         __TBB_ASSERT((m&(m+1))==0, 
"data structure is invalid");
  1214             if( 
lock.try_acquire( b->
mutex, 
true ) ) {
  1216                     const_cast<concurrent_hash_map*>(
this)->rehash_bucket( b, 
h & m ); 
  1230 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT  1234 template<
template<
typename...> 
typename Map, 
typename Key, 
typename T, 
typename... Args>
  1235 using hash_map_t = Map<
  1237     std::conditional_t< (
sizeof...(Args)>0) && !is_allocator_v< pack_element_t<0, Args...> >,
  1239     std::conditional_t< (
sizeof...(Args)>0) && is_allocator_v< pack_element_t<
sizeof...(Args)-1, Args...> >,
  1245 template<
typename I, 
typename... Args>
  1246 concurrent_hash_map(I, I, Args...)
  1247 -> internal::hash_map_t<concurrent_hash_map, internal::iterator_key_t<I>,internal::iterator_mapped_t<I>, Args...>;
  1251 template<
typename Key, 
typename T, 
typename CompareOrAllocator>
  1252 concurrent_hash_map(std::initializer_list<std::pair<const Key, T>>, CompareOrAllocator)
  1253 -> internal::hash_map_t<concurrent_hash_map, Key, T, CompareOrAllocator>;
  1257 template<
typename Key, 
typename T, 
typename HashCompare, 
typename A>
  1258 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 ) {
  1267         __TBB_ASSERT((m&(m+1))==0, 
"data structure is invalid");
  1268         return_value = 
false;
  1273         n = search_bucket( 
key, b() );
  1278                     tmp_n = allocate_node(my_allocator, 
key, t);
  1280                 if( !b.
is_writer() && !b.upgrade_to_writer() ) { 
  1282                     n = search_bucket( 
key, b() );
  1284                         b.downgrade_to_reader();
  1288                 if( check_mask_race(
h, m) )
  1291                 grow_segment = insert_new_node( b(), n = tmp_n, m );
  1293                 return_value = 
true;
  1297                 if( check_mask_race( 
h, m ) )
  1301             return_value = 
true;
  1304         if( !result ) 
goto check_growth;
  1307         if( !result->try_acquire( n->mutex, write ) ) {
  1309                 if( result->try_acquire( n->mutex, write ) ) 
break;
  1310                 if( !backoff.bounded_pause() ) {
  1313                     __TBB_ASSERT( !op_insert || !return_value, 
"Can't acquire new item in locked bucket?" );
  1325     if( grow_segment ) {
  1326 #if __TBB_STATISTICS  1329         enable_segment( grow_segment, my_allocator );
  1332         delete_node( tmp_n );
  1333     return return_value;
  1336 template<
typename Key, 
typename T, 
typename HashCompare, 
typename A>
  1337 template<
typename I>
  1341     __TBB_ASSERT((m&(m+1))==0, 
"data structure is invalid");
  1346         b = get_bucket( 
h &= m );
  1348     node *n = search_bucket( 
key, b );
  1350         return std::make_pair(end_, end_);
  1351     iterator lower(*
this, 
h, b, n), upper(lower);
  1352     return std::make_pair(lower, ++upper);
  1355 template<
typename Key, 
typename T, 
typename HashCompare, 
typename A>
  1365         while( *
p && *
p != n )
  1368             if( check_mask_race( 
h, m ) )
  1379         item_accessor.upgrade_to_writer(); 
  1385 template<
typename Key, 
typename T, 
typename HashCompare, 
typename A>
  1397         while( is_valid(n) && !my_hash_compare.equal(
key, static_cast<node*>(n)->value().first ) ) {
  1402             if( check_mask_race( 
h, m ) )
  1406         else if( !b.
is_writer() && !b.upgrade_to_writer() ) {
  1407             if( check_mask_race( 
h, m ) ) 
  1415         typename node::scoped_t item_locker( n->
mutex, 
true );
  1422 template<
typename Key, 
typename T, 
typename HashCompare, 
typename A>
  1429         internal_swap(table);
  1433 template<
typename Key, 
typename T, 
typename HashCompare, 
typename A>
  1435     reserve( sz, my_allocator ); 
  1439     bucket *bp = get_bucket( b ); 
  1440     for(; b <= 
mask; b++, bp++ ) {
  1443         __TBB_ASSERT( *reinterpret_cast<intptr_t*>(&bp->
mutex) == 0, 
"concurrent or unexpectedly terminated operation during rehash() execution" );
  1447                 __TBB_ASSERT( 
h > 1, 
"The lowermost buckets can't be rehashed" );
  1449                 b_old = get_bucket( 
h &= m );
  1452             mark_rehashed_levels( 
h ); 
  1455                 if( (c & 
mask) != 
h ) { 
  1459                     add_to_bucket( b_new, q );
  1460                 } 
else p = &q->next; 
  1464 #if TBB_USE_PERFORMANCE_WARNINGS  1465     int current_size = 
int(my_size), buckets = 
int(
mask)+1, empty_buckets = 0, overpopulated_buckets = 0; 
  1466     static bool reported = 
false;
  1468 #if TBB_USE_ASSERT || TBB_USE_PERFORMANCE_WARNINGS  1469     for( b = 0; b <= 
mask; b++ ) {
  1470         if( b & (b-2) ) ++bp; 
  1471         else bp = get_bucket( b );
  1473         __TBB_ASSERT( *reinterpret_cast<intptr_t*>(&bp->
mutex) == 0, 
"concurrent or unexpectedly terminated operation during rehash() execution" );
  1475 #if TBB_USE_PERFORMANCE_WARNINGS  1477         else if( n->
next ) overpopulated_buckets++;
  1480         for( ; is_valid(n); n = n->
next ) {
  1482             __TBB_ASSERT( 
h == b, 
"hash() function changed for key in table or internal error" );
  1486 #endif // TBB_USE_ASSERT || TBB_USE_PERFORMANCE_WARNINGS  1487 #if TBB_USE_PERFORMANCE_WARNINGS  1488     if( buckets > current_size) empty_buckets -= buckets - current_size;
  1489     else overpopulated_buckets -= current_size - buckets; 
  1490     if( !reported && buckets >= 512 && ( 2*empty_buckets > current_size || 2*overpopulated_buckets > current_size ) ) {
  1492             "Performance is not optimal because the hash function produces bad randomness in lower bits in %s.\nSize: %d  Empties: %d  Overlaps: %d",
  1494             typeid(*this).name(),
  1496             "concurrent_hash_map",
  1498             current_size, empty_buckets, overpopulated_buckets );
  1504 template<
typename Key, 
typename T, 
typename HashCompare, 
typename A>
  1507     __TBB_ASSERT((m&(m+1))==0, 
"data structure is invalid");
  1508 #if TBB_USE_ASSERT || TBB_USE_PERFORMANCE_WARNINGS || __TBB_STATISTICS  1509 #if TBB_USE_PERFORMANCE_WARNINGS || __TBB_STATISTICS  1510     int current_size = 
int(my_size), buckets = 
int(m)+1, empty_buckets = 0, overpopulated_buckets = 0; 
  1511     static bool reported = 
false;
  1516         if( b & (b-2) ) ++bp; 
  1517         else bp = get_bucket( b );
  1520         __TBB_ASSERT( *reinterpret_cast<intptr_t*>(&bp->
mutex) == 0, 
"concurrent or unexpectedly terminated operation during clear() execution" );
  1521 #if TBB_USE_PERFORMANCE_WARNINGS || __TBB_STATISTICS  1524         else if( n->
next ) overpopulated_buckets++;
  1526 #if __TBB_EXTRA_DEBUG  1527         for(; is_valid(n); n = n->
next ) {
  1534 #if TBB_USE_PERFORMANCE_WARNINGS || __TBB_STATISTICS  1535 #if __TBB_STATISTICS  1536     printf( 
"items=%d buckets: capacity=%d rehashed=%d empty=%d overpopulated=%d"  1537         " concurrent: resizes=%u rehashes=%u restarts=%u\n",
  1538         current_size, 
int(m+1), buckets, empty_buckets, overpopulated_buckets,
  1539         unsigned(my_info_resizes), 
unsigned(my_info_rehashes), 
unsigned(my_info_restarts) );
  1540     my_info_resizes = 0; 
  1541     my_info_restarts = 0; 
  1542     my_info_rehashes = 0;  
  1544     if( buckets > current_size) empty_buckets -= buckets - current_size;
  1545     else overpopulated_buckets -= current_size - buckets; 
  1546     if( !reported && buckets >= 512 && ( 2*empty_buckets > current_size || 2*overpopulated_buckets > current_size ) ) {
  1548             "Performance is not optimal because the hash function produces bad randomness in lower bits in %s.\nSize: %d  Empties: %d  Overlaps: %d",
  1550             typeid(*this).name(),
  1552             "concurrent_hash_map",
  1554             current_size, empty_buckets, overpopulated_buckets );
  1558 #endif // TBB_USE_ASSERT || TBB_USE_PERFORMANCE_WARNINGS || __TBB_STATISTICS  1561     __TBB_ASSERT( 
s+1 == pointers_per_table || !my_table[
s+1], 
"wrong mask or concurrent grow" );
  1563         __TBB_ASSERT( is_valid( my_table[
s] ), 
"wrong mask or concurrent grow" );
  1567             for( 
node_base *n = buckets_ptr[i].node_list; is_valid(n); n = buckets_ptr[i].
node_list ) {
  1571         delete_segment(
s, my_allocator);
  1573     my_mask = embedded_buckets - 1;
  1576 template<
typename Key, 
typename T, 
typename HashCompare, 
typename A>
  1579     if( my_mask == 
mask ) { 
  1580         reserve( source.
my_size, my_allocator ); 
  1581         bucket *dst = 0, *src = 0;
  1582         bool rehash_required = 
false;
  1584             if( k & (k-2) ) ++dst,src++; 
  1585             else { dst = get_bucket( k ); src = source.
get_bucket( k ); }
  1587             node *n = static_cast<node*>( src->node_list );
  1589                 rehash_required = 
true;
  1591             } 
else for(; n; n = static_cast<node*>( n->next ) ) {
  1592                 node* node_ptr = create_node(my_allocator, n->
value().first, n->
value().second);
  1593                 add_to_bucket( dst, node_ptr);
  1597         if( rehash_required ) rehash();
  1601 template<
typename Key, 
typename T, 
typename HashCompare, 
typename A>
  1602 template<
typename I>
  1604     reserve( reserve_size, my_allocator ); 
  1607         hashcode_t h = my_hash_compare.hash( (*first).first );
  1608         bucket *b = get_bucket( 
h & m );
  1610         node* node_ptr = create_node(my_allocator, (*first).first, (*first).second);
  1611         add_to_bucket( b, node_ptr );
  1621 template<
typename Key, 
typename T, 
typename HashCompare, 
typename A1, 
typename A2>
  1623     if(a.
size() != b.
size()) 
return false;
  1626     for(; i != i_end; ++i) {
  1628         if( j == j_end || !(i->second == j->second) ) 
return false;
  1633 template<
typename Key, 
typename T, 
typename HashCompare, 
typename A1, 
typename A2>
  1635 {    
return !(a == b); }
  1637 template<
typename Key, 
typename T, 
typename HashCompare, 
typename A>
  1641 #if _MSC_VER && !defined(__INTEL_COMPILER)  1642     #pragma warning( pop )  1643 #endif // warning 4127 is back  1648 #undef __TBB_concurrent_hash_map_H_include_area friend bool is_write_access_needed(const_accessor const &)
 
bool insert(const value_type &value)
Insert item by copying if there is no such key present already.
 
void internal_move_assign(concurrent_hash_map &&other, tbb::internal::traits_true_type)
 
static size_type segment_size(segment_index_t k)
 
std::pair< const_iterator, const_iterator > equal_range(const Key &key) const
 
bool insert(accessor &result, const Key &key)
Insert item (if not already present) and acquire a write lock on the item.
 
hash_map_node_base * next
Next node in chain.
 
reference operator *() const
Return reference to associated value in hash table.
 
void rehash(size_type n=0)
Rehashes and optionally resizes the whole table.
 
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
 
#define __TBB_USE_OPTIONAL_RTTI
 
const_reference operator *() const
Return reference to associated value in hash table.
 
std::pair< iterator, iterator > equal_range(const Key &key)
 
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.
 
static bool is_valid(void *ptr)
 
void allocator_swap(MyAlloc &my_allocator, OtherAlloc &other_allocator, traits_true_type)
 
internal::hash_map_iterator< concurrent_hash_map, const value_type > const_iterator
 
~const_accessor()
Destroy result after releasing the underlying reference.
 
bucket * get_bucket(hashcode_t h) const
Get bucket by (masked) hashcode.
 
concurrent_hash_map(const HashCompare &compare, const allocator_type &a=allocator_type())
 
~enable_segment_failsafe()
 
static segment_index_t segment_base(segment_index_t k)
 
const_accessor()
Create empty result.
 
void internal_move_assign(concurrent_hash_map &&other, tbb::internal::traits_false_type)
 
bool erase(const_accessor &item_accessor)
Erase item by const_accessor.
 
bucket accessor is to find, rehash, acquire a lock, and access a bucket
 
enable_segment_failsafe(segments_table_t &table, segment_index_t k)
 
hash_map_range(hash_map_range< U > &r)
type conversion
 
bool exclude(const_accessor &item_accessor)
delete item by accessor
 
hash_map_iterator operator++(int)
Post increment.
 
node_allocator_type my_allocator
 
hash_map_iterator & operator=(const hash_map_iterator< Container, typename Container::value_type > &other)
 
Meets requirements of a forward iterator for STL */.
 
Iterator::map_type map_type
 
friend const_accessor * accessor_location(const_accessor &a)
 
bool emplace(Args &&... args)
Insert item by copying if there is no such key present already.
 
concurrent_hash_map(const allocator_type &a=allocator_type())
Construct empty table.
 
void swap(concurrent_hash_map< Key, T, HashCompare, A > &a, concurrent_hash_map< Key, T, HashCompare, A > &b)
 
void allocator_move_assignment(MyAlloc &my_allocator, OtherAlloc &other_allocator, traits_true_type)
 
segment_ptr_t * my_segment_ptr
 
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_string_handle unsigned long long ITT_FORMAT lu const __itt_domain __itt_id __itt_string_handle * key
 
size_type count(const Key &key) const
Return count of items (0 or 1)
 
concurrent_hash_map(std::initializer_list< value_type > il, const HashCompare &compare, const allocator_type &a=allocator_type())
 
tbb::internal::allocator_rebind< Allocator, node >::type node_allocator_type
 
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.
 
size_t segment_index_t
Segment index type.
 
void internal_copy(const concurrent_hash_map &source)
Copy "source" to *this, where *this must start out empty.
 
void internal_move(hash_map_base &&other)
 
void itt_store_word_with_release(tbb::atomic< T > &dst, U src)
 
static node * create_node(node_allocator_type &allocator, Args &&... args)
 
hash_map_base()
Constructor.
 
Combines data access, locking, and garbage collection.
 
void release()
Set to null.
 
static segment_index_t segment_index_of(size_type index)
 
atomic< hashcode_t > my_mask
Hash mask = sum of allocated segment sizes - 1.
 
concurrent_hash_map(const concurrent_hash_map &table)
Copy constructor.
 
bool erase(const Key &key)
Erase item.
 
size_t my_index
Index in hash table for current item.
 
hash_map_iterator & operator++()
 
concurrent_hash_map(concurrent_hash_map &&table, const allocator_type &a)
Move constructor.
 
bucket * operator()()
get bucket pointer
 
void reserve(size_type buckets, const Allocator &allocator)
Prepare enough segments for number of buckets.
 
const Iterator & end() const
 
auto last(Container &c) -> decltype(begin(c))
 
allocator_type get_allocator() const
return allocator object
 
Fast, unfair, spinning reader-writer lock with backoff and writer-preference.
 
const_iterator end() const
 
bool check_rehashing_collision(const hashcode_t h, hashcode_t m_old, hashcode_t m) const
Process mask race, check for rehashing collision.
 
void delete_node(node_base *n)
 
allocator_traits< Alloc >::template rebind_alloc< T >::other type
 
size_type size() const
Number of items in table.
 
hash_map_range(const map_type &map, size_type grainsize_=1)
Init range with container and grainsize specified.
 
bool empty() const
True if size()==0.
 
friend bool is_write_access_needed(accessor_not_used const &)
 
static void construct(Alloc &, PT *p)
 
internal::hash_map_range< iterator > range_type
 
static pointer allocate(Alloc &a, size_type n)
 
hash_map_base::node_base node_base
 
Iterator::difference_type difference_type
 
bool operator!=(const cache_aligned_allocator< T > &, const cache_aligned_allocator< U > &)
 
void __TBB_store_with_release(volatile T &location, V value)
 
bool operator!=(const hash_map_iterator< Container, T > &i, const hash_map_iterator< Container, U > &j)
 
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 generic_move_insert(Accessor &&result, value_type &&value)
 
bool find(const_accessor &result, const Key &key) const
Find item and acquire a read lock on the item.
 
static hash_map_node_base *const rehash_req
Incompleteness flag value.
 
~concurrent_hash_map()
Clear table and destroy it.
 
bool insert(const_accessor &result, const Key &key)
Insert item (if not already present) and acquire a read lock on the item.
 
void allocator_copy_assignment(MyAlloc &my_allocator, OtherAlloc &other_allocator, traits_true_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 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_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
 
bool empty() const
True if range is empty.
 
bool is_divisible() const
True if range can be partitioned into two subranges.
 
concurrent_hash_map(const concurrent_hash_map &table, const allocator_type &a)
 
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.
 
const value_type * const_pointer
 
Range class used with concurrent_hash_map.
 
static void add_to_bucket(bucket *b, node_base *n)
Add node.
 
tick_count::interval_t operator-(const tick_count &t1, const tick_count &t0)
 
bool check_mask_race(const hashcode_t h, hashcode_t &m) const
Check for mask race.
 
bucket_accessor(concurrent_hash_map *base, const hashcode_t h, bool writer=false)
 
std::pair< const Key, T > value_type
 
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.
 
HashCompare my_hash_compare
 
mutex_t::scoped_lock scoped_t
Scoped lock type for mutex.
 
friend const_accessor * accessor_location(accessor_not_used const &)
 
range_type range(size_type grainsize=1)
 
ptrdiff_t difference_type
 
const Iterator & begin() const
 
bool find(accessor &result, const Key &key)
Find item and acquire a write lock on the item.
 
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.
 
Meets "allocator" requirements of ISO C++ Standard, Section 20.1.5.
 
void insert(I first, I last)
Insert range [first, last)
 
void mark_rehashed_levels(hashcode_t h)
 
pointer operator->() const
Return pointer to associated value in hash table.
 
T itt_hide_load_word(const T &src)
 
hash_map_base::size_type size_type
 
void const char const char int ITT_FORMAT __itt_group_sync p
 
static node * do_not_allocate_node(node_allocator_type &, const Key &, const T *)
 
void enable_segment(segment_index_t k, const Allocator &allocator, bool is_initial=false)
Enable segment.
 
const concurrent_hash_map::value_type value_type
Type of value.
 
bool operator==(const hash_map_iterator< Container, T > &i, const hash_map_iterator< Container, U > &j)
 
bool generic_emplace(Accessor &&result, Args &&... args)
 
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)
 
static node * allocate_node_copy_construct(node_allocator_type &allocator, const Key &key, const T *t)
 
void internal_swap(hash_map_base &table)
Swap hash_map_bases.
 
spin_rw_mutex mutex_t
Mutex type for buckets.
 
size_t hashcode_t
Type of a hash code.
 
static node * allocate_node_default_construct(node_allocator_type &allocator, const Key &key, const T *)
 
Dummy type that distinguishes splitting constructor from copy constructor.
 
internal::hash_map_iterator< concurrent_hash_map, value_type > iterator
 
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 max_size() const
Upper bound on size.
 
tbb::internal::false_type propagate_on_container_move_assignment
 
const_range_type range(size_type grainsize=1) const
 
Base class for types that should not be copied or assigned.
 
node * my_node
Pointer to node that has current item.
 
Allows write access to elements and combines data access, locking, and garbage collection.
 
node * search_bucket(const key_type &key, bucket *b) const
 
concurrent_hash_map * my_ch_map
 
const_iterator begin() const
 
static void deallocate(Alloc &a, pointer p, size_type n)
 
Class that implements exponential backoff.
 
const Container * my_map
concurrent_hash_map over which we are iterating.
 
concurrent_hash_map::value_type value_type
Type of value.
 
bool erase(accessor &item_accessor)
Erase item by accessor.
 
auto first(Container &c) -> decltype(begin(c))
 
Iterator::value_type value_type
 
hash_map_iterator()
Construct undefined iterator.
 
Value * operator->() const
 
static hash_map_node_base *const empty_rehashed
Rehashed empty bucket flag.
 
void rehash_bucket(bucket *b_new, const hashcode_t h)
 
static void destroy(Alloc &, T *p)
 
The scoped locking pattern.
 
void set_midpoint() const
Set my_midpoint to point approximately half way between my_begin and my_end.
 
hash_map_iterator(const hash_map_iterator< Container, typename Container::value_type > &other)
 
size_t size_type
Size type.
 
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.
 
concurrent_hash_map & operator=(const concurrent_hash_map &table)
Assignment.
 
void const char const char int ITT_FORMAT __itt_group_sync s
 
Class for determining type of std::allocator<T>::value_type.
 
intptr_t __TBB_Log2(uintptr_t x)
 
void delete_segment(segment_index_t s, const Allocator &allocator)
 
size_t hashcode_t
Type of a hash code.
 
concurrent_hash_map(size_type n, const HashCompare &compare, const allocator_type &a=allocator_type())
 
call_clear_on_leave(concurrent_hash_map *a_ch_map)
 
tbb::internal::allocator_traits< node_allocator_type > node_allocator_traits
 
bucket * segment_ptr_t
Segment pointer.
 
#define __TBB_FORWARDING_REF(A)
 
hash_map_node_base node_base
Node base type.
 
static node * allocate_node_move_construct(node_allocator_type &allocator, const Key &key, const T *t)
 
node_allocator_type & my_alloc
 
#define __TBB_ASSERT(predicate, comment)
No-op version of __TBB_ASSERT.
 
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
 
friend class const_accessor
 
T itt_load_word_with_acquire(const tbb::atomic< T > &src)
 
void itt_hide_store_word(T &dst, T src)
 
const bucket * my_bucket
Pointer to bucket.
 
hash_compare that is default argument for concurrent_hash_map
 
hash_map_range(hash_map_range &r, split)
Split range.
 
atomic< T > & as_atomic(T &t)
 
Unordered map from Key to T.
 
Iterator::reference reference
 
const_pointer internal_fast_find(const Key &key) const
Fast find when no concurrent erasure is used. For internal use inside TBB only!
 
atomic< size_type > my_size
Size of container in stored items.
 
hash_map_base::bucket bucket
 
std::size_t size_type
Type for size of a range.
 
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
 
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
 
concurrent_hash_map(I first, I last, const allocator_type &a=allocator_type())
Construction with copying iteration range and given allocator instance.
 
spin_rw_mutex mutex_t
Mutex type.
 
const value_type & const_reference
 
const_pointer operator->() const
Return pointer to associated value in hash table.
 
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.
 
bucket my_embedded_segment[embedded_buckets]
Zero segment.
 
T __TBB_load_with_acquire(const volatile T &location)
 
void advance_to_next_bucket()
 
friend bool is_write_access_needed(accessor const &)
 
bool empty() const
True if result is empty.
 
void __TBB_EXPORTED_FUNC runtime_warning(const char *format,...)
Report a runtime warning.
 
void swap(concurrent_hash_map &table)
swap two instances. Iterators are invalidated
 
bool insert(value_type &&value)
Insert item by copying if there is no such key present already.
 
concurrent_hash_map(I first, I last, const HashCompare &compare, const allocator_type &a=allocator_type())
 
size_type bucket_count() const
Returns the current number of buckets.
 
node_scoped_guard(node *n, node_allocator_type &alloc)
 
mutex_t::scoped_lock scoped_t
Scoped lock type for mutex.
 
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.
 
Identifiers declared inside namespace internal should never be used directly by client code.
 
bool is_writer()
check whether bucket is locked for write
 
tbb::aligned_space< value_type > my_value
 
internal::hash_map_range< const_iterator > const_range_type
 
segments_table_t my_table
Segment pointers table. Also prevents false sharing between my_mask and my_size.
 
size_type grainsize() const
The grain size for this range.
 
base class of concurrent_hash_map
 
bool operator==(const cache_aligned_allocator< T > &, const cache_aligned_allocator< U > &)
 
void move(tbb_thread &t1, tbb_thread &t2)