![]() |
Shadowrun: Awakened 29 September 2011 - Build 871
|
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.