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-2018 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"
37 #if __TBB_INITIALIZER_LISTS_PRESENT
38 #include <initializer_list>
39 #endif
40 #if TBB_USE_PERFORMANCE_WARNINGS || __TBB_STATISTICS
41 #include <typeinfo>
42 #endif
43 #if __TBB_STATISTICS
44 #include <stdio.h>
45 #endif
46 
47 namespace tbb {
48 
49 namespace interface5 {
50 
51  template<typename Key, typename T, typename HashCompare = tbb_hash_compare<Key>, typename A = tbb_allocator<std::pair<Key, T> > >
53 
55  namespace internal {
56  using namespace tbb::internal;
57 
58 
60  typedef size_t hashcode_t;
70  };
72  static hash_map_node_base *const rehash_req = reinterpret_cast<hash_map_node_base*>(size_t(3));
74  static hash_map_node_base *const empty_rehashed = reinterpret_cast<hash_map_node_base*>(size_t(0));
76  class hash_map_base {
77  public:
79  typedef size_t size_type;
81  typedef size_t hashcode_t;
83  typedef size_t segment_index_t;
94  };
96  static size_type const embedded_block = 1;
98  static size_type const embedded_buckets = 1<<embedded_block;
100  static size_type const first_block = 8; //including embedded_block. perfect with bucket size 16, so the allocations are power of 4096
102  static size_type const pointers_per_table = sizeof(segment_index_t) * 8; // one segment per bit
106  typedef segment_ptr_t segments_table_t[pointers_per_table];
110  segments_table_t my_table;
112  atomic<size_type> my_size; // It must be in separate cache line from my_mask due to performance effects
114  bucket my_embedded_segment[embedded_buckets];
115 #if __TBB_STATISTICS
116  atomic<unsigned> my_info_resizes; // concurrent ones
117  mutable atomic<unsigned> my_info_restarts; // race collisions
118  atomic<unsigned> my_info_rehashes; // invocations of rehash_bucket
119 #endif
120  hash_map_base() {
122  std::memset( this, 0, pointers_per_table*sizeof(segment_ptr_t) // 32*4=128 or 64*8=512
123  + sizeof(my_size) + sizeof(my_mask) // 4+4 or 8+8
124  + embedded_buckets*sizeof(bucket) ); // n*8 or n*16
125  for( size_type i = 0; i < embedded_block; i++ ) // fill the table
126  my_table[i] = my_embedded_segment + segment_base(i);
127  my_mask = embedded_buckets - 1;
128  __TBB_ASSERT( embedded_block <= first_block, "The first block number must include embedded blocks");
129 #if __TBB_STATISTICS
130  my_info_resizes = 0; // concurrent ones
131  my_info_restarts = 0; // race collisions
132  my_info_rehashes = 0; // invocations of rehash_bucket
133 #endif
134  }
135 
138  return segment_index_t( __TBB_Log2( index|1 ) );
139  }
140 
143  return (segment_index_t(1)<<k & ~segment_index_t(1));
144  }
145 
148  return size_type(1)<<k; // fake value for k==0
149  }
150 
152  static bool is_valid( void *ptr ) {
153  return reinterpret_cast<uintptr_t>(ptr) > uintptr_t(63);
154  }
155 
157  static void init_buckets( segment_ptr_t ptr, size_type sz, bool is_initial ) {
158  if( is_initial ) std::memset( static_cast<void*>(ptr), 0, sz*sizeof(bucket) );
159  else for(size_type i = 0; i < sz; i++, ptr++) {
160  *reinterpret_cast<intptr_t*>(&ptr->mutex) = 0;
161  ptr->node_list = rehash_req;
162  }
163  }
164 
166  static void add_to_bucket( bucket *b, node_base *n ) {
167  __TBB_ASSERT(b->node_list != rehash_req, NULL);
168  n->next = b->node_list;
169  b->node_list = n; // its under lock and flag is set
170  }
171 
175  enable_segment_failsafe(segments_table_t &table, segment_index_t k) : my_segment_ptr(&table[k]) {}
177  if( my_segment_ptr ) *my_segment_ptr = 0; // indicate no allocation in progress
178  }
179  };
180 
182  void enable_segment( segment_index_t k, bool is_initial = false ) {
183  __TBB_ASSERT( k, "Zero segment must be embedded" );
184  enable_segment_failsafe watchdog( my_table, k );
186  size_type sz;
187  __TBB_ASSERT( !is_valid(my_table[k]), "Wrong concurrent assignment");
188  if( k >= first_block ) {
189  sz = segment_size( k );
190  segment_ptr_t ptr = alloc.allocate( sz );
191  init_buckets( ptr, sz, is_initial );
192  itt_hide_store_word( my_table[k], ptr );
193  sz <<= 1;// double it to get entire capacity of the container
194  } else { // the first block
195  __TBB_ASSERT( k == embedded_block, "Wrong segment index" );
196  sz = segment_size( first_block );
197  segment_ptr_t ptr = alloc.allocate( sz - embedded_buckets );
198  init_buckets( ptr, sz - embedded_buckets, is_initial );
199  ptr -= segment_base(embedded_block);
200  for(segment_index_t i = embedded_block; i < first_block; i++) // calc the offsets
201  itt_hide_store_word( my_table[i], ptr + segment_base(i) );
202  }
203  itt_store_word_with_release( my_mask, sz-1 );
204  watchdog.my_segment_ptr = 0;
205  }
206 
208  bucket *get_bucket( hashcode_t h ) const throw() { // TODO: add throw() everywhere?
209  segment_index_t s = segment_index_of( h );
210  h -= segment_base(s);
211  segment_ptr_t seg = my_table[s];
212  __TBB_ASSERT( is_valid(seg), "hashcode must be cut by valid mask for allocated segments" );
213  return &seg[h];
214  }
215 
216  // internal serial rehashing helper
217  void mark_rehashed_levels( hashcode_t h ) throw () {
218  segment_index_t s = segment_index_of( h );
219  while( segment_ptr_t seg = my_table[++s] )
220  if( seg[h].node_list == rehash_req ) {
221  seg[h].node_list = empty_rehashed;
222  mark_rehashed_levels( h + ((hashcode_t)1<<s) ); // optimized segment_base(s)
223  }
224  }
225 
227  // Splitting into two functions should help inlining
228  inline bool check_mask_race( const hashcode_t h, hashcode_t &m ) const {
229  hashcode_t m_now, m_old = m;
230  m_now = (hashcode_t) itt_load_word_with_acquire( my_mask );
231  if( m_old != m_now )
232  return check_rehashing_collision( h, m_old, m = m_now );
233  return false;
234  }
235 
238  __TBB_ASSERT(m_old != m, NULL); // TODO?: m arg could be optimized out by passing h = h&m
239  if( (h & m_old) != (h & m) ) { // mask changed for this hashcode, rare event
240  // condition above proves that 'h' has some other bits set beside 'm_old'
241  // find next applicable mask after m_old //TODO: look at bsl instruction
242  for( ++m_old; !(h & m_old); m_old <<= 1 ) // at maximum few rounds depending on the first block size
243  ;
244  m_old = (m_old<<1) - 1; // get full mask from a bit
245  __TBB_ASSERT((m_old&(m_old+1))==0 && m_old <= m, NULL);
246  // check whether it is rehashing/ed
247  if( itt_load_word_with_acquire(get_bucket(h & m_old)->node_list) != rehash_req )
248  {
249 #if __TBB_STATISTICS
250  my_info_restarts++; // race collisions
251 #endif
252  return true;
253  }
254  }
255  return false;
256  }
257 
260  size_type sz = ++my_size; // prefix form is to enforce allocation after the first item inserted
261  add_to_bucket( b, n );
262  // check load factor
263  if( sz >= mask ) { // TODO: add custom load_factor
264  segment_index_t new_seg = __TBB_Log2( mask+1 ); //optimized segment_index_of
265  __TBB_ASSERT( is_valid(my_table[new_seg-1]), "new allocations must not publish new mask until segment has allocated");
266  static const segment_ptr_t is_allocating = (segment_ptr_t)2;
267  if( !itt_hide_load_word(my_table[new_seg])
268  && as_atomic(my_table[new_seg]).compare_and_swap(is_allocating, NULL) == NULL )
269  return new_seg; // The value must be processed
270  }
271  return 0;
272  }
273 
275  void reserve(size_type buckets) {
276  if( !buckets-- ) return;
277  bool is_initial = !my_size;
278  for( size_type m = my_mask; buckets > m; m = my_mask )
279  enable_segment( segment_index_of( m+1 ), is_initial );
280  }
283  using std::swap;
284  swap(this->my_mask, table.my_mask);
285  swap(this->my_size, table.my_size);
286  for(size_type i = 0; i < embedded_buckets; i++)
287  swap(this->my_embedded_segment[i].node_list, table.my_embedded_segment[i].node_list);
288  for(size_type i = embedded_block; i < pointers_per_table; i++)
289  swap(this->my_table[i], table.my_table[i]);
290  }
291  };
292 
293  template<typename Iterator>
295 
297 
299  template<typename Container, typename Value>
301  : public std::iterator<std::forward_iterator_tag,Value>
302  {
303  typedef Container map_type;
304  typedef typename Container::node node;
307 
308  template<typename C, typename T, typename U>
309  friend bool operator==( const hash_map_iterator<C,T>& i, const hash_map_iterator<C,U>& j );
310 
311  template<typename C, typename T, typename U>
312  friend bool operator!=( const hash_map_iterator<C,T>& i, const hash_map_iterator<C,U>& j );
313 
314  template<typename C, typename T, typename U>
315  friend ptrdiff_t operator-( const hash_map_iterator<C,T>& i, const hash_map_iterator<C,U>& j );
316 
317  template<typename C, typename U>
318  friend class hash_map_iterator;
319 
320  template<typename I>
321  friend class hash_map_range;
322 
323  void advance_to_next_bucket() { // TODO?: refactor to iterator_base class
324  size_t k = my_index+1;
325  __TBB_ASSERT( my_bucket, "advancing an invalid iterator?");
326  while( k <= my_map->my_mask ) {
327  // Following test uses 2's-complement wizardry
328  if( k&(k-2) ) // not the beginning of a segment
329  ++my_bucket;
330  else my_bucket = my_map->get_bucket( k );
331  my_node = static_cast<node*>( my_bucket->node_list );
332  if( hash_map_base::is_valid(my_node) ) {
333  my_index = k; return;
334  }
335  ++k;
336  }
337  my_bucket = 0; my_node = 0; my_index = k; // the end
338  }
339 #if !defined(_MSC_VER) || defined(__INTEL_COMPILER)
340  template<typename Key, typename T, typename HashCompare, typename A>
342 #else
343  public: // workaround
344 #endif
345  const Container *my_map;
347 
349  size_t my_index;
350 
353 
356 
357  hash_map_iterator( const Container &map, size_t index, const bucket *b, node_base *n );
358 
359  public:
361  hash_map_iterator(): my_map(), my_index(), my_bucket(), my_node() {}
363  my_map(other.my_map),
364  my_index(other.my_index),
365  my_bucket(other.my_bucket),
366  my_node(other.my_node)
367  {}
368  Value& operator*() const {
369  __TBB_ASSERT( hash_map_base::is_valid(my_node), "iterator uninitialized or at end of container?" );
370  return my_node->item;
371  }
372  Value* operator->() const {return &operator*();}
373  hash_map_iterator& operator++();
374 
377  hash_map_iterator old(*this);
378  operator++();
379  return old;
380  }
381  };
382 
383  template<typename Container, typename Value>
384  hash_map_iterator<Container,Value>::hash_map_iterator( const Container &map, size_t index, const bucket *b, node_base *n ) :
385  my_map(&map),
386  my_index(index),
387  my_bucket(b),
388  my_node( static_cast<node*>(n) )
389  {
390  if( b && !hash_map_base::is_valid(n) )
392  }
393 
394  template<typename Container, typename Value>
396  my_node = static_cast<node*>( my_node->next );
397  if( !my_node ) advance_to_next_bucket();
398  return *this;
399  }
400 
401  template<typename Container, typename T, typename U>
403  return i.my_node == j.my_node && i.my_map == j.my_map;
404  }
405 
406  template<typename Container, typename T, typename U>
408  return i.my_node != j.my_node || i.my_map != j.my_map;
409  }
410 
412 
413  template<typename Iterator>
414  class hash_map_range {
415  typedef typename Iterator::map_type map_type;
416  Iterator my_begin;
417  Iterator my_end;
418  mutable Iterator my_midpoint;
419  size_t my_grainsize;
421  void set_midpoint() const;
422  template<typename U> friend class hash_map_range;
423  public:
425  typedef std::size_t size_type;
426  typedef typename Iterator::value_type value_type;
427  typedef typename Iterator::reference reference;
428  typedef typename Iterator::difference_type difference_type;
429  typedef Iterator iterator;
430 
432  bool empty() const {return my_begin==my_end;}
433 
435  bool is_divisible() const {
436  return my_midpoint!=my_end;
437  }
440  my_end(r.my_end),
442  {
443  r.my_end = my_begin = r.my_midpoint;
444  __TBB_ASSERT( !empty(), "Splitting despite the range is not divisible" );
445  __TBB_ASSERT( !r.empty(), "Splitting despite the range is not divisible" );
446  set_midpoint();
447  r.set_midpoint();
448  }
450  template<typename U>
452  my_begin(r.my_begin),
453  my_end(r.my_end),
456  {}
458  hash_map_range( const map_type &map, size_type grainsize_ = 1 ) :
459  my_begin( Iterator( map, 0, map.my_embedded_segment, map.my_embedded_segment->node_list ) ),
460  my_end( Iterator( map, map.my_mask + 1, 0, 0 ) ),
461  my_grainsize( grainsize_ )
462  {
463  __TBB_ASSERT( grainsize_>0, "grainsize must be positive" );
464  set_midpoint();
465  }
466  const Iterator& begin() const {return my_begin;}
467  const Iterator& end() const {return my_end;}
469  size_type grainsize() const {return my_grainsize;}
470  };
471 
472  template<typename Iterator>
474  // Split by groups of nodes
475  size_t m = my_end.my_index-my_begin.my_index;
476  if( m > my_grainsize ) {
477  m = my_begin.my_index + m/2u;
478  hash_map_base::bucket *b = my_begin.my_map->get_bucket(m);
479  my_midpoint = Iterator(*my_begin.my_map,m,b,b->node_list);
480  } else {
481  my_midpoint = my_end;
482  }
483  __TBB_ASSERT( my_begin.my_index <= my_midpoint.my_index,
484  "my_begin is after my_midpoint" );
485  __TBB_ASSERT( my_midpoint.my_index <= my_end.my_index,
486  "my_midpoint is after my_end" );
487  __TBB_ASSERT( my_begin != my_midpoint || my_begin == my_end,
488  "[my_begin, my_midpoint) range should not be empty" );
489  }
490 
491  } // internal
493 
494 #if _MSC_VER && !defined(__INTEL_COMPILER)
495  // Suppress "conditional expression is constant" warning.
496  #pragma warning( push )
497  #pragma warning( disable: 4127 )
498 #endif
499 
501 
530 template<typename Key, typename T, typename HashCompare, typename Allocator>
531 class concurrent_hash_map : protected internal::hash_map_base {
532  template<typename Container, typename Value>
534 
535  template<typename I>
537 
538 public:
539  typedef Key key_type;
540  typedef T mapped_type;
541  typedef std::pair<const Key,T> value_type;
542  typedef hash_map_base::size_type size_type;
543  typedef ptrdiff_t difference_type;
544  typedef value_type *pointer;
545  typedef const value_type *const_pointer;
547  typedef const value_type &const_reference;
548  typedef internal::hash_map_iterator<concurrent_hash_map,value_type> iterator;
549  typedef internal::hash_map_iterator<concurrent_hash_map,const value_type> const_iterator;
550  typedef internal::hash_map_range<iterator> range_type;
551  typedef internal::hash_map_range<const_iterator> const_range_type;
552  typedef Allocator allocator_type;
553 
554 protected:
555  friend class const_accessor;
556  struct node;
557  typedef typename Allocator::template rebind<node>::other node_allocator_type;
559  HashCompare my_hash_compare;
560 
561  struct node : public node_base {
563  node( const Key &key ) : item(key, T()) {}
564  node( const Key &key, const T &t ) : item(key, t) {}
565 #if __TBB_CPP11_RVALUE_REF_PRESENT
566  node( const Key &key, T &&t ) : item(key, std::move(t)) {}
567  node( value_type&& i ) : item(std::move(i)){}
568 #if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
569  template<typename... Args>
570  node( Args&&... args ) : item(std::forward<Args>(args)...) {}
571 #if __TBB_COPY_FROM_NON_CONST_REF_BROKEN
572  node( value_type& i ) : item(const_cast<const value_type&>(i)) {}
573 #endif //__TBB_COPY_FROM_NON_CONST_REF_BROKEN
574 #endif //__TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
575 #endif //__TBB_CPP11_RVALUE_REF_PRESENT
576  node( const value_type& i ) : item(i) {}
577 
578  // exception-safe allocation, see C++ Standard 2003, clause 5.3.4p17
579  void *operator new( size_t /*size*/, node_allocator_type &a ) {
580  void *ptr = a.allocate(1);
581  if(!ptr)
583  return ptr;
584  }
585  // match placement-new form above to be called if exception thrown in constructor
586  void operator delete( void *ptr, node_allocator_type &a ) { a.deallocate(static_cast<node*>(ptr),1); }
587  };
588 
589  void delete_node( node_base *n ) {
590  my_allocator.destroy( static_cast<node*>(n) );
591  my_allocator.deallocate( static_cast<node*>(n), 1);
592  }
593 
594  static node* allocate_node_copy_construct(node_allocator_type& allocator, const Key &key, const T * t){
595  return new( allocator ) node(key, *t);
596  }
597 
598 #if __TBB_CPP11_RVALUE_REF_PRESENT
599  static node* allocate_node_move_construct(node_allocator_type& allocator, const Key &key, const T * t){
600  return new( allocator ) node(key, std::move(*const_cast<T*>(t)));
601  }
602 #if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
603  template<typename... Args>
604  static node* allocate_node_emplace_construct(node_allocator_type& allocator, Args&&... args){
605  return new( allocator ) node(std::forward<Args>(args)...);
606  }
607 #endif //__TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
608 #endif //__TBB_CPP11_RVALUE_REF_PRESENT
609 
610  static node* allocate_node_default_construct(node_allocator_type& allocator, const Key &key, const T * ){
611  return new( allocator ) node(key);
612  }
613 
614  static node* do_not_allocate_node(node_allocator_type& , const Key &, const T * ){
615  __TBB_ASSERT(false,"this dummy function should not be called");
616  return NULL;
617  }
618 
619  node *search_bucket( const key_type &key, bucket *b ) const {
620  node *n = static_cast<node*>( b->node_list );
621  while( is_valid(n) && !my_hash_compare.equal(key, n->item.first) )
622  n = static_cast<node*>( n->next );
623  __TBB_ASSERT(n != internal::rehash_req, "Search can be executed only for rehashed bucket");
624  return n;
625  }
626 
629  bucket *my_b;
630  public:
631  bucket_accessor( concurrent_hash_map *base, const hashcode_t h, bool writer = false ) { acquire( base, h, writer ); }
633  inline void acquire( concurrent_hash_map *base, const hashcode_t h, bool writer = false ) {
634  my_b = base->get_bucket( h );
635  // TODO: actually, notification is unnecessary here, just hiding double-check
637  && try_acquire( my_b->mutex, /*write=*/true ) )
638  {
639  if( my_b->node_list == internal::rehash_req ) base->rehash_bucket( my_b, h ); //recursive rehashing
640  }
641  else bucket::scoped_t::acquire( my_b->mutex, writer );
642  __TBB_ASSERT( my_b->node_list != internal::rehash_req, NULL);
643  }
645  bool is_writer() { return bucket::scoped_t::is_writer; }
647  bucket *operator() () { return my_b; }
648  };
649 
650  // TODO refactor to hash_base
651  void rehash_bucket( bucket *b_new, const hashcode_t h ) {
652  __TBB_ASSERT( *(intptr_t*)(&b_new->mutex), "b_new must be locked (for write)");
653  __TBB_ASSERT( h > 1, "The lowermost buckets can't be rehashed" );
655  hashcode_t mask = ( 1u<<__TBB_Log2( h ) ) - 1; // get parent mask from the topmost bit
656 #if __TBB_STATISTICS
657  my_info_rehashes++; // invocations of rehash_bucket
658 #endif
659 
660  bucket_accessor b_old( this, h & mask );
661 
662  mask = (mask<<1) | 1; // get full mask for new bucket
663  __TBB_ASSERT( (mask&(mask+1))==0 && (h & mask) == h, NULL );
664  restart:
665  for( node_base **p = &b_old()->node_list, *n = __TBB_load_with_acquire(*p); is_valid(n); n = *p ) {
666  hashcode_t c = my_hash_compare.hash( static_cast<node*>(n)->item.first );
667 #if TBB_USE_ASSERT
668  hashcode_t bmask = h & (mask>>1);
669  bmask = bmask==0? 1 : ( 1u<<(__TBB_Log2( bmask )+1 ) ) - 1; // minimal mask of parent bucket
670  __TBB_ASSERT( (c & bmask) == (h & bmask), "hash() function changed for key in table" );
671 #endif
672  if( (c & mask) == h ) {
673  if( !b_old.is_writer() )
674  if( !b_old.upgrade_to_writer() ) {
675  goto restart; // node ptr can be invalid due to concurrent erase
676  }
677  *p = n->next; // exclude from b_old
678  add_to_bucket( b_new, n );
679  } else p = &n->next; // iterate to next item
680  }
681  }
682 
686  void dismiss() {my_ch_map = 0;}
688  if (my_ch_map){
689  my_ch_map->clear();
690  }
691  }
692  };
693 public:
694 
695  class accessor;
697  class const_accessor : private node::scoped_t /*which derived from no_copy*/ {
698  friend class concurrent_hash_map<Key,T,HashCompare,Allocator>;
699  friend class accessor;
700  public:
703 
705  bool empty() const { return !my_node; }
706 
708  void release() {
709  if( my_node ) {
711  my_node = 0;
712  }
713  }
714 
717  __TBB_ASSERT( my_node, "attempt to dereference empty accessor" );
718  return my_node->item;
719  }
720 
723  return &operator*();
724  }
725 
727  const_accessor() : my_node(NULL) {}
728 
731  my_node = NULL; // scoped lock's release() is called in its destructor
732  }
733  protected:
734  bool is_writer() { return node::scoped_t::is_writer; }
737  };
738 
740  class accessor: public const_accessor {
741  public:
744 
747  __TBB_ASSERT( this->my_node, "attempt to dereference empty accessor" );
748  return this->my_node->item;
749  }
750 
752  pointer operator->() const {
753  return &operator*();
754  }
755  };
756 
759  : internal::hash_map_base(), my_allocator(a)
760  {}
761 
762  explicit concurrent_hash_map( const HashCompare& compare, const allocator_type& a = allocator_type() )
763  : internal::hash_map_base(), my_allocator(a), my_hash_compare(compare)
764  {}
765 
768  : internal::hash_map_base(), my_allocator(a)
769  {
770  reserve( n );
771  }
772 
773  concurrent_hash_map( size_type n, const HashCompare& compare, const allocator_type& a = allocator_type() )
774  : internal::hash_map_base(), my_allocator(a), my_hash_compare(compare)
775  {
776  reserve( n );
777  }
778 
781  : internal::hash_map_base(), my_allocator(a)
782  {
783  call_clear_on_leave scope_guard(this);
784  internal_copy(table);
785  scope_guard.dismiss();
786  }
787 
788 #if __TBB_CPP11_RVALUE_REF_PRESENT
791  : internal::hash_map_base(), my_allocator(std::move(table.get_allocator()))
792  {
793  swap(table);
794  }
795 
798  : internal::hash_map_base(), my_allocator(a)
799  {
800  if (a == table.get_allocator()){
801  this->swap(table);
802  }else{
803  call_clear_on_leave scope_guard(this);
804  internal_copy(std::make_move_iterator(table.begin()), std::make_move_iterator(table.end()), table.size());
805  scope_guard.dismiss();
806  }
807  }
808 #endif //__TBB_CPP11_RVALUE_REF_PRESENT
809 
811  template<typename I>
813  : internal::hash_map_base(), my_allocator(a)
814  {
815  call_clear_on_leave scope_guard(this);
816  internal_copy(first, last, std::distance(first, last));
817  scope_guard.dismiss();
818  }
819 
820  template<typename I>
821  concurrent_hash_map( I first, I last, const HashCompare& compare, const allocator_type& a = allocator_type() )
822  : internal::hash_map_base(), my_allocator(a), my_hash_compare(compare)
823  {
824  call_clear_on_leave scope_guard(this);
825  internal_copy(first, last, std::distance(first, last));
826  scope_guard.dismiss();
827  }
828 
829 #if __TBB_INITIALIZER_LISTS_PRESENT
830  concurrent_hash_map( std::initializer_list<value_type> il, const allocator_type &a = allocator_type() )
832  : internal::hash_map_base(), my_allocator(a)
833  {
834  call_clear_on_leave scope_guard(this);
835  internal_copy(il.begin(), il.end(), il.size());
836  scope_guard.dismiss();
837  }
838 
839  concurrent_hash_map( std::initializer_list<value_type> il, const HashCompare& compare, const allocator_type& a = allocator_type() )
840  : internal::hash_map_base(), my_allocator(a), my_hash_compare(compare)
841  {
842  call_clear_on_leave scope_guard(this);
843  internal_copy(il.begin(), il.end(), il.size());
844  scope_guard.dismiss();
845  }
846 
847 #endif //__TBB_INITIALIZER_LISTS_PRESENT
848 
851  if( this!=&table ) {
852  clear();
853  internal_copy(table);
854  }
855  return *this;
856  }
857 
858 #if __TBB_CPP11_RVALUE_REF_PRESENT
861  if(this != &table){
863  if(pocma_t::value || this->my_allocator == table.my_allocator) {
864  concurrent_hash_map trash (std::move(*this));
865  //TODO: swapping allocators here may be a problem, replace with single direction moving iff pocma is set
866  this->swap(table);
867  } else {
868  //do per element move
869  concurrent_hash_map moved_copy(std::move(table), this->my_allocator);
870  this->swap(moved_copy);
871  }
872  }
873  return *this;
874  }
875 #endif //__TBB_CPP11_RVALUE_REF_PRESENT
876 
877 #if __TBB_INITIALIZER_LISTS_PRESENT
878  concurrent_hash_map& operator=( std::initializer_list<value_type> il ) {
880  clear();
881  internal_copy(il.begin(), il.end(), il.size());
882  return *this;
883  }
884 #endif //__TBB_INITIALIZER_LISTS_PRESENT
885 
886 
888 
890  void rehash(size_type n = 0);
891 
893  void clear();
894 
897 
898  //------------------------------------------------------------------------
899  // Parallel algorithm support
900  //------------------------------------------------------------------------
901  range_type range( size_type grainsize=1 ) {
902  return range_type( *this, grainsize );
903  }
904  const_range_type range( size_type grainsize=1 ) const {
905  return const_range_type( *this, grainsize );
906  }
907 
908  //------------------------------------------------------------------------
909  // STL support - not thread-safe methods
910  //------------------------------------------------------------------------
912  iterator end() { return iterator( *this, 0, 0, 0 ); }
914  const_iterator end() const { return const_iterator( *this, 0, 0, 0 ); }
915  std::pair<iterator, iterator> equal_range( const Key& key ) { return internal_equal_range( key, end() ); }
916  std::pair<const_iterator, const_iterator> equal_range( const Key& key ) const { return internal_equal_range( key, end() ); }
917 
919  size_type size() const { return my_size; }
920 
922  bool empty() const { return my_size == 0; }
923 
925  size_type max_size() const {return (~size_type(0))/sizeof(node);}
926 
928  size_type bucket_count() const { return my_mask+1; }
929 
931  allocator_type get_allocator() const { return this->my_allocator; }
932 
934  void swap( concurrent_hash_map &table );
935 
936  //------------------------------------------------------------------------
937  // concurrent map operations
938  //------------------------------------------------------------------------
939 
941  size_type count( const Key &key ) const {
942  return const_cast<concurrent_hash_map*>(this)->lookup(/*insert*/false, key, NULL, NULL, /*write=*/false, &do_not_allocate_node );
943  }
944 
946 
947  bool find( const_accessor &result, const Key &key ) const {
948  result.release();
949  return const_cast<concurrent_hash_map*>(this)->lookup(/*insert*/false, key, NULL, &result, /*write=*/false, &do_not_allocate_node );
950  }
951 
953 
954  bool find( accessor &result, const Key &key ) {
955  result.release();
956  return lookup(/*insert*/false, key, NULL, &result, /*write=*/true, &do_not_allocate_node );
957  }
958 
960 
961  bool insert( const_accessor &result, const Key &key ) {
962  result.release();
963  return lookup(/*insert*/true, key, NULL, &result, /*write=*/false, &allocate_node_default_construct );
964  }
965 
967 
968  bool insert( accessor &result, const Key &key ) {
969  result.release();
970  return lookup(/*insert*/true, key, NULL, &result, /*write=*/true, &allocate_node_default_construct );
971  }
972 
974 
975  bool insert( const_accessor &result, const value_type &value ) {
976  result.release();
977  return lookup(/*insert*/true, value.first, &value.second, &result, /*write=*/false, &allocate_node_copy_construct );
978  }
979 
981 
982  bool insert( accessor &result, const value_type &value ) {
983  result.release();
984  return lookup(/*insert*/true, value.first, &value.second, &result, /*write=*/true, &allocate_node_copy_construct );
985  }
986 
988 
989  bool insert( const value_type &value ) {
990  return lookup(/*insert*/true, value.first, &value.second, NULL, /*write=*/false, &allocate_node_copy_construct );
991  }
992 
993 #if __TBB_CPP11_RVALUE_REF_PRESENT
994 
996  bool insert( const_accessor &result, value_type && value ) {
997  return generic_move_insert(result, std::move(value));
998  }
999 
1001 
1002  bool insert( accessor &result, value_type && value ) {
1003  return generic_move_insert(result, std::move(value));
1004  }
1005 
1007 
1008  bool insert( value_type && value ) {
1010  }
1011 
1012 #if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
1013 
1015  template<typename... Args>
1016  bool emplace( const_accessor &result, Args&&... args ) {
1017  return generic_emplace(result, std::forward<Args>(args)...);
1018  }
1019 
1021 
1022  template<typename... Args>
1023  bool emplace( accessor &result, Args&&... args ) {
1024  return generic_emplace(result, std::forward<Args>(args)...);
1025  }
1026 
1028 
1029  template<typename... Args>
1030  bool emplace( Args&&... args ) {
1031  return generic_emplace(accessor_not_used(), std::forward<Args>(args)...);
1032  }
1033 #endif //__TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
1034 #endif //__TBB_CPP11_RVALUE_REF_PRESENT
1035 
1037  template<typename I>
1038  void insert( I first, I last ) {
1039  for ( ; first != last; ++first )
1040  insert( *first );
1041  }
1042 
1043 #if __TBB_INITIALIZER_LISTS_PRESENT
1044  void insert( std::initializer_list<value_type> il ) {
1046  insert( il.begin(), il.end() );
1047  }
1048 #endif //__TBB_INITIALIZER_LISTS_PRESENT
1049 
1051 
1052  bool erase( const Key& key );
1053 
1055 
1056  bool erase( const_accessor& item_accessor ) {
1057  return exclude( item_accessor );
1058  }
1059 
1061 
1062  bool erase( accessor& item_accessor ) {
1063  return exclude( item_accessor );
1064  }
1065 
1066 protected:
1068  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 ) ;
1069 
1070  struct accessor_not_used { void release(){}};
1071  friend const_accessor* accessor_location( accessor_not_used const& ){ return NULL;}
1072  friend const_accessor* accessor_location( const_accessor & a ) { return &a;}
1073 
1074  friend bool is_write_access_needed( accessor const& ) { return true;}
1075  friend bool is_write_access_needed( const_accessor const& ) { return false;}
1076  friend bool is_write_access_needed( accessor_not_used const& ) { return false;}
1077 
1078 #if __TBB_CPP11_RVALUE_REF_PRESENT
1079  template<typename Accessor>
1080  bool generic_move_insert( Accessor && result, value_type && value ) {
1081  result.release();
1082  return lookup(/*insert*/true, value.first, &value.second, accessor_location(result), is_write_access_needed(result), &allocate_node_move_construct );
1083  }
1084 
1085 #if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
1086  template<typename Accessor, typename... Args>
1087  bool generic_emplace( Accessor && result, Args &&... args ) {
1088  result.release();
1089  node * node_ptr = allocate_node_emplace_construct(my_allocator, std::forward<Args>(args)...);
1090  return lookup(/*insert*/true, node_ptr->item.first, NULL, accessor_location(result), is_write_access_needed(result), &do_not_allocate_node, node_ptr );
1091  }
1092 #endif //__TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
1093 #endif //__TBB_CPP11_RVALUE_REF_PRESENT
1094 
1096  bool exclude( const_accessor &item_accessor );
1097 
1099  template<typename I>
1100  std::pair<I, I> internal_equal_range( const Key& key, I end ) const;
1101 
1103  void internal_copy( const concurrent_hash_map& source );
1104 
1105  template<typename I>
1106  void internal_copy( I first, I last, size_type reserve_size );
1107 
1109 
1111  const_pointer internal_fast_find( const Key& key ) const {
1112  hashcode_t h = my_hash_compare.hash( key );
1114  node *n;
1115  restart:
1116  __TBB_ASSERT((m&(m+1))==0, "data structure is invalid");
1117  bucket *b = get_bucket( h & m );
1118  // TODO: actually, notification is unnecessary here, just hiding double-check
1120  {
1122  if( lock.try_acquire( b->mutex, /*write=*/true ) ) {
1123  if( b->node_list == internal::rehash_req)
1124  const_cast<concurrent_hash_map*>(this)->rehash_bucket( b, h & m ); //recursive rehashing
1125  }
1126  else lock.acquire( b->mutex, /*write=*/false );
1128  }
1129  n = search_bucket( key, b );
1130  if( n )
1131  return &n->item;
1132  else if( check_mask_race( h, m ) )
1133  goto restart;
1134  return 0;
1135  }
1136 };
1137 
1138 template<typename Key, typename T, typename HashCompare, typename A>
1139 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 ) {
1140  __TBB_ASSERT( !result || !result->my_node, NULL );
1141  bool return_value;
1142  hashcode_t const h = my_hash_compare.hash( key );
1144  segment_index_t grow_segment = 0;
1145  node *n;
1146  restart:
1147  {//lock scope
1148  __TBB_ASSERT((m&(m+1))==0, "data structure is invalid");
1149  return_value = false;
1150  // get bucket
1151  bucket_accessor b( this, h & m );
1152 
1153  // find a node
1154  n = search_bucket( key, b() );
1155  if( op_insert ) {
1156  // [opt] insert a key
1157  if( !n ) {
1158  if( !tmp_n ) {
1159  tmp_n = allocate_node(my_allocator, key, t);
1160  }
1161  if( !b.is_writer() && !b.upgrade_to_writer() ) { // TODO: improved insertion
1162  // Rerun search_list, in case another thread inserted the item during the upgrade.
1163  n = search_bucket( key, b() );
1164  if( is_valid(n) ) { // unfortunately, it did
1165  b.downgrade_to_reader();
1166  goto exists;
1167  }
1168  }
1169  if( check_mask_race(h, m) )
1170  goto restart; // b.release() is done in ~b().
1171  // insert and set flag to grow the container
1172  grow_segment = insert_new_node( b(), n = tmp_n, m );
1173  tmp_n = 0;
1174  return_value = true;
1175  }
1176  } else { // find or count
1177  if( !n ) {
1178  if( check_mask_race( h, m ) )
1179  goto restart; // b.release() is done in ~b(). TODO: replace by continue
1180  return false;
1181  }
1182  return_value = true;
1183  }
1184  exists:
1185  if( !result ) goto check_growth;
1186  // TODO: the following seems as generic/regular operation
1187  // acquire the item
1188  if( !result->try_acquire( n->mutex, write ) ) {
1189  for( tbb::internal::atomic_backoff backoff(true);; ) {
1190  if( result->try_acquire( n->mutex, write ) ) break;
1191  if( !backoff.bounded_pause() ) {
1192  // the wait takes really long, restart the operation
1193  b.release();
1194  __TBB_ASSERT( !op_insert || !return_value, "Can't acquire new item in locked bucket?" );
1195  __TBB_Yield();
1196  m = (hashcode_t) itt_load_word_with_acquire( my_mask );
1197  goto restart;
1198  }
1199  }
1200  }
1201  }//lock scope
1202  result->my_node = n;
1203  result->my_hash = h;
1204 check_growth:
1205  // [opt] grow the container
1206  if( grow_segment ) {
1207 #if __TBB_STATISTICS
1208  my_info_resizes++; // concurrent ones
1209 #endif
1210  enable_segment( grow_segment );
1211  }
1212  if( tmp_n ) // if op_insert only
1213  delete_node( tmp_n );
1214  return return_value;
1215 }
1216 
1217 template<typename Key, typename T, typename HashCompare, typename A>
1218 template<typename I>
1219 std::pair<I, I> concurrent_hash_map<Key,T,HashCompare,A>::internal_equal_range( const Key& key, I end_ ) const {
1220  hashcode_t h = my_hash_compare.hash( key );
1221  hashcode_t m = my_mask;
1222  __TBB_ASSERT((m&(m+1))==0, "data structure is invalid");
1223  h &= m;
1224  bucket *b = get_bucket( h );
1225  while( b->node_list == internal::rehash_req ) {
1226  m = ( 1u<<__TBB_Log2( h ) ) - 1; // get parent mask from the topmost bit
1227  b = get_bucket( h &= m );
1228  }
1229  node *n = search_bucket( key, b );
1230  if( !n )
1231  return std::make_pair(end_, end_);
1232  iterator lower(*this, h, b, n), upper(lower);
1233  return std::make_pair(lower, ++upper);
1234 }
1235 
1236 template<typename Key, typename T, typename HashCompare, typename A>
1238  __TBB_ASSERT( item_accessor.my_node, NULL );
1239  node_base *const n = item_accessor.my_node;
1240  hashcode_t const h = item_accessor.my_hash;
1242  do {
1243  // get bucket
1244  bucket_accessor b( this, h & m, /*writer=*/true );
1245  node_base **p = &b()->node_list;
1246  while( *p && *p != n )
1247  p = &(*p)->next;
1248  if( !*p ) { // someone else was first
1249  if( check_mask_race( h, m ) )
1250  continue;
1251  item_accessor.release();
1252  return false;
1253  }
1254  __TBB_ASSERT( *p == n, NULL );
1255  *p = n->next; // remove from container
1256  my_size--;
1257  break;
1258  } while(true);
1259  if( !item_accessor.is_writer() ) // need to get exclusive lock
1260  item_accessor.upgrade_to_writer(); // return value means nothing here
1261  item_accessor.release();
1262  delete_node( n ); // Only one thread can delete it
1263  return true;
1264 }
1265 
1266 template<typename Key, typename T, typename HashCompare, typename A>
1268  node_base *n;
1269  hashcode_t const h = my_hash_compare.hash( key );
1271 restart:
1272  {//lock scope
1273  // get bucket
1274  bucket_accessor b( this, h & m );
1275  search:
1276  node_base **p = &b()->node_list;
1277  n = *p;
1278  while( is_valid(n) && !my_hash_compare.equal(key, static_cast<node*>(n)->item.first ) ) {
1279  p = &n->next;
1280  n = *p;
1281  }
1282  if( !n ) { // not found, but mask could be changed
1283  if( check_mask_race( h, m ) )
1284  goto restart;
1285  return false;
1286  }
1287  else if( !b.is_writer() && !b.upgrade_to_writer() ) {
1288  if( check_mask_race( h, m ) ) // contended upgrade, check mask
1289  goto restart;
1290  goto search;
1291  }
1292  *p = n->next;
1293  my_size--;
1294  }
1295  {
1296  typename node::scoped_t item_locker( n->mutex, /*write=*/true );
1297  }
1298  // note: there should be no threads pretending to acquire this mutex again, do not try to upgrade const_accessor!
1299  delete_node( n ); // Only one thread can delete it due to write lock on the bucket
1300  return true;
1301 }
1302 
1303 template<typename Key, typename T, typename HashCompare, typename A>
1305  //TODO: respect C++11 allocator_traits<A>::propogate_on_constainer_swap
1306  using std::swap;
1307  swap(this->my_allocator, table.my_allocator);
1308  swap(this->my_hash_compare, table.my_hash_compare);
1309  internal_swap(table);
1310 }
1311 
1312 template<typename Key, typename T, typename HashCompare, typename A>
1314  reserve( sz ); // TODO: add reduction of number of buckets as well
1315  hashcode_t mask = my_mask;
1316  hashcode_t b = (mask+1)>>1; // size or first index of the last segment
1317  __TBB_ASSERT((b&(b-1))==0, NULL); // zero or power of 2
1318  bucket *bp = get_bucket( b ); // only the last segment should be scanned for rehashing
1319  for(; b <= mask; b++, bp++ ) {
1320  node_base *n = bp->node_list;
1321  __TBB_ASSERT( is_valid(n) || n == internal::empty_rehashed || n == internal::rehash_req, "Broken internal structure" );
1322  __TBB_ASSERT( *reinterpret_cast<intptr_t*>(&bp->mutex) == 0, "concurrent or unexpectedly terminated operation during rehash() execution" );
1323  if( n == internal::rehash_req ) { // rehash bucket, conditional because rehashing of a previous bucket may affect this one
1324  hashcode_t h = b; bucket *b_old = bp;
1325  do {
1326  __TBB_ASSERT( h > 1, "The lowermost buckets can't be rehashed" );
1327  hashcode_t m = ( 1u<<__TBB_Log2( h ) ) - 1; // get parent mask from the topmost bit
1328  b_old = get_bucket( h &= m );
1329  } while( b_old->node_list == internal::rehash_req );
1330  // now h - is index of the root rehashed bucket b_old
1331  mark_rehashed_levels( h ); // mark all non-rehashed children recursively across all segments
1332  for( node_base **p = &b_old->node_list, *q = *p; is_valid(q); q = *p ) {
1333  hashcode_t c = my_hash_compare.hash( static_cast<node*>(q)->item.first );
1334  if( (c & mask) != h ) { // should be rehashed
1335  *p = q->next; // exclude from b_old
1336  bucket *b_new = get_bucket( c & mask );
1337  __TBB_ASSERT( b_new->node_list != internal::rehash_req, "hash() function changed for key in table or internal error" );
1338  add_to_bucket( b_new, q );
1339  } else p = &q->next; // iterate to next item
1340  }
1341  }
1342  }
1343 #if TBB_USE_PERFORMANCE_WARNINGS
1344  int current_size = int(my_size), buckets = int(mask)+1, empty_buckets = 0, overpopulated_buckets = 0; // usage statistics
1345  static bool reported = false;
1346 #endif
1347 #if TBB_USE_ASSERT || TBB_USE_PERFORMANCE_WARNINGS
1348  for( b = 0; b <= mask; b++ ) {// only last segment should be scanned for rehashing
1349  if( b & (b-2) ) ++bp; // not the beginning of a segment
1350  else bp = get_bucket( b );
1351  node_base *n = bp->node_list;
1352  __TBB_ASSERT( *reinterpret_cast<intptr_t*>(&bp->mutex) == 0, "concurrent or unexpectedly terminated operation during rehash() execution" );
1353  __TBB_ASSERT( is_valid(n) || n == internal::empty_rehashed, "Broken internal structure" );
1354 #if TBB_USE_PERFORMANCE_WARNINGS
1355  if( n == internal::empty_rehashed ) empty_buckets++;
1356  else if( n->next ) overpopulated_buckets++;
1357 #endif
1358 #if TBB_USE_ASSERT
1359  for( ; is_valid(n); n = n->next ) {
1360  hashcode_t h = my_hash_compare.hash( static_cast<node*>(n)->item.first ) & mask;
1361  __TBB_ASSERT( h == b, "hash() function changed for key in table or internal error" );
1362  }
1363 #endif
1364  }
1365 #endif // TBB_USE_ASSERT || TBB_USE_PERFORMANCE_WARNINGS
1366 #if TBB_USE_PERFORMANCE_WARNINGS
1367  if( buckets > current_size) empty_buckets -= buckets - current_size;
1368  else overpopulated_buckets -= current_size - buckets; // TODO: load_factor?
1369  if( !reported && buckets >= 512 && ( 2*empty_buckets > current_size || 2*overpopulated_buckets > current_size ) ) {
1371  "Performance is not optimal because the hash function produces bad randomness in lower bits in %s.\nSize: %d Empties: %d Overlaps: %d",
1373  typeid(*this).name(),
1374 #else
1375  "concurrent_hash_map",
1376 #endif
1377  current_size, empty_buckets, overpopulated_buckets );
1378  reported = true;
1379  }
1380 #endif
1381 }
1382 
1383 template<typename Key, typename T, typename HashCompare, typename A>
1385  hashcode_t m = my_mask;
1386  __TBB_ASSERT((m&(m+1))==0, "data structure is invalid");
1387 #if TBB_USE_ASSERT || TBB_USE_PERFORMANCE_WARNINGS || __TBB_STATISTICS
1388 #if TBB_USE_PERFORMANCE_WARNINGS || __TBB_STATISTICS
1389  int current_size = int(my_size), buckets = int(m)+1, empty_buckets = 0, overpopulated_buckets = 0; // usage statistics
1390  static bool reported = false;
1391 #endif
1392  bucket *bp = 0;
1393  // check consistency
1394  for( segment_index_t b = 0; b <= m; b++ ) {
1395  if( b & (b-2) ) ++bp; // not the beginning of a segment
1396  else bp = get_bucket( b );
1397  node_base *n = bp->node_list;
1398  __TBB_ASSERT( is_valid(n) || n == internal::empty_rehashed || n == internal::rehash_req, "Broken internal structure" );
1399  __TBB_ASSERT( *reinterpret_cast<intptr_t*>(&bp->mutex) == 0, "concurrent or unexpectedly terminated operation during clear() execution" );
1400 #if TBB_USE_PERFORMANCE_WARNINGS || __TBB_STATISTICS
1401  if( n == internal::empty_rehashed ) empty_buckets++;
1402  else if( n == internal::rehash_req ) buckets--;
1403  else if( n->next ) overpopulated_buckets++;
1404 #endif
1405 #if __TBB_EXTRA_DEBUG
1406  for(; is_valid(n); n = n->next ) {
1407  hashcode_t h = my_hash_compare.hash( static_cast<node*>(n)->item.first );
1408  h &= m;
1409  __TBB_ASSERT( h == b || get_bucket(h)->node_list == internal::rehash_req, "hash() function changed for key in table or internal error" );
1410  }
1411 #endif
1412  }
1413 #if TBB_USE_PERFORMANCE_WARNINGS || __TBB_STATISTICS
1414 #if __TBB_STATISTICS
1415  printf( "items=%d buckets: capacity=%d rehashed=%d empty=%d overpopulated=%d"
1416  " concurrent: resizes=%u rehashes=%u restarts=%u\n",
1417  current_size, int(m+1), buckets, empty_buckets, overpopulated_buckets,
1418  unsigned(my_info_resizes), unsigned(my_info_rehashes), unsigned(my_info_restarts) );
1419  my_info_resizes = 0; // concurrent ones
1420  my_info_restarts = 0; // race collisions
1421  my_info_rehashes = 0; // invocations of rehash_bucket
1422 #endif
1423  if( buckets > current_size) empty_buckets -= buckets - current_size;
1424  else overpopulated_buckets -= current_size - buckets; // TODO: load_factor?
1425  if( !reported && buckets >= 512 && ( 2*empty_buckets > current_size || 2*overpopulated_buckets > current_size ) ) {
1427  "Performance is not optimal because the hash function produces bad randomness in lower bits in %s.\nSize: %d Empties: %d Overlaps: %d",
1429  typeid(*this).name(),
1430 #else
1431  "concurrent_hash_map",
1432 #endif
1433  current_size, empty_buckets, overpopulated_buckets );
1434  reported = true;
1435  }
1436 #endif
1437 #endif // TBB_USE_ASSERT || TBB_USE_PERFORMANCE_WARNINGS || __TBB_STATISTICS
1438  my_size = 0;
1439  segment_index_t s = segment_index_of( m );
1440  __TBB_ASSERT( s+1 == pointers_per_table || !my_table[s+1], "wrong mask or concurrent grow" );
1442  do {
1443  __TBB_ASSERT( is_valid( my_table[s] ), "wrong mask or concurrent grow" );
1444  segment_ptr_t buckets_ptr = my_table[s];
1445  size_type sz = segment_size( s ? s : 1 );
1446  for( segment_index_t i = 0; i < sz; i++ )
1447  for( node_base *n = buckets_ptr[i].node_list; is_valid(n); n = buckets_ptr[i].node_list ) {
1448  buckets_ptr[i].node_list = n->next;
1449  delete_node( n );
1450  }
1451  if( s >= first_block) // the first segment or the next
1452  alloc.deallocate( buckets_ptr, sz );
1453  else if( s == embedded_block && embedded_block != first_block )
1454  alloc.deallocate( buckets_ptr, segment_size(first_block)-embedded_buckets );
1455  if( s >= embedded_block ) my_table[s] = 0;
1456  } while(s-- > 0);
1457  my_mask = embedded_buckets - 1;
1458 }
1459 
1460 template<typename Key, typename T, typename HashCompare, typename A>
1462  hashcode_t mask = source.my_mask;
1463  if( my_mask == mask ) { // optimized version
1464  reserve( source.my_size ); // TODO: load_factor?
1465  bucket *dst = 0, *src = 0;
1466  bool rehash_required = false;
1467  for( hashcode_t k = 0; k <= mask; k++ ) {
1468  if( k & (k-2) ) ++dst,src++; // not the beginning of a segment
1469  else { dst = get_bucket( k ); src = source.get_bucket( k ); }
1470  __TBB_ASSERT( dst->node_list != internal::rehash_req, "Invalid bucket in destination table");
1471  node *n = static_cast<node*>( src->node_list );
1472  if( n == internal::rehash_req ) { // source is not rehashed, items are in previous buckets
1473  rehash_required = true;
1475  } else for(; n; n = static_cast<node*>( n->next ) ) {
1476  add_to_bucket( dst, new( my_allocator ) node(n->item.first, n->item.second) );
1477  ++my_size; // TODO: replace by non-atomic op
1478  }
1479  }
1480  if( rehash_required ) rehash();
1481  } else internal_copy( source.begin(), source.end(), source.my_size );
1482 }
1483 
1484 template<typename Key, typename T, typename HashCompare, typename A>
1485 template<typename I>
1487  reserve( reserve_size ); // TODO: load_factor?
1488  hashcode_t m = my_mask;
1489  for(; first != last; ++first) {
1490  hashcode_t h = my_hash_compare.hash( (*first).first );
1491  bucket *b = get_bucket( h & m );
1492  __TBB_ASSERT( b->node_list != internal::rehash_req, "Invalid bucket in destination table");
1493  node *n = new( my_allocator ) node(*first);
1494  add_to_bucket( b, n );
1495  ++my_size; // TODO: replace by non-atomic op
1496  }
1497 }
1498 
1499 } // namespace interface5
1500 
1502 
1503 
1504 template<typename Key, typename T, typename HashCompare, typename A1, typename A2>
1506  if(a.size() != b.size()) return false;
1509  for(; i != i_end; ++i) {
1510  j = b.equal_range(i->first).first;
1511  if( j == j_end || !(i->second == j->second) ) return false;
1512  }
1513  return true;
1514 }
1515 
1516 template<typename Key, typename T, typename HashCompare, typename A1, typename A2>
1518 { return !(a == b); }
1519 
1520 template<typename Key, typename T, typename HashCompare, typename A>
1522 { a.swap( b ); }
1523 
1524 #if _MSC_VER && !defined(__INTEL_COMPILER)
1525  #pragma warning( pop )
1526 #endif // warning 4127 is back
1527 
1528 } // namespace tbb
1529 
1530 #endif /* __TBB_concurrent_hash_map_H */
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
concurrent_hash_map(concurrent_hash_map &&table, const allocator_type &a)
Move constructor.
hash_map_range(hash_map_range< U > &r)
type conversion
segments_table_t my_table
Segment pointers table. Also prevents false sharing between my_mask and my_size.
intptr_t __TBB_Log2(uintptr_t x)
Definition: tbb_machine.h:864
std::pair< iterator, iterator > equal_range(const Key &key)
bool erase(accessor &item_accessor)
Erase item by accessor.
Allocator::template rebind< node >::other node_allocator_type
bool empty() const
True if size()==0.
friend bool is_write_access_needed(accessor const &)
T itt_hide_load_word(const T &src)
segment_index_t insert_new_node(bucket *b, node_base *n, hashcode_t mask)
Insert a node and check for load factor.
reference operator *() const
Return reference to associated value in hash table.
hash_map_range(hash_map_range &r, split)
Split range.
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.
bool empty() const
True if range is empty.
Class that implements exponential backoff.
Definition: tbb_machine.h:349
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.
void rehash(size_type n=0)
Rehashes and optionally resizes the whole table.
atomic< size_type > my_size
Size of container in stored items.
static node * allocate_node_copy_construct(node_allocator_type &allocator, const Key &key, const T *t)
hash_map_node_base node_base
Node base type.
bool operator==(const cache_aligned_allocator< T > &, const cache_aligned_allocator< U > &)
void __TBB_store_with_release(volatile T &location, V value)
Definition: tbb_machine.h:717
atomic< T > & as_atomic(T &t)
Definition: atomic.h:547
void const char const char int ITT_FORMAT __itt_group_sync s
bucket my_embedded_segment[embedded_buckets]
Zero segment.
static segment_index_t segment_index_of(size_type index)
Identifiers declared inside namespace internal should never be used directly by client code.
Definition: atomic.h:55
size_t my_index
Index in hash table for current item.
size_type max_size() const
Upper bound on size.
node * my_node
Pointer to node that has current item.
Dummy type that distinguishes splitting constructor from copy constructor.
Definition: tbb_stddef.h:399
std::size_t size_type
Type for size of a range.
bool check_mask_race(const hashcode_t h, hashcode_t &m) const
Check for mask race.
const_pointer operator->() const
Return pointer to associated value in hash table.
mutex_t::scoped_lock scoped_t
Scoped lock type for mutex.
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...
bucket_accessor(concurrent_hash_map *base, const hashcode_t h, bool writer=false)
Base class for types that should not be copied or assigned.
Definition: tbb_stddef.h:335
auto last(Container &c) -> decltype(begin(c))
static segment_index_t segment_base(segment_index_t k)
Meets "allocator" requirements of ISO C++ Standard, Section 20.1.5.
static hash_map_node_base *const rehash_req
Incompleteness flag value.
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 &)
hash_map_node_base * next
Next node in chain.
bucket accessor is to find, rehash, acquire a lock, and access a bucket
bool is_divisible() const
True if range can be partitioned into two subranges.
const concurrent_hash_map::value_type value_type
Type of value.
static node * allocate_node_emplace_construct(node_allocator_type &allocator, Args &&... args)
void swap(concurrent_hash_map &table)
swap two instances. Iterators are invalidated
bool emplace(Args &&... args)
Insert item by copying if there is no such key present already.
internal::hash_map_iterator< concurrent_hash_map, const value_type > const_iterator
void rehash_bucket(bucket *b_new, const hashcode_t h)
static node * do_not_allocate_node(node_allocator_type &, const Key &, const T *)
concurrent_hash_map(const HashCompare &compare, const allocator_type &a=allocator_type())
void deallocate(pointer p, size_type)
Free block of memory that starts on a cache line.
Allows write access to elements and combines data access, locking, and garbage collection.
#define __TBB_ASSERT(predicate, comment)
No-op version of __TBB_ASSERT.
Definition: tbb_stddef.h:169
Release.
Definition: atomic.h:49
auto first(Container &c) -> decltype(begin(c))
node * search_bucket(const key_type &key, bucket *b) const
void const char const char int ITT_FORMAT __itt_group_sync p
hash_map_iterator(const hash_map_iterator< Container, typename Container::value_type > &other)
Class for determining type of std::allocator<T>::value_type.
Definition: tbb_stddef.h:454
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
Fast, unfair, spinning reader-writer lock with backoff and writer-preference.
Definition: spin_rw_mutex.h:42
Unordered map from Key to T.
void internal_swap(hash_map_base &table)
Swap hash_map_bases.
void enable_segment(segment_index_t k, bool is_initial=false)
Enable segment.
pointer operator->() const
Return pointer to associated value in hash table.
internal::hash_map_range< iterator > range_type
internal::hash_map_iterator< concurrent_hash_map, value_type > iterator
bool insert(value_type &&value)
Insert item by copying if there is no such key present already.
static hash_map_node_base *const empty_rehashed
Rehashed empty bucket flag.
bool is_writer()
check whether bucket is locked for write
T __TBB_load_with_acquire(const volatile T &location)
Definition: tbb_machine.h:713
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)
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.
Range class used with concurrent_hash_map.
base class of concurrent_hash_map
concurrent_hash_map(I first, I last, const HashCompare &compare, const allocator_type &a=allocator_type())
std::pair< const_iterator, const_iterator > equal_range(const Key &key) const
bool find(const_accessor &result, const Key &key) const
Find item and acquire a read lock on the item.
concurrent_hash_map(std::initializer_list< value_type > il, const HashCompare &compare, const allocator_type &a=allocator_type())
internal::hash_map_range< const_iterator > const_range_type
concurrent_hash_map(size_type n, const HashCompare &compare, const allocator_type &a=allocator_type())
concurrent_hash_map::value_type value_type
Type of value.
range_type range(size_type grainsize=1)
Combines data access, locking, and garbage collection.
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.
hash_map_iterator operator++(int)
Post increment.
size_type grainsize() const
The grain size for this range.
void move(tbb_thread &t1, tbb_thread &t2)
Definition: tbb_thread.h:309
static node * allocate_node_move_construct(node_allocator_type &allocator, const Key &key, const T *t)
size_type size() const
Number of items in table.
size_type bucket_count() const
Returns the current number of buckets.
bool insert(accessor &result, const Key &key)
Insert item (if not already present) and acquire a write lock on the item.
concurrent_hash_map(I first, I last, const allocator_type &a=allocator_type())
Construction with copying iteration range and given allocator instance.
bool erase(const_accessor &item_accessor)
Erase item by const_accessor.
T itt_load_word_with_acquire(const tbb::atomic< T > &src)
mutex_t::scoped_lock scoped_t
Scoped lock type for mutex.
allocator_type get_allocator() const
return allocator object
void __TBB_EXPORTED_FUNC runtime_warning(const char *format,...)
Report a runtime warning.
hash_map_iterator()
Construct undefined iterator.
static void add_to_bucket(bucket *b, node_base *n)
Add node.
bool insert(const value_type &value)
Insert item by copying if there is no such key present already.
bool operator!=(const hash_map_iterator< Container, T > &i, const hash_map_iterator< Container, U > &j)
static void init_buckets(segment_ptr_t ptr, size_type sz, bool is_initial)
Initialize buckets.
atomic< hashcode_t > my_mask
Hash mask = sum of allocated segment sizes - 1.
bucket * get_bucket(hashcode_t h) const
Get bucket by (masked) hashcode.
bool generic_emplace(Accessor &&result, Args &&... args)
Acquire.
Definition: atomic.h:47
const_pointer internal_fast_find(const Key &key) const
Fast find when no concurrent erasure is used. For internal use inside TBB only!
Meets requirements of a forward iterator for STL */.
The graph class.
~const_accessor()
Destroy result after releasing the underlying reference.
friend bool is_write_access_needed(accessor_not_used const &)
#define __TBB_Yield()
Definition: ibm_aix51.h:48
#define __TBB_USE_OPTIONAL_RTTI
Definition: tbb_config.h:126
bool operator!=(const cache_aligned_allocator< T > &, const cache_aligned_allocator< U > &)
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
enable_segment_failsafe(segments_table_t &table, segment_index_t k)
bool generic_move_insert(Accessor &&result, value_type &&value)
static size_type segment_size(segment_index_t k)
~concurrent_hash_map()
Clear table and destroy it.
concurrent_hash_map(const concurrent_hash_map &table, const allocator_type &a=allocator_type())
Copy constructor.
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
void internal_copy(const concurrent_hash_map &source)
Copy "source" to *this, where *this must start out empty.
const Container * my_map
concurrent_hash_map over which we are iterating.
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
bool operator==(const hash_map_iterator< Container, T > &i, const hash_map_iterator< Container, U > &j)
void itt_hide_store_word(T &dst, T src)
size_t hashcode_t
Type of a hash code.
const bucket * my_bucket
Pointer to bucket.
void reserve(size_type buckets)
Prepare enough segments for number of buckets.
concurrent_hash_map(const allocator_type &a=allocator_type())
Construct empty table.
const_reference operator *() const
Return reference to associated value in hash table.
const_range_type range(size_type grainsize=1) const
bool erase(const Key &key)
Erase item.
size_type count(const Key &key) const
Return count of items (0 or 1)
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
hash_map_range(const map_type &map, size_type grainsize_=1)
Init range with container and grainsize specified.
spin_rw_mutex mutex_t
Mutex type for buckets.
bool find(accessor &result, const Key &key)
Find item and acquire a write lock on the item.
void insert(I first, I last)
Insert range [first, last)
bool exclude(const_accessor &item_accessor)
delete item by accessor
friend bool is_write_access_needed(const_accessor const &)
friend const_accessor * accessor_location(const_accessor &a)
bool check_rehashing_collision(const hashcode_t h, hashcode_t m_old, hashcode_t m) const
Process mask race, check for rehashing collision.
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
tick_count::interval_t operator-(const tick_count &t1, const tick_count &t0)
Definition: tick_count.h:130
concurrent_hash_map & operator=(const concurrent_hash_map &table)
Assignment.
void set_midpoint() const
Set my_midpoint to point approximately half way between my_begin and my_end.
pointer allocate(size_type n, const void *hint=0)
Allocate space for n objects, starting on a cache/sector line.
bool insert(const_accessor &result, const Key &key)
Insert item (if not already present) and acquire a read 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.
void throw_exception(exception_id eid)
Versionless convenience wrapper for throw_exception_v4()
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_default_construct(node_allocator_type &allocator, const Key &key, const T *)
void swap(concurrent_hash_map< Key, T, HashCompare, A > &a, concurrent_hash_map< Key, T, HashCompare, A > &b)
void itt_store_word_with_release(tbb::atomic< T > &dst, U src)
The scoped locking pattern.
Definition: spin_rw_mutex.h:90

Copyright © 2005-2018 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.