Intel(R) Threading Building Blocks Doxygen Documentation  version 4.2.3
concurrent_hash_map.h
Go to the documentation of this file.
1 /*
2  Copyright (c) 2005-2019 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 
18 
19 */
20 
21 #ifndef __TBB_concurrent_hash_map_H
22 #define __TBB_concurrent_hash_map_H
23 
24 #include "tbb_stddef.h"
25 #include <iterator>
26 #include <utility> // Need std::pair
27 #include <cstring> // Need std::memset
28 #include __TBB_STD_SWAP_HEADER
29 
31 #include "tbb_allocator.h"
32 #include "spin_rw_mutex.h"
33 #include "atomic.h"
34 #include "tbb_exception.h"
35 #include "tbb_profiling.h"
39 #if __TBB_INITIALIZER_LISTS_PRESENT
40 #include <initializer_list>
41 #endif
42 #if TBB_USE_PERFORMANCE_WARNINGS || __TBB_STATISTICS
43 #include <typeinfo>
44 #endif
45 #if __TBB_STATISTICS
46 #include <stdio.h>
47 #endif
48 
49 namespace tbb {
50 
51 namespace interface5 {
52 
53  template<typename Key, typename T, typename HashCompare = tbb_hash_compare<Key>, typename A = tbb_allocator<std::pair<const Key, T> > >
55 
57  namespace internal {
58  using namespace tbb::internal;
59 
60 
62  typedef size_t hashcode_t;
71  mutex_t mutex;
72  };
74  static hash_map_node_base *const rehash_req = reinterpret_cast<hash_map_node_base*>(size_t(3));
76  static hash_map_node_base *const empty_rehashed = reinterpret_cast<hash_map_node_base*>(size_t(0));
78  class hash_map_base {
79  public:
81  typedef size_t size_type;
83  typedef size_t hashcode_t;
85  typedef size_t segment_index_t;
94  mutex_t mutex;
95  node_base *node_list;
96  };
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; //including embedded_block. perfect with bucket size 16, so the allocations are power of 4096
104  static size_type const pointers_per_table = sizeof(segment_index_t) * 8; // one segment per bit
108  typedef segment_ptr_t segments_table_t[pointers_per_table];
112  segments_table_t my_table;
114  atomic<size_type> my_size; // It must be in separate cache line from my_mask due to performance effects
116  bucket my_embedded_segment[embedded_buckets];
117 #if __TBB_STATISTICS
118  atomic<unsigned> my_info_resizes; // concurrent ones
119  mutable atomic<unsigned> my_info_restarts; // race collisions
120  atomic<unsigned> my_info_rehashes; // invocations of rehash_bucket
121 #endif
122  hash_map_base() {
124  std::memset( this, 0, pointers_per_table*sizeof(segment_ptr_t) // 32*4=128 or 64*8=512
125  + sizeof(my_size) + sizeof(my_mask) // 4+4 or 8+8
126  + embedded_buckets*sizeof(bucket) ); // n*8 or n*16
127  for( size_type i = 0; i < embedded_block; i++ ) // fill the table
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");
131 #if __TBB_STATISTICS
132  my_info_resizes = 0; // concurrent ones
133  my_info_restarts = 0; // race collisions
134  my_info_rehashes = 0; // invocations of rehash_bucket
135 #endif
136  }
137 
139  static segment_index_t segment_index_of( size_type index ) {
140  return segment_index_t( __TBB_Log2( index|1 ) );
141  }
142 
144  static segment_index_t segment_base( segment_index_t k ) {
145  return (segment_index_t(1)<<k & ~segment_index_t(1));
146  }
147 
149  static size_type segment_size( segment_index_t k ) {
150  return size_type(1)<<k; // fake value for k==0
151  }
152 
154  static bool is_valid( void *ptr ) {
155  return reinterpret_cast<uintptr_t>(ptr) > uintptr_t(63);
156  }
157 
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;
163  ptr->node_list = rehash_req;
164  }
165  }
166 
168  static void add_to_bucket( bucket *b, node_base *n ) {
169  __TBB_ASSERT(b->node_list != rehash_req, NULL);
170  n->next = b->node_list;
171  b->node_list = n; // its under lock and flag is set
172  }
173 
176  segment_ptr_t *my_segment_ptr;
177  enable_segment_failsafe(segments_table_t &table, segment_index_t k) : my_segment_ptr(&table[k]) {}
179  if( my_segment_ptr ) *my_segment_ptr = 0; // indicate no allocation in progress
180  }
181  };
182 
184  void enable_segment( segment_index_t k, bool is_initial = false ) {
185  __TBB_ASSERT( k, "Zero segment must be embedded" );
186  enable_segment_failsafe watchdog( my_table, k );
188  size_type sz;
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 );
194  itt_hide_store_word( my_table[k], ptr );
195  sz <<= 1;// double it to get entire capacity of the container
196  } else { // the first block
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++) // calc the offsets
203  itt_hide_store_word( my_table[i], ptr + segment_base(i) );
204  }
205  itt_store_word_with_release( my_mask, sz-1 );
206  watchdog.my_segment_ptr = 0;
207  }
208 
210  bucket *get_bucket( hashcode_t h ) const throw() { // TODO: add throw() everywhere?
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" );
215  return &seg[h];
216  }
217 
218  // internal serial rehashing helper
219  void mark_rehashed_levels( hashcode_t h ) throw () {
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 ) {
223  seg[h].node_list = empty_rehashed;
224  mark_rehashed_levels( h + ((hashcode_t)1<<s) ); // optimized segment_base(s)
225  }
226  }
227 
229  // Splitting into two functions should help inlining
230  inline bool check_mask_race( const hashcode_t h, hashcode_t &m ) const {
231  hashcode_t m_now, m_old = m;
232  m_now = (hashcode_t) itt_load_word_with_acquire( my_mask );
233  if( m_old != m_now )
234  return check_rehashing_collision( h, m_old, m = m_now );
235  return false;
236  }
237 
239  bool check_rehashing_collision( const hashcode_t h, hashcode_t m_old, hashcode_t m ) const {
240  __TBB_ASSERT(m_old != m, NULL); // TODO?: m arg could be optimized out by passing h = h&m
241  if( (h & m_old) != (h & m) ) { // mask changed for this hashcode, rare event
242  // condition above proves that 'h' has some other bits set beside 'm_old'
243  // find next applicable mask after m_old //TODO: look at bsl instruction
244  for( ++m_old; !(h & m_old); m_old <<= 1 ) // at maximum few rounds depending on the first block size
245  ;
246  m_old = (m_old<<1) - 1; // get full mask from a bit
247  __TBB_ASSERT((m_old&(m_old+1))==0 && m_old <= m, NULL);
248  // check whether it is rehashing/ed
249  if( itt_load_word_with_acquire(get_bucket(h & m_old)->node_list) != rehash_req )
250  {
251 #if __TBB_STATISTICS
252  my_info_restarts++; // race collisions
253 #endif
254  return true;
255  }
256  }
257  return false;
258  }
259 
261  segment_index_t insert_new_node( bucket *b, node_base *n, hashcode_t mask ) {
262  size_type sz = ++my_size; // prefix form is to enforce allocation after the first item inserted
263  add_to_bucket( b, n );
264  // check load factor
265  if( sz >= mask ) { // TODO: add custom load_factor
266  segment_index_t new_seg = __TBB_Log2( mask+1 ); //optimized segment_index_of
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;
269  if( !itt_hide_load_word(my_table[new_seg])
270  && as_atomic(my_table[new_seg]).compare_and_swap(is_allocating, NULL) == NULL )
271  return new_seg; // The value must be processed
272  }
273  return 0;
274  }
275 
277  void reserve(size_type buckets) {
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 );
282  }
285  using std::swap;
286  swap(this->my_mask, table.my_mask);
287  swap(this->my_size, table.my_size);
288  for(size_type i = 0; i < embedded_buckets; i++)
289  swap(this->my_embedded_segment[i].node_list, table.my_embedded_segment[i].node_list);
290  for(size_type i = embedded_block; i < pointers_per_table; i++)
291  swap(this->my_table[i], table.my_table[i]);
292  }
293  };
294 
295  template<typename Iterator>
297 
299 
301  template<typename Container, typename Value>
303  : public std::iterator<std::forward_iterator_tag,Value>
304  {
305  typedef Container map_type;
306  typedef typename Container::node node;
309 
310  template<typename C, typename T, typename U>
311  friend bool operator==( const hash_map_iterator<C,T>& i, const hash_map_iterator<C,U>& j );
312 
313  template<typename C, typename T, typename U>
314  friend bool operator!=( const hash_map_iterator<C,T>& i, const hash_map_iterator<C,U>& j );
315 
316  template<typename C, typename T, typename U>
317  friend ptrdiff_t operator-( const hash_map_iterator<C,T>& i, const hash_map_iterator<C,U>& j );
318 
319  template<typename C, typename U>
320  friend class hash_map_iterator;
321 
322  template<typename I>
323  friend class hash_map_range;
324 
325  void advance_to_next_bucket() { // TODO?: refactor to iterator_base class
326  size_t k = my_index+1;
327  __TBB_ASSERT( my_bucket, "advancing an invalid iterator?");
328  while( k <= my_map->my_mask ) {
329  // Following test uses 2's-complement wizardry
330  if( k&(k-2) ) // not the beginning of a segment
331  ++my_bucket;
332  else my_bucket = my_map->get_bucket( k );
333  my_node = static_cast<node*>( my_bucket->node_list );
334  if( hash_map_base::is_valid(my_node) ) {
335  my_index = k; return;
336  }
337  ++k;
338  }
339  my_bucket = 0; my_node = 0; my_index = k; // the end
340  }
341 #if !defined(_MSC_VER) || defined(__INTEL_COMPILER)
342  template<typename Key, typename T, typename HashCompare, typename A>
344 #else
345  public: // workaround
346 #endif
347  const Container *my_map;
349 
351  size_t my_index;
352 
354  const bucket *my_bucket;
355 
357  node *my_node;
358 
359  hash_map_iterator( const Container &map, size_t index, const bucket *b, node_base *n );
360 
361  public:
363  hash_map_iterator(): my_map(), my_index(), my_bucket(), my_node() {}
365  my_map(other.my_map),
366  my_index(other.my_index),
367  my_bucket(other.my_bucket),
368  my_node(other.my_node)
369  {}
370  Value& operator*() const {
371  __TBB_ASSERT( hash_map_base::is_valid(my_node), "iterator uninitialized or at end of container?" );
372  return my_node->item;
373  }
374  Value* operator->() const {return &operator*();}
375  hash_map_iterator& operator++();
376 
379  hash_map_iterator old(*this);
380  operator++();
381  return old;
382  }
383  };
384 
385  template<typename Container, typename Value>
386  hash_map_iterator<Container,Value>::hash_map_iterator( const Container &map, size_t index, const bucket *b, node_base *n ) :
387  my_map(&map),
388  my_index(index),
389  my_bucket(b),
390  my_node( static_cast<node*>(n) )
391  {
392  if( b && !hash_map_base::is_valid(n) )
394  }
395 
396  template<typename Container, typename Value>
398  my_node = static_cast<node*>( my_node->next );
400  return *this;
401  }
402 
403  template<typename Container, typename T, typename U>
405  return i.my_node == j.my_node && i.my_map == j.my_map;
406  }
407 
408  template<typename Container, typename T, typename U>
410  return i.my_node != j.my_node || i.my_map != j.my_map;
411  }
412 
414 
415  template<typename Iterator>
416  class hash_map_range {
417  typedef typename Iterator::map_type map_type;
418  Iterator my_begin;
419  Iterator my_end;
420  mutable Iterator my_midpoint;
421  size_t my_grainsize;
423  void set_midpoint() const;
424  template<typename U> friend class hash_map_range;
425  public:
427  typedef std::size_t size_type;
428  typedef typename Iterator::value_type value_type;
429  typedef typename Iterator::reference reference;
430  typedef typename Iterator::difference_type difference_type;
431  typedef Iterator iterator;
432 
434  bool empty() const {return my_begin==my_end;}
435 
437  bool is_divisible() const {
438  return my_midpoint!=my_end;
439  }
442  my_end(r.my_end),
443  my_grainsize(r.my_grainsize)
444  {
445  r.my_end = my_begin = r.my_midpoint;
446  __TBB_ASSERT( !empty(), "Splitting despite the range is not divisible" );
447  __TBB_ASSERT( !r.empty(), "Splitting despite the range is not divisible" );
448  set_midpoint();
449  r.set_midpoint();
450  }
452  template<typename U>
454  my_begin(r.my_begin),
455  my_end(r.my_end),
456  my_midpoint(r.my_midpoint),
457  my_grainsize(r.my_grainsize)
458  {}
460  hash_map_range( const map_type &map, size_type grainsize_ = 1 ) :
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_ )
464  {
465  __TBB_ASSERT( grainsize_>0, "grainsize must be positive" );
466  set_midpoint();
467  }
468  const Iterator& begin() const {return my_begin;}
469  const Iterator& end() const {return my_end;}
471  size_type grainsize() const {return my_grainsize;}
472  };
473 
474  template<typename Iterator>
476  // Split by groups of nodes
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;
480  hash_map_base::bucket *b = my_begin.my_map->get_bucket(m);
481  my_midpoint = Iterator(*my_begin.my_map,m,b,b->node_list);
482  } else {
483  my_midpoint = my_end;
484  }
485  __TBB_ASSERT( my_begin.my_index <= my_midpoint.my_index,
486  "my_begin is after my_midpoint" );
487  __TBB_ASSERT( my_midpoint.my_index <= my_end.my_index,
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" );
491  }
492 
493  } // internal
495 
496 #if _MSC_VER && !defined(__INTEL_COMPILER)
497  // Suppress "conditional expression is constant" warning.
498  #pragma warning( push )
499  #pragma warning( disable: 4127 )
500 #endif
501 
503 
532 template<typename Key, typename T, typename HashCompare, typename Allocator>
533 class concurrent_hash_map : protected internal::hash_map_base {
534  template<typename Container, typename Value>
535  friend class internal::hash_map_iterator;
536 
537  template<typename I>
538  friend class internal::hash_map_range;
539 
540 public:
541  typedef Key key_type;
542  typedef T mapped_type;
543  typedef std::pair<const Key,T> value_type;
545  typedef ptrdiff_t difference_type;
546  typedef value_type *pointer;
547  typedef const value_type *const_pointer;
548  typedef value_type &reference;
549  typedef const value_type &const_reference;
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;
552  typedef internal::hash_map_range<iterator> range_type;
553  typedef internal::hash_map_range<const_iterator> const_range_type;
554  typedef Allocator allocator_type;
555 
556 protected:
557  friend class const_accessor;
558  struct node;
560  node_allocator_type my_allocator;
561  HashCompare my_hash_compare;
562 
563  struct node : public node_base {
564  value_type item;
565  node( const Key &key ) : item(key, T()) {}
566  node( const Key &key, const T &t ) : item(key, t) {}
567 #if __TBB_CPP11_RVALUE_REF_PRESENT
568  node( const Key &key, T &&t ) : item(key, std::move(t)) {}
569  node( value_type&& i ) : item(std::move(i)){}
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) {}
579 
580  // exception-safe allocation, see C++ Standard 2003, clause 5.3.4p17
581  void *operator new( size_t /*size*/, node_allocator_type &a ) {
582  void *ptr = a.allocate(1);
583  if(!ptr)
585  return ptr;
586  }
587  // match placement-new form above to be called if exception thrown in constructor
588  void operator delete( void *ptr, node_allocator_type &a ) { a.deallocate(static_cast<node*>(ptr),1); }
589  };
590 
591  void delete_node( node_base *n ) {
592  my_allocator.destroy( static_cast<node*>(n) );
593  my_allocator.deallocate( static_cast<node*>(n), 1);
594  }
595 
596  static node* allocate_node_copy_construct(node_allocator_type& allocator, const Key &key, const T * t){
597  return new( allocator ) node(key, *t);
598  }
599 
600 #if __TBB_CPP11_RVALUE_REF_PRESENT
601  static node* allocate_node_move_construct(node_allocator_type& allocator, const Key &key, const T * t){
602  return new( allocator ) node(key, std::move(*const_cast<T*>(t)));
603  }
604 #if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
605  template<typename... Args>
606  static node* allocate_node_emplace_construct(node_allocator_type& allocator, Args&&... args){
607  return new( allocator ) node(std::forward<Args>(args)...);
608  }
609 #endif //__TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
610 #endif //__TBB_CPP11_RVALUE_REF_PRESENT
611 
612  static node* allocate_node_default_construct(node_allocator_type& allocator, const Key &key, const T * ){
613  return new( allocator ) node(key);
614  }
615 
616  static node* do_not_allocate_node(node_allocator_type& , const Key &, const T * ){
617  __TBB_ASSERT(false,"this dummy function should not be called");
618  return NULL;
619  }
620 
621  node *search_bucket( const key_type &key, bucket *b ) const {
622  node *n = static_cast<node*>( b->node_list );
623  while( is_valid(n) && !my_hash_compare.equal(key, n->item.first) )
624  n = static_cast<node*>( n->next );
625  __TBB_ASSERT(n != internal::rehash_req, "Search can be executed only for rehashed bucket");
626  return n;
627  }
628 
632  public:
633  bucket_accessor( concurrent_hash_map *base, const hashcode_t h, bool writer = false ) { acquire( base, h, writer ); }
635  inline void acquire( concurrent_hash_map *base, const hashcode_t h, bool writer = false ) {
636  my_b = base->get_bucket( h );
637  // TODO: actually, notification is unnecessary here, just hiding double-check
638  if( itt_load_word_with_acquire(my_b->node_list) == internal::rehash_req
639  && try_acquire( my_b->mutex, /*write=*/true ) )
640  {
641  if( my_b->node_list == internal::rehash_req ) base->rehash_bucket( my_b, h ); //recursive rehashing
642  }
643  else bucket::scoped_t::acquire( my_b->mutex, writer );
644  __TBB_ASSERT( my_b->node_list != internal::rehash_req, NULL);
645  }
649  bucket *operator() () { return my_b; }
650  };
651 
652  // TODO refactor to hash_base
653  void rehash_bucket( bucket *b_new, const hashcode_t h ) {
654  __TBB_ASSERT( *(intptr_t*)(&b_new->mutex), "b_new must be locked (for write)");
655  __TBB_ASSERT( h > 1, "The lowermost buckets can't be rehashed" );
657  hashcode_t mask = ( 1u<<__TBB_Log2( h ) ) - 1; // get parent mask from the topmost bit
658 #if __TBB_STATISTICS
659  my_info_rehashes++; // invocations of rehash_bucket
660 #endif
661 
662  bucket_accessor b_old( this, h & mask );
663 
664  mask = (mask<<1) | 1; // get full mask for new bucket
665  __TBB_ASSERT( (mask&(mask+1))==0 && (h & mask) == h, NULL );
666  restart:
667  for( node_base **p = &b_old()->node_list, *n = __TBB_load_with_acquire(*p); is_valid(n); n = *p ) {
668  hashcode_t c = my_hash_compare.hash( static_cast<node*>(n)->item.first );
669 #if TBB_USE_ASSERT
670  hashcode_t bmask = h & (mask>>1);
671  bmask = bmask==0? 1 : ( 1u<<(__TBB_Log2( bmask )+1 ) ) - 1; // minimal mask of parent bucket
672  __TBB_ASSERT( (c & bmask) == (h & bmask), "hash() function changed for key in table" );
673 #endif
674  if( (c & mask) == h ) {
675  if( !b_old.is_writer() )
676  if( !b_old.upgrade_to_writer() ) {
677  goto restart; // node ptr can be invalid due to concurrent erase
678  }
679  *p = n->next; // exclude from b_old
680  add_to_bucket( b_new, n );
681  } else p = &n->next; // iterate to next item
682  }
683  }
684 
687  call_clear_on_leave( concurrent_hash_map* a_ch_map ) : my_ch_map(a_ch_map) {}
688  void dismiss() {my_ch_map = 0;}
690  if (my_ch_map){
691  my_ch_map->clear();
692  }
693  }
694  };
695 public:
696 
697  class accessor;
699  class const_accessor : private node::scoped_t /*which derived from no_copy*/ {
700  friend class concurrent_hash_map<Key,T,HashCompare,Allocator>;
701  friend class accessor;
702  public:
705 
707  bool empty() const { return !my_node; }
708 
710  void release() {
711  if( my_node ) {
713  my_node = 0;
714  }
715  }
716 
718  const_reference operator*() const {
719  __TBB_ASSERT( my_node, "attempt to dereference empty accessor" );
720  return my_node->item;
721  }
722 
724  const_pointer operator->() const {
725  return &operator*();
726  }
727 
729  const_accessor() : my_node(NULL) {}
730 
733  my_node = NULL; // scoped lock's release() is called in its destructor
734  }
735  protected:
736  bool is_writer() { return node::scoped_t::is_writer; }
738  hashcode_t my_hash;
739  };
740 
742  class accessor: public const_accessor {
743  public:
746 
748  reference operator*() const {
749  __TBB_ASSERT( this->my_node, "attempt to dereference empty accessor" );
750  return this->my_node->item;
751  }
752 
754  pointer operator->() const {
755  return &operator*();
756  }
757  };
758 
760  explicit concurrent_hash_map( const allocator_type &a = allocator_type() )
761  : internal::hash_map_base(), my_allocator(a)
762  {}
763 
764  explicit concurrent_hash_map( const HashCompare& compare, const allocator_type& a = allocator_type() )
765  : internal::hash_map_base(), my_allocator(a), my_hash_compare(compare)
766  {}
767 
769  concurrent_hash_map( size_type n, const allocator_type &a = allocator_type() )
770  : internal::hash_map_base(), my_allocator(a)
771  {
772  reserve( n );
773  }
774 
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)
777  {
778  reserve( n );
779  }
780 
782  concurrent_hash_map( const concurrent_hash_map &table, const allocator_type &a = allocator_type() )
783  : internal::hash_map_base(), my_allocator(a)
784  {
785  call_clear_on_leave scope_guard(this);
786  internal_copy(table);
787  scope_guard.dismiss();
788  }
789 
790 #if __TBB_CPP11_RVALUE_REF_PRESENT
793  : internal::hash_map_base(), my_allocator(std::move(table.get_allocator()))
794  {
795  swap(table);
796  }
797 
799  concurrent_hash_map( concurrent_hash_map &&table, const allocator_type &a )
800  : internal::hash_map_base(), my_allocator(a)
801  {
802  if (a == table.get_allocator()){
803  this->swap(table);
804  }else{
805  call_clear_on_leave scope_guard(this);
806  internal_copy(std::make_move_iterator(table.begin()), std::make_move_iterator(table.end()), table.size());
807  scope_guard.dismiss();
808  }
809  }
810 #endif //__TBB_CPP11_RVALUE_REF_PRESENT
811 
813  template<typename I>
814  concurrent_hash_map( I first, I last, const allocator_type &a = allocator_type() )
815  : internal::hash_map_base(), my_allocator(a)
816  {
817  call_clear_on_leave scope_guard(this);
818  internal_copy(first, last, std::distance(first, last));
819  scope_guard.dismiss();
820  }
821 
822  template<typename I>
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)
825  {
826  call_clear_on_leave scope_guard(this);
827  internal_copy(first, last, std::distance(first, last));
828  scope_guard.dismiss();
829  }
830 
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)
835  {
836  call_clear_on_leave scope_guard(this);
837  internal_copy(il.begin(), il.end(), il.size());
838  scope_guard.dismiss();
839  }
840 
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)
843  {
844  call_clear_on_leave scope_guard(this);
845  internal_copy(il.begin(), il.end(), il.size());
846  scope_guard.dismiss();
847  }
848 
849 #endif //__TBB_INITIALIZER_LISTS_PRESENT
850 
853  if( this!=&table ) {
854  clear();
855  internal_copy(table);
856  }
857  return *this;
858  }
859 
860 #if __TBB_CPP11_RVALUE_REF_PRESENT
861  concurrent_hash_map& operator=( concurrent_hash_map &&table ) {
863  if(this != &table) {
865  if(pocma_t::value || this->my_allocator == table.my_allocator) {
866  concurrent_hash_map trash (std::move(*this));
867  //TODO: swapping allocators here may be a problem, replace with single direction moving iff pocma is set
868  this->swap(table);
869  } else {
870  //do per element move
871  concurrent_hash_map moved_copy(std::move(table), this->my_allocator);
872  this->swap(moved_copy);
873  }
874  }
875  return *this;
876  }
877 #endif //__TBB_CPP11_RVALUE_REF_PRESENT
878 
879 #if __TBB_INITIALIZER_LISTS_PRESENT
880  concurrent_hash_map& operator=( std::initializer_list<value_type> il ) {
882  clear();
883  internal_copy(il.begin(), il.end(), il.size());
884  return *this;
885  }
886 #endif //__TBB_INITIALIZER_LISTS_PRESENT
887 
888 
890 
892  void rehash(size_type n = 0);
893 
895  void clear();
896 
898  ~concurrent_hash_map() { clear(); }
899 
900  //------------------------------------------------------------------------
901  // Parallel algorithm support
902  //------------------------------------------------------------------------
903  range_type range( size_type grainsize=1 ) {
904  return range_type( *this, grainsize );
905  }
906  const_range_type range( size_type grainsize=1 ) const {
907  return const_range_type( *this, grainsize );
908  }
909 
910  //------------------------------------------------------------------------
911  // STL support - not thread-safe methods
912  //------------------------------------------------------------------------
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() ); }
919 
921  size_type size() const { return my_size; }
922 
924  bool empty() const { return my_size == 0; }
925 
927  size_type max_size() const {return (~size_type(0))/sizeof(node);}
928 
930  size_type bucket_count() const { return my_mask+1; }
931 
933  allocator_type get_allocator() const { return this->my_allocator; }
934 
936  void swap( concurrent_hash_map &table );
937 
938  //------------------------------------------------------------------------
939  // concurrent map operations
940  //------------------------------------------------------------------------
941 
943  size_type count( const Key &key ) const {
944  return const_cast<concurrent_hash_map*>(this)->lookup(/*insert*/false, key, NULL, NULL, /*write=*/false, &do_not_allocate_node );
945  }
946 
948 
949  bool find( const_accessor &result, const Key &key ) const {
950  result.release();
951  return const_cast<concurrent_hash_map*>(this)->lookup(/*insert*/false, key, NULL, &result, /*write=*/false, &do_not_allocate_node );
952  }
953 
955 
956  bool find( accessor &result, const Key &key ) {
957  result.release();
958  return lookup(/*insert*/false, key, NULL, &result, /*write=*/true, &do_not_allocate_node );
959  }
960 
962 
963  bool insert( const_accessor &result, const Key &key ) {
964  result.release();
965  return lookup(/*insert*/true, key, NULL, &result, /*write=*/false, &allocate_node_default_construct );
966  }
967 
969 
970  bool insert( accessor &result, const Key &key ) {
971  result.release();
972  return lookup(/*insert*/true, key, NULL, &result, /*write=*/true, &allocate_node_default_construct );
973  }
974 
976 
977  bool insert( const_accessor &result, const value_type &value ) {
978  result.release();
979  return lookup(/*insert*/true, value.first, &value.second, &result, /*write=*/false, &allocate_node_copy_construct );
980  }
981 
983 
984  bool insert( accessor &result, const value_type &value ) {
985  result.release();
986  return lookup(/*insert*/true, value.first, &value.second, &result, /*write=*/true, &allocate_node_copy_construct );
987  }
988 
990 
991  bool insert( const value_type &value ) {
992  return lookup(/*insert*/true, value.first, &value.second, NULL, /*write=*/false, &allocate_node_copy_construct );
993  }
994 
995 #if __TBB_CPP11_RVALUE_REF_PRESENT
996 
998  bool insert( const_accessor &result, value_type && value ) {
999  return generic_move_insert(result, std::move(value));
1000  }
1001 
1003 
1004  bool insert( accessor &result, value_type && value ) {
1005  return generic_move_insert(result, std::move(value));
1006  }
1007 
1009 
1010  bool insert( value_type && value ) {
1011  return generic_move_insert(accessor_not_used(), std::move(value));
1012  }
1013 
1014 #if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
1015 
1017  template<typename... Args>
1018  bool emplace( const_accessor &result, Args&&... args ) {
1019  return generic_emplace(result, std::forward<Args>(args)...);
1020  }
1021 
1023 
1024  template<typename... Args>
1025  bool emplace( accessor &result, Args&&... args ) {
1026  return generic_emplace(result, std::forward<Args>(args)...);
1027  }
1028 
1030 
1031  template<typename... Args>
1032  bool emplace( Args&&... args ) {
1033  return generic_emplace(accessor_not_used(), std::forward<Args>(args)...);
1034  }
1035 #endif //__TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
1036 #endif //__TBB_CPP11_RVALUE_REF_PRESENT
1037 
1039  template<typename I>
1040  void insert( I first, I last ) {
1041  for ( ; first != last; ++first )
1042  insert( *first );
1043  }
1044 
1045 #if __TBB_INITIALIZER_LISTS_PRESENT
1046  void insert( std::initializer_list<value_type> il ) {
1048  insert( il.begin(), il.end() );
1049  }
1050 #endif //__TBB_INITIALIZER_LISTS_PRESENT
1051 
1053 
1054  bool erase( const Key& key );
1055 
1057 
1058  bool erase( const_accessor& item_accessor ) {
1059  return exclude( item_accessor );
1060  }
1061 
1063 
1064  bool erase( accessor& item_accessor ) {
1065  return exclude( item_accessor );
1066  }
1067 
1068 protected:
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 ) ;
1071 
1072  struct accessor_not_used { void release(){}};
1073  friend const_accessor* accessor_location( accessor_not_used const& ){ return NULL;}
1074  friend const_accessor* accessor_location( const_accessor & a ) { return &a;}
1075 
1076  friend bool is_write_access_needed( accessor const& ) { return true;}
1077  friend bool is_write_access_needed( const_accessor const& ) { return false;}
1078  friend bool is_write_access_needed( accessor_not_used const& ) { return false;}
1079 
1080 #if __TBB_CPP11_RVALUE_REF_PRESENT
1081  template<typename Accessor>
1082  bool generic_move_insert( Accessor && result, value_type && value ) {
1083  result.release();
1084  return lookup(/*insert*/true, value.first, &value.second, accessor_location(result), is_write_access_needed(result), &allocate_node_move_construct );
1085  }
1086 
1087 #if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
1088  template<typename Accessor, typename... Args>
1089  bool generic_emplace( Accessor && result, Args &&... args ) {
1090  result.release();
1091  node * node_ptr = allocate_node_emplace_construct(my_allocator, std::forward<Args>(args)...);
1092  return lookup(/*insert*/true, node_ptr->item.first, NULL, accessor_location(result), is_write_access_needed(result), &do_not_allocate_node, node_ptr );
1093  }
1094 #endif //__TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
1095 #endif //__TBB_CPP11_RVALUE_REF_PRESENT
1096 
1098  bool exclude( const_accessor &item_accessor );
1099 
1101  template<typename I>
1102  std::pair<I, I> internal_equal_range( const Key& key, I end ) const;
1103 
1105  void internal_copy( const concurrent_hash_map& source );
1106 
1107  template<typename I>
1108  void internal_copy( I first, I last, size_type reserve_size );
1109 
1111 
1113  const_pointer internal_fast_find( const Key& key ) const {
1114  hashcode_t h = my_hash_compare.hash( key );
1115  hashcode_t m = (hashcode_t) itt_load_word_with_acquire( my_mask );
1116  node *n;
1117  restart:
1118  __TBB_ASSERT((m&(m+1))==0, "data structure is invalid");
1119  bucket *b = get_bucket( h & m );
1120  // TODO: actually, notification is unnecessary here, just hiding double-check
1122  {
1124  if( lock.try_acquire( b->mutex, /*write=*/true ) ) {
1125  if( b->node_list == internal::rehash_req)
1126  const_cast<concurrent_hash_map*>(this)->rehash_bucket( b, h & m ); //recursive rehashing
1127  }
1128  else lock.acquire( b->mutex, /*write=*/false );
1130  }
1131  n = search_bucket( key, b );
1132  if( n )
1133  return &n->item;
1134  else if( check_mask_race( h, m ) )
1135  goto restart;
1136  return 0;
1137  }
1138 };
1139 
1140 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
1141 namespace internal {
1142 using namespace tbb::internal;
1143 
1144 template<template<typename...> typename Map, typename Key, typename T, typename... Args>
1145 using hash_map_t = Map<
1146  Key, T,
1147  std::conditional_t< (sizeof...(Args)>0) && !is_allocator_v< pack_element_t<0, Args...> >,
1148  pack_element_t<0, Args...>, tbb_hash_compare<Key> >,
1149  std::conditional_t< (sizeof...(Args)>0) && is_allocator_v< pack_element_t<sizeof...(Args)-1, Args...> >,
1150  pack_element_t<sizeof...(Args)-1, Args...>, tbb_allocator<std::pair<const Key, T> > >
1151 >;
1152 }
1153 
1154 // Deduction guide for the constructor from two iterators and hash_compare/ allocator
1155 template<typename I, typename... Args>
1156 concurrent_hash_map(I, I, Args...)
1157 -> internal::hash_map_t<concurrent_hash_map, internal::iterator_key_t<I>,internal::iterator_mapped_t<I>, Args...>;
1158 
1159 // Deduction guide for the constructor from an initializer_list and hash_compare/ allocator
1160 // Deduction guide for an initializer_list, hash_compare and allocator is implicit
1161 template<typename Key, typename T, typename CompareOrAllocator>
1162 concurrent_hash_map(std::initializer_list<std::pair<const Key, T>>, CompareOrAllocator)
1163 -> internal::hash_map_t<concurrent_hash_map, Key, T, CompareOrAllocator>;
1164 
1165 #endif /* __TBB_CPP17_DEDUCTION_GUIDES_PRESENT */
1166 
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 ) {
1169  __TBB_ASSERT( !result || !result->my_node, NULL );
1170  bool return_value;
1171  hashcode_t const h = my_hash_compare.hash( key );
1172  hashcode_t m = (hashcode_t) itt_load_word_with_acquire( my_mask );
1173  segment_index_t grow_segment = 0;
1174  node *n;
1175  restart:
1176  {//lock scope
1177  __TBB_ASSERT((m&(m+1))==0, "data structure is invalid");
1178  return_value = false;
1179  // get bucket
1180  bucket_accessor b( this, h & m );
1181 
1182  // find a node
1183  n = search_bucket( key, b() );
1184  if( op_insert ) {
1185  // [opt] insert a key
1186  if( !n ) {
1187  if( !tmp_n ) {
1188  tmp_n = allocate_node(my_allocator, key, t);
1189  }
1190  if( !b.is_writer() && !b.upgrade_to_writer() ) { // TODO: improved insertion
1191  // Rerun search_list, in case another thread inserted the item during the upgrade.
1192  n = search_bucket( key, b() );
1193  if( is_valid(n) ) { // unfortunately, it did
1194  b.downgrade_to_reader();
1195  goto exists;
1196  }
1197  }
1198  if( check_mask_race(h, m) )
1199  goto restart; // b.release() is done in ~b().
1200  // insert and set flag to grow the container
1201  grow_segment = insert_new_node( b(), n = tmp_n, m );
1202  tmp_n = 0;
1203  return_value = true;
1204  }
1205  } else { // find or count
1206  if( !n ) {
1207  if( check_mask_race( h, m ) )
1208  goto restart; // b.release() is done in ~b(). TODO: replace by continue
1209  return false;
1210  }
1211  return_value = true;
1212  }
1213  exists:
1214  if( !result ) goto check_growth;
1215  // TODO: the following seems as generic/regular operation
1216  // acquire the item
1217  if( !result->try_acquire( n->mutex, write ) ) {
1218  for( tbb::internal::atomic_backoff backoff(true);; ) {
1219  if( result->try_acquire( n->mutex, write ) ) break;
1220  if( !backoff.bounded_pause() ) {
1221  // the wait takes really long, restart the operation
1222  b.release();
1223  __TBB_ASSERT( !op_insert || !return_value, "Can't acquire new item in locked bucket?" );
1224  __TBB_Yield();
1225  m = (hashcode_t) itt_load_word_with_acquire( my_mask );
1226  goto restart;
1227  }
1228  }
1229  }
1230  }//lock scope
1231  result->my_node = n;
1232  result->my_hash = h;
1233 check_growth:
1234  // [opt] grow the container
1235  if( grow_segment ) {
1236 #if __TBB_STATISTICS
1237  my_info_resizes++; // concurrent ones
1238 #endif
1239  enable_segment( grow_segment );
1240  }
1241  if( tmp_n ) // if op_insert only
1242  delete_node( tmp_n );
1243  return return_value;
1244 }
1245 
1246 template<typename Key, typename T, typename HashCompare, typename A>
1247 template<typename I>
1248 std::pair<I, I> concurrent_hash_map<Key,T,HashCompare,A>::internal_equal_range( const Key& key, I end_ ) const {
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");
1252  h &= m;
1253  bucket *b = get_bucket( h );
1254  while( b->node_list == internal::rehash_req ) {
1255  m = ( 1u<<__TBB_Log2( h ) ) - 1; // get parent mask from the topmost bit
1256  b = get_bucket( h &= m );
1257  }
1258  node *n = search_bucket( key, b );
1259  if( !n )
1260  return std::make_pair(end_, end_);
1261  iterator lower(*this, h, b, n), upper(lower);
1262  return std::make_pair(lower, ++upper);
1263 }
1264 
1265 template<typename Key, typename T, typename HashCompare, typename A>
1267  __TBB_ASSERT( item_accessor.my_node, NULL );
1268  node_base *const n = item_accessor.my_node;
1269  hashcode_t const h = item_accessor.my_hash;
1270  hashcode_t m = (hashcode_t) itt_load_word_with_acquire( my_mask );
1271  do {
1272  // get bucket
1273  bucket_accessor b( this, h & m, /*writer=*/true );
1274  node_base **p = &b()->node_list;
1275  while( *p && *p != n )
1276  p = &(*p)->next;
1277  if( !*p ) { // someone else was first
1278  if( check_mask_race( h, m ) )
1279  continue;
1280  item_accessor.release();
1281  return false;
1282  }
1283  __TBB_ASSERT( *p == n, NULL );
1284  *p = n->next; // remove from container
1285  my_size--;
1286  break;
1287  } while(true);
1288  if( !item_accessor.is_writer() ) // need to get exclusive lock
1289  item_accessor.upgrade_to_writer(); // return value means nothing here
1290  item_accessor.release();
1291  delete_node( n ); // Only one thread can delete it
1292  return true;
1293 }
1294 
1295 template<typename Key, typename T, typename HashCompare, typename A>
1297  node_base *n;
1298  hashcode_t const h = my_hash_compare.hash( key );
1299  hashcode_t m = (hashcode_t) itt_load_word_with_acquire( my_mask );
1300 restart:
1301  {//lock scope
1302  // get bucket
1303  bucket_accessor b( this, h & m );
1304  search:
1305  node_base **p = &b()->node_list;
1306  n = *p;
1307  while( is_valid(n) && !my_hash_compare.equal(key, static_cast<node*>(n)->item.first ) ) {
1308  p = &n->next;
1309  n = *p;
1310  }
1311  if( !n ) { // not found, but mask could be changed
1312  if( check_mask_race( h, m ) )
1313  goto restart;
1314  return false;
1315  }
1316  else if( !b.is_writer() && !b.upgrade_to_writer() ) {
1317  if( check_mask_race( h, m ) ) // contended upgrade, check mask
1318  goto restart;
1319  goto search;
1320  }
1321  *p = n->next;
1322  my_size--;
1323  }
1324  {
1325  typename node::scoped_t item_locker( n->mutex, /*write=*/true );
1326  }
1327  // note: there should be no threads pretending to acquire this mutex again, do not try to upgrade const_accessor!
1328  delete_node( n ); // Only one thread can delete it due to write lock on the bucket
1329  return true;
1330 }
1331 
1332 template<typename Key, typename T, typename HashCompare, typename A>
1334  //TODO: respect C++11 allocator_traits<A>::propogate_on_constainer_swap
1335  using std::swap;
1336  swap(this->my_allocator, table.my_allocator);
1337  swap(this->my_hash_compare, table.my_hash_compare);
1338  internal_swap(table);
1339 }
1340 
1341 template<typename Key, typename T, typename HashCompare, typename A>
1343  reserve( sz ); // TODO: add reduction of number of buckets as well
1344  hashcode_t mask = my_mask;
1345  hashcode_t b = (mask+1)>>1; // size or first index of the last segment
1346  __TBB_ASSERT((b&(b-1))==0, NULL); // zero or power of 2
1347  bucket *bp = get_bucket( b ); // only the last segment should be scanned for rehashing
1348  for(; b <= mask; b++, bp++ ) {
1349  node_base *n = bp->node_list;
1350  __TBB_ASSERT( is_valid(n) || n == internal::empty_rehashed || n == internal::rehash_req, "Broken internal structure" );
1351  __TBB_ASSERT( *reinterpret_cast<intptr_t*>(&bp->mutex) == 0, "concurrent or unexpectedly terminated operation during rehash() execution" );
1352  if( n == internal::rehash_req ) { // rehash bucket, conditional because rehashing of a previous bucket may affect this one
1353  hashcode_t h = b; bucket *b_old = bp;
1354  do {
1355  __TBB_ASSERT( h > 1, "The lowermost buckets can't be rehashed" );
1356  hashcode_t m = ( 1u<<__TBB_Log2( h ) ) - 1; // get parent mask from the topmost bit
1357  b_old = get_bucket( h &= m );
1358  } while( b_old->node_list == internal::rehash_req );
1359  // now h - is index of the root rehashed bucket b_old
1360  mark_rehashed_levels( h ); // mark all non-rehashed children recursively across all segments
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 ) { // should be rehashed
1364  *p = q->next; // exclude from b_old
1365  bucket *b_new = get_bucket( c & mask );
1366  __TBB_ASSERT( b_new->node_list != internal::rehash_req, "hash() function changed for key in table or internal error" );
1367  add_to_bucket( b_new, q );
1368  } else p = &q->next; // iterate to next item
1369  }
1370  }
1371  }
1372 #if TBB_USE_PERFORMANCE_WARNINGS
1373  int current_size = int(my_size), buckets = int(mask)+1, empty_buckets = 0, overpopulated_buckets = 0; // usage statistics
1374  static bool reported = false;
1375 #endif
1376 #if TBB_USE_ASSERT || TBB_USE_PERFORMANCE_WARNINGS
1377  for( b = 0; b <= mask; b++ ) {// only last segment should be scanned for rehashing
1378  if( b & (b-2) ) ++bp; // not the beginning of a segment
1379  else bp = get_bucket( b );
1380  node_base *n = bp->node_list;
1381  __TBB_ASSERT( *reinterpret_cast<intptr_t*>(&bp->mutex) == 0, "concurrent or unexpectedly terminated operation during rehash() execution" );
1382  __TBB_ASSERT( is_valid(n) || n == internal::empty_rehashed, "Broken internal structure" );
1383 #if TBB_USE_PERFORMANCE_WARNINGS
1384  if( n == internal::empty_rehashed ) empty_buckets++;
1385  else if( n->next ) overpopulated_buckets++;
1386 #endif
1387 #if TBB_USE_ASSERT
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" );
1391  }
1392 #endif
1393  }
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; // TODO: load_factor?
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(),
1403 #else
1404  "concurrent_hash_map",
1405 #endif
1406  current_size, empty_buckets, overpopulated_buckets );
1407  reported = true;
1408  }
1409 #endif
1410 }
1411 
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; // usage statistics
1419  static bool reported = false;
1420 #endif
1421  bucket *bp = 0;
1422  // check consistency
1423  for( segment_index_t b = 0; b <= m; b++ ) {
1424  if( b & (b-2) ) ++bp; // not the beginning of a segment
1425  else bp = get_bucket( b );
1426  node_base *n = bp->node_list;
1427  __TBB_ASSERT( is_valid(n) || n == internal::empty_rehashed || n == internal::rehash_req, "Broken internal structure" );
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
1430  if( n == internal::empty_rehashed ) empty_buckets++;
1431  else if( n == internal::rehash_req ) buckets--;
1432  else if( n->next ) overpopulated_buckets++;
1433 #endif
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 );
1437  h &= m;
1438  __TBB_ASSERT( h == b || get_bucket(h)->node_list == internal::rehash_req, "hash() function changed for key in table or internal error" );
1439  }
1440 #endif
1441  }
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; // concurrent ones
1449  my_info_restarts = 0; // race collisions
1450  my_info_rehashes = 0; // invocations of rehash_bucket
1451 #endif
1452  if( buckets > current_size) empty_buckets -= buckets - current_size;
1453  else overpopulated_buckets -= current_size - buckets; // TODO: load_factor?
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(),
1459 #else
1460  "concurrent_hash_map",
1461 #endif
1462  current_size, empty_buckets, overpopulated_buckets );
1463  reported = true;
1464  }
1465 #endif
1466 #endif // TBB_USE_ASSERT || TBB_USE_PERFORMANCE_WARNINGS || __TBB_STATISTICS
1467  my_size = 0;
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" );
1471  do {
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 ) {
1477  buckets_ptr[i].node_list = n->next;
1478  delete_node( n );
1479  }
1480  if( s >= first_block) // the first segment or the next
1481  alloc.deallocate( buckets_ptr, sz );
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;
1485  } while(s-- > 0);
1486  my_mask = embedded_buckets - 1;
1487 }
1488 
1489 template<typename Key, typename T, typename HashCompare, typename A>
1491  hashcode_t mask = source.my_mask;
1492  if( my_mask == mask ) { // optimized version
1493  reserve( source.my_size ); // TODO: load_factor?
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++; // not the beginning of a segment
1498  else { dst = get_bucket( k ); src = source.get_bucket( k ); }
1499  __TBB_ASSERT( dst->node_list != internal::rehash_req, "Invalid bucket in destination table");
1500  node *n = static_cast<node*>( src->node_list );
1501  if( n == internal::rehash_req ) { // source is not rehashed, items are in previous buckets
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) );
1506  ++my_size; // TODO: replace by non-atomic op
1507  }
1508  }
1509  if( rehash_required ) rehash();
1510  } else internal_copy( source.begin(), source.end(), source.my_size );
1511 }
1512 
1513 template<typename Key, typename T, typename HashCompare, typename A>
1514 template<typename I>
1515 void concurrent_hash_map<Key,T,HashCompare,A>::internal_copy(I first, I last, size_type reserve_size) {
1516  reserve( reserve_size ); // TODO: load_factor?
1517  hashcode_t m = my_mask;
1518  for(; first != last; ++first) {
1519  hashcode_t h = my_hash_compare.hash( (*first).first );
1520  bucket *b = get_bucket( h & m );
1521  __TBB_ASSERT( b->node_list != internal::rehash_req, "Invalid bucket in destination table");
1522  node *n = new( my_allocator ) node(*first);
1523  add_to_bucket( b, n );
1524  ++my_size; // TODO: replace by non-atomic op
1525  }
1526 }
1527 
1528 } // namespace interface5
1529 
1531 
1532 
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) {
1539  j = b.equal_range(i->first).first;
1540  if( j == j_end || !(i->second == j->second) ) return false;
1541  }
1542  return true;
1543 }
1544 
1545 template<typename Key, typename T, typename HashCompare, typename A1, typename A2>
1547 { return !(a == b); }
1548 
1549 template<typename Key, typename T, typename HashCompare, typename A>
1551 { a.swap( b ); }
1552 
1553 #if _MSC_VER && !defined(__INTEL_COMPILER)
1554  #pragma warning( pop )
1555 #endif // warning 4127 is back
1556 
1557 } // namespace tbb
1558 
1559 #endif /* __TBB_concurrent_hash_map_H */
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.
Class that implements exponential backoff.
Definition: tbb_machine.h:352
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)
node * search_bucket(const key_type &key, bucket *b) const
bool empty() const
True if size()==0.
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)
Definition: tbb_machine.h:716
pointer operator->() const
Return pointer to associated value in hash table.
internal::hash_map_iterator< concurrent_hash_map, value_type > iterator
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...
#define __TBB_ASSERT(predicate, comment)
No-op version of __TBB_ASSERT.
Definition: tbb_stddef.h:169
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.
enable_segment_failsafe(segments_table_t &table, segment_index_t k)
tick_count::interval_t operator-(const tick_count &t1, const tick_count &t0)
Definition: tick_count.h:130
~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.
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.
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.
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
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 */.
#define __TBB_Yield()
Definition: ibm_aix51.h:48
static size_type segment_size(segment_index_t k)
bool empty() const
True if range is empty.
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.
Unordered map from Key to T.
void internal_swap(hash_map_base &table)
Swap hash_map_bases.
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.
Acquire.
Definition: atomic.h:47
void const char const char int ITT_FORMAT __itt_group_sync p
const_range_type range(size_type grainsize=1) 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.
Definition: spin_rw_mutex.h:90
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
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)
Definition: tbb_machine.h:867
bool find(accessor &result, const Key &key)
Find item and acquire a write lock on the item.
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
bool is_writer()
check whether bucket is locked for write
atomic< size_type > my_size
Size of container in stored items.
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...
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) ...
hash_map_node_base node_base
Node base type.
The graph class.
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.
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())
static hash_map_node_base *const rehash_req
Incompleteness flag value.
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 move(tbb_thread &t1, tbb_thread &t2)
Definition: tbb_thread.h:309
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...
static node * allocate_node_move_construct(node_allocator_type &allocator, const Key &key, const T *t)
hash_map_iterator operator++(int)
Post increment.
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
Definition: tbb_config.h:129
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...
STL namespace.
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.
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)
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.
Definition: tbb_stddef.h:399
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.
Definition: tbb_stddef.h:335
Identifiers declared inside namespace internal should never be used directly by client code...
Definition: atomic.h:55
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.
Release.
Definition: atomic.h:49
void __TBB_store_with_release(volatile T &location, V value)
Definition: tbb_machine.h:720
node * my_node
Pointer to node that has current item.
atomic< T > & as_atomic(T &t)
Definition: atomic.h:547
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 &)
Fast, unfair, spinning reader-writer lock with backoff and writer-preference.
Definition: spin_rw_mutex.h:42
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.
Definition: tbb_allocator.h:62

Copyright © 2005-2019 Intel Corporation. All Rights Reserved.

Intel, Pentium, Intel Xeon, Itanium, Intel XScale and VTune are registered trademarks or trademarks of Intel Corporation or its subsidiaries in the United States and other countries.

* Other names and brands may be claimed as the property of others.