Shadowrun: Awakened 29 September 2011 - Build 871
concurrent_hash_map.h
Go to the documentation of this file.
00001 /*
00002     Copyright 2005-2010 Intel Corporation.  All Rights Reserved.
00003 
00004     This file is part of Threading Building Blocks.
00005 
00006     Threading Building Blocks is free software; you can redistribute it
00007     and/or modify it under the terms of the GNU General Public License
00008     version 2 as published by the Free Software Foundation.
00009 
00010     Threading Building Blocks is distributed in the hope that it will be
00011     useful, but WITHOUT ANY WARRANTY; without even the implied warranty
00012     of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013     GNU General Public License for more details.
00014 
00015     You should have received a copy of the GNU General Public License
00016     along with Threading Building Blocks; if not, write to the Free Software
00017     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00018 
00019     As a special exception, you may use this file as part of a free software
00020     library without restriction.  Specifically, if other files instantiate
00021     templates or use macros or inline functions from this file, or you compile
00022     this file and link it with other files to produce an executable, this
00023     file does not by itself cause the resulting executable to be covered by
00024     the GNU General Public License.  This exception does not however
00025     invalidate any other reasons why the executable file might be covered by
00026     the GNU General Public License.
00027 */
00028 
00029 #ifndef __TBB_concurrent_hash_map_H
00030 #define __TBB_concurrent_hash_map_H
00031 
00032 #include "tbb_stddef.h"
00033 
00034 #if !TBB_USE_EXCEPTIONS && _MSC_VER
00035     // Suppress "C++ exception handler used, but unwind semantics are not enabled" warning in STL headers
00036     #pragma warning (push)
00037     #pragma warning (disable: 4530)
00038 #endif
00039 
00040 #include <iterator>
00041 #include <utility>      // Need std::pair
00042 #include <cstring>      // Need std::memset
00043 
00044 #if !TBB_USE_EXCEPTIONS && _MSC_VER
00045     #pragma warning (pop)
00046 #endif
00047 
00048 #include "cache_aligned_allocator.h"
00049 #include "tbb_allocator.h"
00050 #include "spin_rw_mutex.h"
00051 #include "atomic.h"
00052 #include "aligned_space.h"
00053 #include "tbb_exception.h"
00054 #include "_concurrent_unordered_internal.h" // Need tbb_hasher
00055 #if TBB_USE_PERFORMANCE_WARNINGS
00056 #include <typeinfo>
00057 #endif
00058 
00059 namespace tbb {
00060 
00062 namespace internal {
00064     void* __TBB_EXPORTED_FUNC itt_load_pointer_with_acquire_v3( const void* src );
00066     void __TBB_EXPORTED_FUNC itt_store_pointer_with_release_v3( void* dst, void* src );
00068     void* __TBB_EXPORTED_FUNC itt_load_pointer_v3( const void* src );
00069 }
00071 
00073 template<typename Key>
00074 struct tbb_hash_compare {
00075     static size_t hash( const Key& a ) { return tbb_hasher(a); }
00076     static bool equal( const Key& a, const Key& b ) { return a == b; }
00077 };
00078 
00079 namespace interface4 {
00080 
00081     template<typename Key, typename T, typename HashCompare = tbb_hash_compare<Key>, typename A = tbb_allocator<std::pair<Key, T> > >
00082     class concurrent_hash_map;
00083 
00085     namespace internal {
00086 
00087 
00089     typedef size_t hashcode_t;
00091     struct hash_map_node_base : tbb::internal::no_copy {
00093         typedef spin_rw_mutex mutex_t;
00095         typedef mutex_t::scoped_lock scoped_t;
00097         hash_map_node_base *next;
00098         mutex_t mutex;
00099     };
00101     static hash_map_node_base *const rehash_req = reinterpret_cast<hash_map_node_base*>(size_t(3));
00103     static hash_map_node_base *const empty_rehashed = reinterpret_cast<hash_map_node_base*>(size_t(0));
00105     class hash_map_base {
00106     public:
00108         typedef size_t size_type;
00110         typedef size_t hashcode_t;
00112         typedef size_t segment_index_t;
00114         typedef hash_map_node_base node_base;
00116         struct bucket : tbb::internal::no_copy {
00118             typedef spin_rw_mutex mutex_t;
00120             typedef mutex_t::scoped_lock scoped_t;
00121             mutex_t mutex;
00122             node_base *node_list;
00123         };
00125         static size_type const embedded_block = 1;
00127         static size_type const embedded_buckets = 1<<embedded_block;
00129         static size_type const first_block = 8; //including embedded_block. perfect with bucket size 16, so the allocations are power of 4096
00131         static size_type const pointers_per_table = sizeof(segment_index_t) * 8; // one segment per bit
00133         typedef bucket *segment_ptr_t;
00135         typedef segment_ptr_t segments_table_t[pointers_per_table];
00137         atomic<hashcode_t> my_mask;
00139         segments_table_t my_table;
00141         atomic<size_type> my_size; // It must be in separate cache line from my_mask due to performance effects
00143         bucket my_embedded_segment[embedded_buckets];
00144 
00146         hash_map_base() {
00147             std::memset( this, 0, pointers_per_table*sizeof(segment_ptr_t) // 32*4=128   or 64*8=512
00148                 + sizeof(my_size) + sizeof(my_mask)  // 4+4 or 8+8
00149                 + embedded_buckets*sizeof(bucket) ); // n*8 or n*16
00150             for( size_type i = 0; i < embedded_block; i++ ) // fill the table
00151                 my_table[i] = my_embedded_segment + segment_base(i);
00152             my_mask = embedded_buckets - 1;
00153             __TBB_ASSERT( embedded_block <= first_block, "The first block number must include embedded blocks");
00154         }
00155 
00157         static segment_index_t segment_index_of( size_type index ) {
00158             return segment_index_t( __TBB_Log2( index|1 ) );
00159         }
00160 
00162         static segment_index_t segment_base( segment_index_t k ) {
00163             return (segment_index_t(1)<<k & ~segment_index_t(1));
00164         }
00165 
00167         static size_type segment_size( segment_index_t k ) {
00168             return size_type(1)<<k; // fake value for k==0
00169         }
00170         
00172         static bool is_valid( void *ptr ) {
00173             return reinterpret_cast<size_t>(ptr) > size_t(63);
00174         }
00175 
00177         static void init_buckets( segment_ptr_t ptr, size_type sz, bool is_initial ) {
00178             if( is_initial ) std::memset(ptr, 0, sz*sizeof(bucket) );
00179             else for(size_type i = 0; i < sz; i++, ptr++) {
00180                     *reinterpret_cast<intptr_t*>(&ptr->mutex) = 0;
00181                     ptr->node_list = rehash_req;
00182                 }
00183         }
00184         
00186         static void add_to_bucket( bucket *b, node_base *n ) {
00187             __TBB_ASSERT(b->node_list != rehash_req, NULL);
00188             n->next = b->node_list;
00189             b->node_list = n; // its under lock and flag is set
00190         }
00191 
00193         struct enable_segment_failsafe {
00194             segment_ptr_t *my_segment_ptr;
00195             enable_segment_failsafe(segments_table_t &table, segment_index_t k) : my_segment_ptr(&table[k]) {}
00196             ~enable_segment_failsafe() {
00197                 if( my_segment_ptr ) *my_segment_ptr = 0; // indicate no allocation in progress
00198             }
00199         };
00200 
00202         void enable_segment( segment_index_t k, bool is_initial = false ) {
00203             __TBB_ASSERT( k, "Zero segment must be embedded" );
00204             enable_segment_failsafe watchdog( my_table, k );
00205             cache_aligned_allocator<bucket> alloc;
00206             size_type sz;
00207             __TBB_ASSERT( !is_valid(my_table[k]), "Wrong concurrent assignment");
00208             if( k >= first_block ) {
00209                 sz = segment_size( k );
00210                 segment_ptr_t ptr = alloc.allocate( sz );
00211                 init_buckets( ptr, sz, is_initial );
00212 #if TBB_USE_THREADING_TOOLS
00213                 // TODO: actually, fence and notification are unnecessary here and below
00214                 itt_store_pointer_with_release_v3( my_table + k, ptr );
00215 #else
00216                 my_table[k] = ptr;// my_mask has release fence
00217 #endif
00218                 sz <<= 1;// double it to get entire capacity of the container
00219             } else { // the first block
00220                 __TBB_ASSERT( k == embedded_block, "Wrong segment index" );
00221                 sz = segment_size( first_block );
00222                 segment_ptr_t ptr = alloc.allocate( sz - embedded_buckets );
00223                 init_buckets( ptr, sz - embedded_buckets, is_initial );
00224                 ptr -= segment_base(embedded_block);
00225                 for(segment_index_t i = embedded_block; i < first_block; i++) // calc the offsets
00226 #if TBB_USE_THREADING_TOOLS
00227                     itt_store_pointer_with_release_v3( my_table + i, ptr + segment_base(i) );
00228 #else
00229                     my_table[i] = ptr + segment_base(i);
00230 #endif
00231             }
00232 #if TBB_USE_THREADING_TOOLS
00233             itt_store_pointer_with_release_v3( &my_mask, (void*)(sz-1) );
00234 #else
00235             my_mask = sz - 1;
00236 #endif
00237             watchdog.my_segment_ptr = 0;
00238         }
00239 
00241         bucket *get_bucket( hashcode_t h ) const throw() { // TODO: add throw() everywhere?
00242             segment_index_t s = segment_index_of( h );
00243             h -= segment_base(s);
00244             segment_ptr_t seg = my_table[s];
00245             __TBB_ASSERT( is_valid(seg), "hashcode must be cut by valid mask for allocated segments" );
00246             return &seg[h];
00247         }
00248 
00249         // internal serial rehashing helper
00250         void mark_rehashed_levels( hashcode_t h ) throw () {
00251             segment_index_t s = segment_index_of( h );
00252             while( segment_ptr_t seg = my_table[++s] )
00253                 if( seg[h].node_list == rehash_req ) {
00254                     seg[h].node_list = empty_rehashed;
00255                     mark_rehashed_levels( h + segment_base(s) );
00256                 }
00257         }
00258 
00260         // Splitting into two functions should help inlining
00261         inline bool check_mask_race( const hashcode_t h, hashcode_t &m ) const {
00262             hashcode_t m_now, m_old = m;
00263 #if TBB_USE_THREADING_TOOLS
00264             m_now = (hashcode_t) itt_load_pointer_with_acquire_v3( &my_mask );
00265 #else
00266             m_now = my_mask;
00267 #endif
00268             if( m_old != m_now )
00269                 return check_rehashing_collision( h, m_old, m = m_now );
00270             return false;
00271         }
00272 
00274         bool check_rehashing_collision( const hashcode_t h, hashcode_t m_old, hashcode_t m ) const {
00275             __TBB_ASSERT(m_old != m, NULL); // TODO?: m arg could be optimized out by passing h = h&m
00276             if( (h & m_old) != (h & m) ) { // mask changed for this hashcode, rare event
00277                 // condition above proves that 'h' has some other bits set beside 'm_old'
00278                 // find next applicable mask after m_old    //TODO: look at bsl instruction
00279                 for( ++m_old; !(h & m_old); m_old <<= 1 ) // at maximum few rounds depending on the first block size
00280                     ;
00281                 m_old = (m_old<<1) - 1; // get full mask from a bit
00282                 __TBB_ASSERT((m_old&(m_old+1))==0 && m_old <= m, NULL);
00283                 // check whether it is rehashing/ed
00284 #if TBB_USE_THREADING_TOOLS
00285                 if( itt_load_pointer_with_acquire_v3(&( get_bucket(h & m_old)->node_list )) != rehash_req )
00286 #else
00287                 if( __TBB_load_with_acquire(get_bucket( h & m_old )->node_list) != rehash_req )
00288 #endif
00289                     return true;
00290             }
00291             return false;
00292         }
00293 
00295         segment_index_t insert_new_node( bucket *b, node_base *n, hashcode_t mask ) {
00296             size_type sz = ++my_size; // prefix form is to enforce allocation after the first item inserted
00297             add_to_bucket( b, n );
00298             // check load factor
00299             if( sz >= mask ) { // TODO: add custom load_factor 
00300                 segment_index_t new_seg = segment_index_of( mask+1 );
00301                 __TBB_ASSERT( is_valid(my_table[new_seg-1]), "new allocations must not publish new mask until segment has allocated");
00302 #if TBB_USE_THREADING_TOOLS
00303                 if( !itt_load_pointer_v3(my_table+new_seg)
00304 #else
00305                 if( !my_table[new_seg]
00306 #endif
00307                   && __TBB_CompareAndSwapW(&my_table[new_seg], 2, 0) == 0 )
00308                     return new_seg; // The value must be processed
00309             }
00310             return 0;
00311         }
00312 
00314         void reserve(size_type buckets) {
00315             if( !buckets-- ) return;
00316             bool is_initial = !my_size;
00317             for( size_type m = my_mask; buckets > m; m = my_mask )
00318                 enable_segment( segment_index_of( m+1 ), is_initial );
00319         }
00321         void internal_swap(hash_map_base &table) {
00322             std::swap(this->my_mask, table.my_mask);
00323             std::swap(this->my_size, table.my_size);
00324             for(size_type i = 0; i < embedded_buckets; i++)
00325                 std::swap(this->my_embedded_segment[i].node_list, table.my_embedded_segment[i].node_list);
00326             for(size_type i = embedded_block; i < pointers_per_table; i++)
00327                 std::swap(this->my_table[i], table.my_table[i]);
00328         }
00329     };
00330 
00331     template<typename Iterator>
00332     class hash_map_range;
00333 
00335 
00337     template<typename Container, typename Value>
00338     class hash_map_iterator
00339         : public std::iterator<std::forward_iterator_tag,Value>
00340     {
00341         typedef Container map_type;
00342         typedef typename Container::node node;
00343         typedef hash_map_base::node_base node_base;
00344         typedef hash_map_base::bucket bucket;
00345 
00346         template<typename C, typename T, typename U>
00347         friend bool operator==( const hash_map_iterator<C,T>& i, const hash_map_iterator<C,U>& j );
00348 
00349         template<typename C, typename T, typename U>
00350         friend bool operator!=( const hash_map_iterator<C,T>& i, const hash_map_iterator<C,U>& j );
00351 
00352         template<typename C, typename T, typename U>
00353         friend ptrdiff_t operator-( const hash_map_iterator<C,T>& i, const hash_map_iterator<C,U>& j );
00354     
00355         template<typename C, typename U>
00356         friend class hash_map_iterator;
00357 
00358         template<typename I>
00359         friend class hash_map_range;
00360 
00361         void advance_to_next_bucket() { // TODO?: refactor to iterator_base class
00362             size_t k = my_index+1;
00363             while( my_bucket && k <= my_map->my_mask ) {
00364                 // Following test uses 2's-complement wizardry
00365                 if( k& (k-2) ) // not the beginning of a segment
00366                     ++my_bucket;
00367                 else my_bucket = my_map->get_bucket( k );
00368                 my_node = static_cast<node*>( my_bucket->node_list );
00369                 if( hash_map_base::is_valid(my_node) ) {
00370                     my_index = k; return;
00371                 }
00372                 ++k;
00373             }
00374             my_bucket = 0; my_node = 0; my_index = k; // the end
00375         }
00376 #if !defined(_MSC_VER) || defined(__INTEL_COMPILER)
00377         template<typename Key, typename T, typename HashCompare, typename A>
00378         friend class interface4::concurrent_hash_map;
00379 #else
00380     public: // workaround
00381 #endif
00382 
00383         const Container *my_map;
00384 
00386         size_t my_index;
00387 
00389         const bucket *my_bucket;
00390 
00392         node *my_node;
00393 
00394         hash_map_iterator( const Container &map, size_t index, const bucket *b, node_base *n );
00395 
00396     public:
00398         hash_map_iterator() {}
00399         hash_map_iterator( const hash_map_iterator<Container,typename Container::value_type> &other ) :
00400             my_map(other.my_map),
00401             my_index(other.my_index),
00402             my_bucket(other.my_bucket),
00403             my_node(other.my_node)
00404         {}
00405         Value& operator*() const {
00406             __TBB_ASSERT( hash_map_base::is_valid(my_node), "iterator uninitialized or at end of container?" );
00407             return my_node->item;
00408         }
00409         Value* operator->() const {return &operator*();}
00410         hash_map_iterator& operator++();
00411         
00413         Value* operator++(int) {
00414             Value* result = &operator*();
00415             operator++();
00416             return result;
00417         }
00418     };
00419 
00420     template<typename Container, typename Value>
00421     hash_map_iterator<Container,Value>::hash_map_iterator( const Container &map, size_t index, const bucket *b, node_base *n ) :
00422         my_map(&map),
00423         my_index(index),
00424         my_bucket(b),
00425         my_node( static_cast<node*>(n) )
00426     {
00427         if( b && !hash_map_base::is_valid(n) )
00428             advance_to_next_bucket();
00429     }
00430 
00431     template<typename Container, typename Value>
00432     hash_map_iterator<Container,Value>& hash_map_iterator<Container,Value>::operator++() {
00433         my_node = static_cast<node*>( my_node->next );
00434         if( !my_node ) advance_to_next_bucket();
00435         return *this;
00436     }
00437 
00438     template<typename Container, typename T, typename U>
00439     bool operator==( const hash_map_iterator<Container,T>& i, const hash_map_iterator<Container,U>& j ) {
00440         return i.my_node == j.my_node && i.my_map == j.my_map;
00441     }
00442 
00443     template<typename Container, typename T, typename U>
00444     bool operator!=( const hash_map_iterator<Container,T>& i, const hash_map_iterator<Container,U>& j ) {
00445         return i.my_node != j.my_node || i.my_map != j.my_map;
00446     }
00447 
00449 
00450     template<typename Iterator>
00451     class hash_map_range {
00452         typedef typename Iterator::map_type map_type;
00453         Iterator my_begin;
00454         Iterator my_end;
00455         mutable Iterator my_midpoint;
00456         size_t my_grainsize;
00458         void set_midpoint() const;
00459         template<typename U> friend class hash_map_range;
00460     public:
00462         typedef std::size_t size_type;
00463         typedef typename Iterator::value_type value_type;
00464         typedef typename Iterator::reference reference;
00465         typedef typename Iterator::difference_type difference_type;
00466         typedef Iterator iterator;
00467 
00469         bool empty() const {return my_begin==my_end;}
00470 
00472         bool is_divisible() const {
00473             return my_midpoint!=my_end;
00474         }
00476         hash_map_range( hash_map_range& r, split ) : 
00477             my_end(r.my_end),
00478             my_grainsize(r.my_grainsize)
00479         {
00480             r.my_end = my_begin = r.my_midpoint;
00481             __TBB_ASSERT( !empty(), "Splitting despite the range is not divisible" );
00482             __TBB_ASSERT( !r.empty(), "Splitting despite the range is not divisible" );
00483             set_midpoint();
00484             r.set_midpoint();
00485         }
00487         template<typename U>
00488         hash_map_range( hash_map_range<U>& r) : 
00489             my_begin(r.my_begin),
00490             my_end(r.my_end),
00491             my_midpoint(r.my_midpoint),
00492             my_grainsize(r.my_grainsize)
00493         {}
00494 #if TBB_DEPRECATED
00495 
00496         hash_map_range( const Iterator& begin_, const Iterator& end_, size_type grainsize_ = 1 ) : 
00497             my_begin(begin_), 
00498             my_end(end_),
00499             my_grainsize(grainsize_)
00500         {
00501             if(!my_end.my_index && !my_end.my_bucket) // end
00502                 my_end.my_index = my_end.my_map->my_mask + 1;
00503             set_midpoint();
00504             __TBB_ASSERT( grainsize_>0, "grainsize must be positive" );
00505         }
00506 #endif
00507 
00508         hash_map_range( const map_type &map, size_type grainsize_ = 1 ) : 
00509             my_begin( Iterator( map, 0, map.my_embedded_segment, map.my_embedded_segment->node_list ) ),
00510             my_end( Iterator( map, map.my_mask + 1, 0, 0 ) ),
00511             my_grainsize( grainsize_ )
00512         {
00513             __TBB_ASSERT( grainsize_>0, "grainsize must be positive" );
00514             set_midpoint();
00515         }
00516         const Iterator& begin() const {return my_begin;}
00517         const Iterator& end() const {return my_end;}
00519         size_type grainsize() const {return my_grainsize;}
00520     };
00521 
00522     template<typename Iterator>
00523     void hash_map_range<Iterator>::set_midpoint() const {
00524         // Split by groups of nodes
00525         size_t m = my_end.my_index-my_begin.my_index;
00526         if( m > my_grainsize ) {
00527             m = my_begin.my_index + m/2u;
00528             hash_map_base::bucket *b = my_begin.my_map->get_bucket(m);
00529             my_midpoint = Iterator(*my_begin.my_map,m,b,b->node_list);
00530         } else {
00531             my_midpoint = my_end;
00532         }
00533         __TBB_ASSERT( my_begin.my_index <= my_midpoint.my_index,
00534             "my_begin is after my_midpoint" );
00535         __TBB_ASSERT( my_midpoint.my_index <= my_end.my_index,
00536             "my_midpoint is after my_end" );
00537         __TBB_ASSERT( my_begin != my_midpoint || my_begin == my_end,
00538             "[my_begin, my_midpoint) range should not be empty" );
00539     }
00540 
00541     } // internal
00543 
00545 
00574 template<typename Key, typename T, typename HashCompare, typename Allocator>
00575 class concurrent_hash_map : protected internal::hash_map_base {
00576     template<typename Container, typename Value>
00577     friend class internal::hash_map_iterator;
00578 
00579     template<typename I>
00580     friend class internal::hash_map_range;
00581 
00582 public:
00583     typedef Key key_type;
00584     typedef T mapped_type;
00585     typedef std::pair<const Key,T> value_type;
00586     typedef hash_map_base::size_type size_type;
00587     typedef ptrdiff_t difference_type;
00588     typedef value_type *pointer;
00589     typedef const value_type *const_pointer;
00590     typedef value_type &reference;
00591     typedef const value_type &const_reference;
00592     typedef internal::hash_map_iterator<concurrent_hash_map,value_type> iterator;
00593     typedef internal::hash_map_iterator<concurrent_hash_map,const value_type> const_iterator;
00594     typedef internal::hash_map_range<iterator> range_type;
00595     typedef internal::hash_map_range<const_iterator> const_range_type;
00596     typedef Allocator allocator_type;
00597 
00598 protected:
00599     friend class const_accessor;
00600     struct node;
00601     typedef typename Allocator::template rebind<node>::other node_allocator_type;
00602     node_allocator_type my_allocator;
00603     HashCompare my_hash_compare;
00604 
00605     struct node : public node_base {
00606         value_type item;
00607         node( const Key &key ) : item(key, T()) {}
00608         node( const Key &key, const T &t ) : item(key, t) {}
00609         // exception-safe allocation, see C++ Standard 2003, clause 5.3.4p17
00610         void *operator new( size_t /*size*/, node_allocator_type &a ) {
00611             void *ptr = a.allocate(1);
00612             if(!ptr) 
00613                 tbb::internal::throw_exception(tbb::internal::eid_bad_alloc);
00614             return ptr;
00615         }
00616         // match placement-new form above to be called if exception thrown in constructor
00617         void operator delete( void *ptr, node_allocator_type &a ) {return a.deallocate(static_cast<node*>(ptr),1); }
00618     };
00619 
00620     void delete_node( node_base *n ) {
00621         my_allocator.destroy( static_cast<node*>(n) );
00622         my_allocator.deallocate( static_cast<node*>(n), 1);
00623     }
00624 
00625     node *search_bucket( const key_type &key, bucket *b ) const {
00626         node *n = static_cast<node*>( b->node_list );
00627         while( is_valid(n) && !my_hash_compare.equal(key, n->item.first) )
00628             n = static_cast<node*>( n->next );
00629         __TBB_ASSERT(n != internal::rehash_req, "Search can be executed only for rehashed bucket");
00630         return n;
00631     }
00632 
00634     class bucket_accessor : public bucket::scoped_t {
00635         bool my_is_writer; // TODO: use it from base type
00636         bucket *my_b;
00637     public:
00638         bucket_accessor( concurrent_hash_map *base, const hashcode_t h, bool writer = false ) { acquire( base, h, writer ); }
00640         inline void acquire( concurrent_hash_map *base, const hashcode_t h, bool writer = false ) {
00641             my_b = base->get_bucket( h );
00642 #if TBB_USE_THREADING_TOOLS
00643             // TODO: actually, notification is unnecessary here, just hiding double-check
00644             if( itt_load_pointer_with_acquire_v3(&my_b->node_list) == internal::rehash_req
00645 #else
00646             if( __TBB_load_with_acquire(my_b->node_list) == internal::rehash_req
00647 #endif
00648                 && try_acquire( my_b->mutex, /*write=*/true ) )
00649             {
00650                 if( my_b->node_list == internal::rehash_req ) base->rehash_bucket( my_b, h ); //recursive rehashing
00651                 my_is_writer = true;
00652             }
00653             else bucket::scoped_t::acquire( my_b->mutex, /*write=*/my_is_writer = writer );
00654             __TBB_ASSERT( my_b->node_list != internal::rehash_req, NULL);
00655         }
00657         bool is_writer() { return my_is_writer; }
00659         bucket *operator() () { return my_b; }
00660         // TODO: optimize out
00661         bool upgrade_to_writer() { my_is_writer = true; return bucket::scoped_t::upgrade_to_writer(); }
00662     };
00663 
00664     // TODO refactor to hash_base
00665     void rehash_bucket( bucket *b_new, const hashcode_t h ) {
00666         __TBB_ASSERT( *(intptr_t*)(&b_new->mutex), "b_new must be locked (for write)");
00667         __TBB_ASSERT( h > 1, "The lowermost buckets can't be rehashed" );
00668         __TBB_store_with_release(b_new->node_list, internal::empty_rehashed); // mark rehashed
00669         hashcode_t mask = ( 1u<<__TBB_Log2( h ) ) - 1; // get parent mask from the topmost bit
00670 
00671         bucket_accessor b_old( this, h & mask );
00672 
00673         mask = (mask<<1) | 1; // get full mask for new bucket
00674         __TBB_ASSERT( (mask&(mask+1))==0 && (h & mask) == h, NULL );
00675     restart:
00676         for( node_base **p = &b_old()->node_list, *n = __TBB_load_with_acquire(*p); is_valid(n); n = *p ) {
00677             hashcode_t c = my_hash_compare.hash( static_cast<node*>(n)->item.first );
00678 #if TBB_USE_ASSERT
00679             hashcode_t bmask = h & (mask>>1);
00680             bmask = bmask==0? 1 : ( 1u<<(__TBB_Log2( bmask )+1 ) ) - 1; // minimal mask of parent bucket
00681             __TBB_ASSERT( (c & bmask) == (h & bmask), "hash() function changed for key in table" );
00682 #endif
00683             if( (c & mask) == h ) {
00684                 if( !b_old.is_writer() )
00685                     if( !b_old.upgrade_to_writer() ) {
00686                         goto restart; // node ptr can be invalid due to concurrent erase
00687                     }
00688                 *p = n->next; // exclude from b_old
00689                 add_to_bucket( b_new, n );
00690             } else p = &n->next; // iterate to next item
00691         }
00692     }
00693 
00694 public:
00695     
00696     class accessor;
00698     class const_accessor {
00699         friend class concurrent_hash_map<Key,T,HashCompare,Allocator>;
00700         friend class accessor;
00701         void operator=( const accessor & ) const; // Deny access
00702         const_accessor( const accessor & );       // Deny access
00703     public:
00705         typedef const typename concurrent_hash_map::value_type value_type;
00706 
00708         bool empty() const {return !my_node;}
00709 
00711         void release() {
00712             if( my_node ) {
00713                 my_lock.release();
00714                 my_node = 0;
00715             }
00716         }
00717 
00719         const_reference operator*() const {
00720             __TBB_ASSERT( my_node, "attempt to dereference empty accessor" );
00721             return my_node->item;
00722         }
00723 
00725         const_pointer operator->() const {
00726             return &operator*();
00727         }
00728 
00730         const_accessor() : my_node(NULL) {}
00731 
00733         ~const_accessor() {
00734             my_node = NULL; // my_lock.release() is called in scoped_lock destructor
00735         }
00736     private:
00737         node *my_node;
00738         typename node::scoped_t my_lock;
00739         hashcode_t my_hash;
00740     };
00741 
00743     class accessor: public const_accessor {
00744     public:
00746         typedef typename concurrent_hash_map::value_type value_type;
00747 
00749         reference operator*() const {
00750             __TBB_ASSERT( this->my_node, "attempt to dereference empty accessor" );
00751             return this->my_node->item;
00752         }
00753 
00755         pointer operator->() const {
00756             return &operator*();
00757         }
00758     };
00759 
00761     concurrent_hash_map(const allocator_type &a = allocator_type())
00762         : internal::hash_map_base(), my_allocator(a)
00763     {}
00764 
00766     concurrent_hash_map(size_type n, const allocator_type &a = allocator_type())
00767         : my_allocator(a)
00768     {
00769         reserve( n );
00770     }
00771 
00773     concurrent_hash_map( const concurrent_hash_map& table, const allocator_type &a = allocator_type())
00774         : internal::hash_map_base(), my_allocator(a)
00775     {
00776         internal_copy(table);
00777     }
00778 
00780     template<typename I>
00781     concurrent_hash_map(I first, I last, const allocator_type &a = allocator_type())
00782         : my_allocator(a)
00783     {
00784         reserve( std::distance(first, last) ); // TODO: load_factor?
00785         internal_copy(first, last);
00786     }
00787 
00789     concurrent_hash_map& operator=( const concurrent_hash_map& table ) {
00790         if( this!=&table ) {
00791             clear();
00792             internal_copy(table);
00793         } 
00794         return *this;
00795     }
00796 
00797 
00799 
00801     void rehash(size_type n = 0);
00802     
00804     void clear();
00805 
00807     ~concurrent_hash_map() { clear(); }
00808 
00809     //------------------------------------------------------------------------
00810     // Parallel algorithm support
00811     //------------------------------------------------------------------------
00812     range_type range( size_type grainsize=1 ) {
00813         return range_type( *this, grainsize );
00814     }
00815     const_range_type range( size_type grainsize=1 ) const {
00816         return const_range_type( *this, grainsize );
00817     }
00818 
00819     //------------------------------------------------------------------------
00820     // STL support - not thread-safe methods
00821     //------------------------------------------------------------------------
00822     iterator begin() {return iterator(*this,0,my_embedded_segment,my_embedded_segment->node_list);}
00823     iterator end() {return iterator(*this,0,0,0);}
00824     const_iterator begin() const {return const_iterator(*this,0,my_embedded_segment,my_embedded_segment->node_list);}
00825     const_iterator end() const {return const_iterator(*this,0,0,0);}
00826     std::pair<iterator, iterator> equal_range( const Key& key ) { return internal_equal_range(key, end()); }
00827     std::pair<const_iterator, const_iterator> equal_range( const Key& key ) const { return internal_equal_range(key, end()); }
00828     
00830     size_type size() const { return my_size; }
00831 
00833     bool empty() const { return my_size == 0; }
00834 
00836     size_type max_size() const {return (~size_type(0))/sizeof(node);}
00837 
00839     size_type bucket_count() const { return my_mask+1; }
00840 
00842     allocator_type get_allocator() const { return this->my_allocator; }
00843 
00845     void swap(concurrent_hash_map &table);
00846 
00847     //------------------------------------------------------------------------
00848     // concurrent map operations
00849     //------------------------------------------------------------------------
00850 
00852     size_type count( const Key &key ) const {
00853         return const_cast<concurrent_hash_map*>(this)->lookup(/*insert*/false, key, NULL, NULL, /*write=*/false );
00854     }
00855 
00857 
00858     bool find( const_accessor &result, const Key &key ) const {
00859         result.release();
00860         return const_cast<concurrent_hash_map*>(this)->lookup(/*insert*/false, key, NULL, &result, /*write=*/false );
00861     }
00862 
00864 
00865     bool find( accessor &result, const Key &key ) {
00866         result.release();
00867         return lookup(/*insert*/false, key, NULL, &result, /*write=*/true );
00868     }
00869         
00871 
00872     bool insert( const_accessor &result, const Key &key ) {
00873         result.release();
00874         return lookup(/*insert*/true, key, NULL, &result, /*write=*/false );
00875     }
00876 
00878 
00879     bool insert( accessor &result, const Key &key ) {
00880         result.release();
00881         return lookup(/*insert*/true, key, NULL, &result, /*write=*/true );
00882     }
00883 
00885 
00886     bool insert( const_accessor &result, const value_type &value ) {
00887         result.release();
00888         return lookup(/*insert*/true, value.first, &value.second, &result, /*write=*/false );
00889     }
00890 
00892 
00893     bool insert( accessor &result, const value_type &value ) {
00894         result.release();
00895         return lookup(/*insert*/true, value.first, &value.second, &result, /*write=*/true );
00896     }
00897 
00899 
00900     bool insert( const value_type &value ) {
00901         return lookup(/*insert*/true, value.first, &value.second, NULL, /*write=*/false );
00902     }
00903 
00905     template<typename I>
00906     void insert(I first, I last) {
00907         for(; first != last; ++first)
00908             insert( *first );
00909     }
00910 
00912 
00913     bool erase( const Key& key );
00914 
00916 
00917     bool erase( const_accessor& item_accessor ) {
00918         return exclude( item_accessor, /*readonly=*/ true );
00919     }
00920 
00922 
00923     bool erase( accessor& item_accessor ) {
00924         return exclude( item_accessor, /*readonly=*/ false );
00925     }
00926 
00927 protected:
00929     bool lookup( bool op_insert, const Key &key, const T *t, const_accessor *result, bool write );
00930 
00932     bool exclude( const_accessor &item_accessor, bool readonly );
00933 
00935     template<typename I>
00936     std::pair<I, I> internal_equal_range( const Key& key, I end ) const;
00937 
00939     void internal_copy( const concurrent_hash_map& source );
00940 
00941     template<typename I>
00942     void internal_copy(I first, I last);
00943 
00945 
00947     const_pointer internal_fast_find( const Key& key ) const {
00948         hashcode_t h = my_hash_compare.hash( key );
00949 #if TBB_USE_THREADING_TOOLS
00950         hashcode_t m = (hashcode_t) itt_load_pointer_with_acquire_v3( &my_mask );
00951 #else
00952         hashcode_t m = my_mask;
00953 #endif
00954         node *n;
00955     restart:
00956         __TBB_ASSERT((m&(m+1))==0, NULL);
00957         bucket *b = get_bucket( h & m );
00958 #if TBB_USE_THREADING_TOOLS
00959         // TODO: actually, notification is unnecessary here, just hiding double-check
00960         if( itt_load_pointer_with_acquire_v3(&b->node_list) == internal::rehash_req )
00961 #else
00962         if( __TBB_load_with_acquire(b->node_list) == internal::rehash_req )
00963 #endif
00964         {
00965             bucket::scoped_t lock;
00966             if( lock.try_acquire( b->mutex, /*write=*/true ) ) {
00967                 if( b->node_list == internal::rehash_req)
00968                     const_cast<concurrent_hash_map*>(this)->rehash_bucket( b, h & m ); //recursive rehashing
00969             }
00970             else lock.acquire( b->mutex, /*write=*/false );
00971             __TBB_ASSERT(b->node_list!=internal::rehash_req,NULL);
00972         }
00973         n = search_bucket( key, b );
00974         if( n )
00975             return &n->item;
00976         else if( check_mask_race( h, m ) )
00977             goto restart;
00978         return 0;
00979     }
00980 };
00981 
00982 #if _MSC_VER && !defined(__INTEL_COMPILER)
00983     // Suppress "conditional expression is constant" warning.
00984     #pragma warning( push )
00985     #pragma warning( disable: 4127 )
00986 #endif
00987 
00988 template<typename Key, typename T, typename HashCompare, typename A>
00989 bool concurrent_hash_map<Key,T,HashCompare,A>::lookup( bool op_insert, const Key &key, const T *t, const_accessor *result, bool write ) {
00990     __TBB_ASSERT( !result || !result->my_node, NULL );
00991     segment_index_t grow_segment;
00992     bool return_value;
00993     node *n, *tmp_n = 0;
00994     hashcode_t const h = my_hash_compare.hash( key );
00995 #if TBB_USE_THREADING_TOOLS
00996     hashcode_t m = (hashcode_t) itt_load_pointer_with_acquire_v3( &my_mask );
00997 #else
00998     hashcode_t m = my_mask;
00999 #endif
01000     restart:
01001     {//lock scope
01002         __TBB_ASSERT((m&(m+1))==0, NULL);
01003         return_value = false;
01004         // get bucket
01005         bucket_accessor b( this, h & m );
01006 
01007         // find a node
01008         n = search_bucket( key, b() );
01009         if( op_insert ) {
01010             // [opt] insert a key
01011             if( !n ) {
01012                 if( !tmp_n ) {
01013                     if(t) tmp_n = new( my_allocator ) node(key, *t);
01014                     else  tmp_n = new( my_allocator ) node(key);
01015                 }
01016                 if( !b.is_writer() && !b.upgrade_to_writer() ) { // TODO: improved insertion
01017                     // Rerun search_list, in case another thread inserted the item during the upgrade.
01018                     n = search_bucket( key, b() );
01019                     if( is_valid(n) ) { // unfortunately, it did
01020                         b.downgrade_to_reader();
01021                         goto exists;
01022                     }
01023                 }
01024                 if( check_mask_race(h, m) )
01025                     goto restart; // b.release() is done in ~b().
01026                 // insert and set flag to grow the container
01027                 grow_segment = insert_new_node( b(), n = tmp_n, m );
01028                 tmp_n = 0;
01029                 return_value = true;
01030             } else {
01031     exists:     grow_segment = 0;
01032             }
01033         } else { // find or count
01034             if( !n ) {
01035                 if( check_mask_race( h, m ) )
01036                     goto restart; // b.release() is done in ~b(). TODO: replace by continue
01037                 return false;
01038             }
01039             return_value = true;
01040             grow_segment = 0;
01041         }
01042         if( !result ) goto check_growth;
01043         // TODO: the following seems as generic/regular operation
01044         // acquire the item
01045         if( !result->my_lock.try_acquire( n->mutex, write ) ) {
01046             // we are unlucky, prepare for longer wait
01047             tbb::internal::atomic_backoff trials;
01048             do {
01049                 if( !trials.bounded_pause() ) {
01050                     // the wait takes really long, restart the operation
01051                     b.release();
01052                     __TBB_ASSERT( !op_insert || !return_value, "Can't acquire new item in locked bucket?" );
01053                     __TBB_Yield();
01054 #if TBB_USE_THREADING_TOOLS
01055                     m = (hashcode_t) itt_load_pointer_with_acquire_v3( &my_mask );
01056 #else
01057                     m = my_mask;
01058 #endif
01059                     goto restart;
01060                 }
01061             } while( !result->my_lock.try_acquire( n->mutex, write ) );
01062         }
01063     }//lock scope
01064     result->my_node = n;
01065     result->my_hash = h;
01066 check_growth:
01067     // [opt] grow the container
01068     if( grow_segment )
01069         enable_segment( grow_segment );
01070     if( tmp_n ) // if op_insert only
01071         delete_node( tmp_n );
01072     return return_value;
01073 }
01074 
01075 template<typename Key, typename T, typename HashCompare, typename A>
01076 template<typename I>
01077 std::pair<I, I> concurrent_hash_map<Key,T,HashCompare,A>::internal_equal_range( const Key& key, I end_ ) const {
01078     hashcode_t h = my_hash_compare.hash( key );
01079     hashcode_t m = my_mask;
01080     __TBB_ASSERT((m&(m+1))==0, NULL);
01081     h &= m;
01082     bucket *b = get_bucket( h );
01083     while( b->node_list == internal::rehash_req ) {
01084         m = ( 1u<<__TBB_Log2( h ) ) - 1; // get parent mask from the topmost bit
01085         b = get_bucket( h &= m );
01086     }
01087     node *n = search_bucket( key, b );
01088     if( !n )
01089         return std::make_pair(end_, end_);
01090     iterator lower(*this, h, b, n), upper(lower);
01091     return std::make_pair(lower, ++upper);
01092 }
01093 
01094 template<typename Key, typename T, typename HashCompare, typename A>
01095 bool concurrent_hash_map<Key,T,HashCompare,A>::exclude( const_accessor &item_accessor, bool readonly ) {
01096     __TBB_ASSERT( item_accessor.my_node, NULL );
01097     node_base *const n = item_accessor.my_node;
01098     item_accessor.my_node = NULL; // we ought release accessor anyway
01099     hashcode_t const h = item_accessor.my_hash;
01100 #if TBB_USE_THREADING_TOOLS
01101     hashcode_t m = (hashcode_t) itt_load_pointer_with_acquire_v3( &my_mask );
01102 #else
01103     hashcode_t m = my_mask;
01104 #endif
01105     do {
01106         // get bucket
01107         bucket_accessor b( this, h & m, /*writer=*/true );
01108         node_base **p = &b()->node_list;
01109         while( *p && *p != n )
01110             p = &(*p)->next;
01111         if( !*p ) { // someone else was the first
01112             if( check_mask_race( h, m ) )
01113                 continue;
01114             item_accessor.my_lock.release();
01115             return false;
01116         }
01117         __TBB_ASSERT( *p == n, NULL );
01118         *p = n->next; // remove from container
01119         my_size--;
01120         break;
01121     } while(true);
01122     if( readonly ) // need to get exclusive lock
01123         item_accessor.my_lock.upgrade_to_writer(); // return value means nothing here
01124     item_accessor.my_lock.release();
01125     delete_node( n ); // Only one thread can delete it due to write lock on the chain_mutex
01126     return true;
01127 }
01128 
01129 template<typename Key, typename T, typename HashCompare, typename A>
01130 bool concurrent_hash_map<Key,T,HashCompare,A>::erase( const Key &key ) {
01131     node_base *n;
01132     hashcode_t const h = my_hash_compare.hash( key );
01133 #if TBB_USE_THREADING_TOOLS
01134     hashcode_t m = (hashcode_t) itt_load_pointer_with_acquire_v3( &my_mask );
01135 #else
01136     hashcode_t m = my_mask;
01137 #endif
01138 restart:
01139     {//lock scope
01140         // get bucket
01141         bucket_accessor b( this, h & m );
01142     search:
01143         node_base **p = &b()->node_list;
01144         n = *p;
01145         while( is_valid(n) && !my_hash_compare.equal(key, static_cast<node*>(n)->item.first ) ) {
01146             p = &n->next;
01147             n = *p;
01148         }
01149         if( !n ) { // not found, but mask could be changed
01150             if( check_mask_race( h, m ) )
01151                 goto restart;
01152             return false;
01153         }
01154         else if( !b.is_writer() && !b.upgrade_to_writer() ) {
01155             if( check_mask_race( h, m ) ) // contended upgrade, check mask
01156                 goto restart;
01157             goto search;
01158         }
01159         *p = n->next;
01160         my_size--;
01161     }
01162     {
01163         typename node::scoped_t item_locker( n->mutex, /*write=*/true );
01164     }
01165     // note: there should be no threads pretending to acquire this mutex again, do not try to upgrade const_accessor!
01166     delete_node( n ); // Only one thread can delete it due to write lock on the bucket
01167     return true;
01168 }
01169 
01170 template<typename Key, typename T, typename HashCompare, typename A>
01171 void concurrent_hash_map<Key,T,HashCompare,A>::swap(concurrent_hash_map<Key,T,HashCompare,A> &table) {
01172     std::swap(this->my_allocator, table.my_allocator);
01173     std::swap(this->my_hash_compare, table.my_hash_compare);
01174     internal_swap(table);
01175 }
01176 
01177 template<typename Key, typename T, typename HashCompare, typename A>
01178 void concurrent_hash_map<Key,T,HashCompare,A>::rehash(size_type sz) {
01179     reserve( sz ); // TODO: add reduction of number of buckets as well
01180     hashcode_t mask = my_mask;
01181     hashcode_t b = (mask+1)>>1; // size or first index of the last segment
01182     __TBB_ASSERT((b&(b-1))==0, NULL);
01183     bucket *bp = get_bucket( b ); // only the last segment should be scanned for rehashing
01184     for(; b <= mask; b++, bp++ ) {
01185         node_base *n = bp->node_list;
01186         __TBB_ASSERT( is_valid(n) || n == internal::empty_rehashed || n == internal::rehash_req, "Broken internal structure" );
01187         __TBB_ASSERT( *reinterpret_cast<intptr_t*>(&bp->mutex) == 0, "concurrent or unexpectedly terminated operation during rehash() execution" );
01188         if( n == internal::rehash_req ) { // rehash bucket, conditional because rehashing of a previous bucket may affect this one
01189             hashcode_t h = b; bucket *b_old = bp;
01190             do {
01191                 __TBB_ASSERT( h > 1, "The lowermost buckets can't be rehashed" );
01192                 hashcode_t m = ( 1u<<__TBB_Log2( h ) ) - 1; // get parent mask from the topmost bit
01193                 b_old = get_bucket( h &= m );
01194             } while( b_old->node_list == internal::rehash_req );
01195             // now h - is index of the root rehashed bucket b_old
01196             mark_rehashed_levels( h ); // mark all non-rehashed children recursively across all segments
01197             for( node_base **p = &b_old->node_list, *q = *p; is_valid(q); q = *p ) {
01198                 hashcode_t c = my_hash_compare.hash( static_cast<node*>(q)->item.first );
01199                 if( (c & mask) != h ) { // should be rehashed
01200                     *p = q->next; // exclude from b_old
01201                     bucket *b_new = get_bucket( c & mask );
01202                     __TBB_ASSERT( b_new->node_list != internal::rehash_req, "hash() function changed for key in table or internal error" );
01203                     add_to_bucket( b_new, q );
01204                 } else p = &q->next; // iterate to next item
01205             }
01206         }
01207     }
01208 #if TBB_USE_PERFORMANCE_WARNINGS
01209     int current_size = int(my_size), buckets = int(mask)+1, empty_buckets = 0, overpopulated_buckets = 0; // usage statistics
01210     static bool reported = false;
01211 #endif
01212 #if TBB_USE_ASSERT || TBB_USE_PERFORMANCE_WARNINGS
01213     for( b = 0; b <= mask; b++ ) {// only last segment should be scanned for rehashing
01214         if( b & (b-2) ) ++bp; // not the beginning of a segment
01215         else bp = get_bucket( b );
01216         node_base *n = bp->node_list;
01217         __TBB_ASSERT( *reinterpret_cast<intptr_t*>(&bp->mutex) == 0, "concurrent or unexpectedly terminated operation during rehash() execution" );
01218         __TBB_ASSERT( is_valid(n) || n == internal::empty_rehashed, "Broken internal structure" );
01219 #if TBB_USE_PERFORMANCE_WARNINGS
01220         if( n == internal::empty_rehashed ) empty_buckets++;
01221         else if( n->next ) overpopulated_buckets++;
01222 #endif
01223 #if TBB_USE_ASSERT
01224         for( ; is_valid(n); n = n->next ) {
01225             hashcode_t h = my_hash_compare.hash( static_cast<node*>(n)->item.first ) & mask;
01226             __TBB_ASSERT( h == b, "hash() function changed for key in table or internal error" );
01227         }
01228 #endif
01229     }
01230 #endif // TBB_USE_ASSERT || TBB_USE_PERFORMANCE_WARNINGS
01231 #if TBB_USE_PERFORMANCE_WARNINGS
01232     if( buckets > current_size) empty_buckets -= buckets - current_size;
01233     else overpopulated_buckets -= current_size - buckets; // TODO: load_factor?
01234     if( !reported && buckets >= 512 && ( 2*empty_buckets > current_size || 2*overpopulated_buckets > current_size ) ) {
01235         tbb::internal::runtime_warning(
01236             "Performance is not optimal because the hash function produces bad randomness in lower bits in %s.\nSize: %d  Empties: %d  Overlaps: %d",
01237             typeid(*this).name(), current_size, empty_buckets, overpopulated_buckets );
01238         reported = true;
01239     }
01240 #endif
01241 }
01242 
01243 template<typename Key, typename T, typename HashCompare, typename A>
01244 void concurrent_hash_map<Key,T,HashCompare,A>::clear() {
01245     hashcode_t m = my_mask;
01246     __TBB_ASSERT((m&(m+1))==0, NULL);
01247 #if TBB_USE_ASSERT || TBB_USE_PERFORMANCE_WARNINGS
01248 #if TBB_USE_PERFORMANCE_WARNINGS
01249     int current_size = int(my_size), buckets = int(m)+1, empty_buckets = 0, overpopulated_buckets = 0; // usage statistics
01250     static bool reported = false;
01251 #endif
01252     bucket *bp = 0;
01253     // check consistency
01254     for( segment_index_t b = 0; b <= m; b++ ) {
01255         if( b & (b-2) ) ++bp; // not the beginning of a segment
01256         else bp = get_bucket( b );
01257         node_base *n = bp->node_list;
01258         __TBB_ASSERT( is_valid(n) || n == internal::empty_rehashed || n == internal::rehash_req, "Broken internal structure" );
01259         __TBB_ASSERT( *reinterpret_cast<intptr_t*>(&bp->mutex) == 0, "concurrent or unexpectedly terminated operation during clear() execution" );
01260 #if TBB_USE_PERFORMANCE_WARNINGS
01261         if( n == internal::empty_rehashed ) empty_buckets++;
01262         else if( n == internal::rehash_req ) buckets--;
01263         else if( n->next ) overpopulated_buckets++;
01264 #endif
01265 #if __TBB_EXTRA_DEBUG
01266         for(; is_valid(n); n = n->next ) {
01267             hashcode_t h = my_hash_compare.hash( static_cast<node*>(n)->item.first );
01268             h &= m;
01269             __TBB_ASSERT( h == b || get_bucket(h)->node_list == internal::rehash_req, "hash() function changed for key in table or internal error" );
01270         }
01271 #endif
01272     }
01273 #if TBB_USE_PERFORMANCE_WARNINGS
01274     if( buckets > current_size) empty_buckets -= buckets - current_size;
01275     else overpopulated_buckets -= current_size - buckets; // TODO: load_factor?
01276     if( !reported && buckets >= 512 && ( 2*empty_buckets > current_size || 2*overpopulated_buckets > current_size ) ) {
01277         tbb::internal::runtime_warning(
01278             "Performance is not optimal because the hash function produces bad randomness in lower bits in %s.\nSize: %d  Empties: %d  Overlaps: %d",
01279             typeid(*this).name(), current_size, empty_buckets, overpopulated_buckets );
01280         reported = true;
01281     }
01282 #endif
01283 #endif//TBB_USE_ASSERT || TBB_USE_PERFORMANCE_WARNINGS
01284     my_size = 0;
01285     segment_index_t s = segment_index_of( m );
01286     __TBB_ASSERT( s+1 == pointers_per_table || !my_table[s+1], "wrong mask or concurrent grow" );
01287     cache_aligned_allocator<bucket> alloc;
01288     do {
01289         __TBB_ASSERT( is_valid( my_table[s] ), "wrong mask or concurrent grow" );
01290         segment_ptr_t buckets_ptr = my_table[s];
01291         size_type sz = segment_size( s ? s : 1 );
01292         for( segment_index_t i = 0; i < sz; i++ )
01293             for( node_base *n = buckets_ptr[i].node_list; is_valid(n); n = buckets_ptr[i].node_list ) {
01294                 buckets_ptr[i].node_list = n->next;
01295                 delete_node( n );
01296             }
01297         if( s >= first_block) // the first segment or the next
01298             alloc.deallocate( buckets_ptr, sz );
01299         else if( s == embedded_block && embedded_block != first_block )
01300             alloc.deallocate( buckets_ptr, segment_size(first_block)-embedded_buckets );
01301         if( s >= embedded_block ) my_table[s] = 0;
01302     } while(s-- > 0);
01303     my_mask = embedded_buckets - 1;
01304 }
01305 
01306 template<typename Key, typename T, typename HashCompare, typename A>
01307 void concurrent_hash_map<Key,T,HashCompare,A>::internal_copy( const concurrent_hash_map& source ) {
01308     reserve( source.my_size ); // TODO: load_factor?
01309     hashcode_t mask = source.my_mask;
01310     if( my_mask == mask ) { // optimized version
01311         bucket *dst = 0, *src = 0;
01312         bool rehash_required = false;
01313         for( hashcode_t k = 0; k <= mask; k++ ) {
01314             if( k & (k-2) ) ++dst,src++; // not the beginning of a segment
01315             else { dst = get_bucket( k ); src = source.get_bucket( k ); }
01316             __TBB_ASSERT( dst->node_list != internal::rehash_req, "Invalid bucket in destination table");
01317             node *n = static_cast<node*>( src->node_list );
01318             if( n == internal::rehash_req ) { // source is not rehashed, items are in previous buckets
01319                 rehash_required = true;
01320                 dst->node_list = internal::rehash_req;
01321             } else for(; n; n = static_cast<node*>( n->next ) ) {
01322                 add_to_bucket( dst, new( my_allocator ) node(n->item.first, n->item.second) );
01323                 ++my_size; // TODO: replace by non-atomic op
01324             }
01325         }
01326         if( rehash_required ) rehash();
01327     } else internal_copy( source.begin(), source.end() );
01328 }
01329 
01330 template<typename Key, typename T, typename HashCompare, typename A>
01331 template<typename I>
01332 void concurrent_hash_map<Key,T,HashCompare,A>::internal_copy(I first, I last) {
01333     hashcode_t m = my_mask;
01334     for(; first != last; ++first) {
01335         hashcode_t h = my_hash_compare.hash( first->first );
01336         bucket *b = get_bucket( h & m );
01337         __TBB_ASSERT( b->node_list != internal::rehash_req, "Invalid bucket in destination table");
01338         node *n = new( my_allocator ) node(first->first, first->second);
01339         add_to_bucket( b, n );
01340         ++my_size; // TODO: replace by non-atomic op
01341     }
01342 }
01343 
01344 } // namespace interface4
01345 
01346 using interface4::concurrent_hash_map;
01347 
01348 
01349 template<typename Key, typename T, typename HashCompare, typename A1, typename A2>
01350 inline bool operator==(const concurrent_hash_map<Key, T, HashCompare, A1> &a, const concurrent_hash_map<Key, T, HashCompare, A2> &b) {
01351     if(a.size() != b.size()) return false;
01352     typename concurrent_hash_map<Key, T, HashCompare, A1>::const_iterator i(a.begin()), i_end(a.end());
01353     typename concurrent_hash_map<Key, T, HashCompare, A2>::const_iterator j, j_end(b.end());
01354     for(; i != i_end; ++i) {
01355         j = b.equal_range(i->first).first;
01356         if( j == j_end || !(i->second == j->second) ) return false;
01357     }
01358     return true;
01359 }
01360 
01361 template<typename Key, typename T, typename HashCompare, typename A1, typename A2>
01362 inline bool operator!=(const concurrent_hash_map<Key, T, HashCompare, A1> &a, const concurrent_hash_map<Key, T, HashCompare, A2> &b)
01363 {    return !(a == b); }
01364 
01365 template<typename Key, typename T, typename HashCompare, typename A>
01366 inline void swap(concurrent_hash_map<Key, T, HashCompare, A> &a, concurrent_hash_map<Key, T, HashCompare, A> &b)
01367 {    a.swap( b ); }
01368 
01369 #if _MSC_VER && !defined(__INTEL_COMPILER)
01370     #pragma warning( pop )
01371 #endif // warning 4127 is back
01372 
01373 } // namespace tbb
01374 
01375 #endif /* __TBB_concurrent_hash_map_H */

Copyright © 2007-2010 by The Shadowrun: Awakened Team. This work is licensed under the GNU Lesser General Public License 3.

GNU Lesser General Public License 3 Sourceforge.net