concurrent_hash_map.h

00001 /*
00002     Copyright 2005-2010 Intel Corporation.  All Rights Reserved.
00003 
00004     The source code contained or described herein and all documents related
00005     to the source code ("Material") are owned by Intel Corporation or its
00006     suppliers or licensors.  Title to the Material remains with Intel
00007     Corporation or its suppliers and licensors.  The Material is protected
00008     by worldwide copyright laws and treaty provisions.  No part of the
00009     Material may be used, copied, reproduced, modified, published, uploaded,
00010     posted, transmitted, distributed, or disclosed in any way without
00011     Intel's prior express written permission.
00012 
00013     No license under any patent, copyright, trade secret or other
00014     intellectual property right is granted to or conferred upon you by
00015     disclosure or delivery of the Materials, either expressly, by
00016     implication, inducement, estoppel or otherwise.  Any license under such
00017     intellectual property rights must be express and approved by Intel in
00018     writing.
00019 */
00020 
00021 #ifndef __TBB_concurrent_hash_map_H
00022 #define __TBB_concurrent_hash_map_H
00023 
00024 #include "tbb_stddef.h"
00025 
00026 #if !TBB_USE_EXCEPTIONS && _MSC_VER
00027     // Suppress "C++ exception handler used, but unwind semantics are not enabled" warning in STL headers
00028     #pragma warning (push)
00029     #pragma warning (disable: 4530)
00030 #endif
00031 
00032 #include <iterator>
00033 #include <utility>      // Need std::pair
00034 #include <cstring>      // Need std::memset
00035 
00036 #if !TBB_USE_EXCEPTIONS && _MSC_VER
00037     #pragma warning (pop)
00038 #endif
00039 
00040 #include "cache_aligned_allocator.h"
00041 #include "tbb_allocator.h"
00042 #include "spin_rw_mutex.h"
00043 #include "atomic.h"
00044 #include "aligned_space.h"
00045 #include "tbb_exception.h"
00046 #include "_concurrent_unordered_internal.h" // Need tbb_hasher
00047 #if TBB_USE_PERFORMANCE_WARNINGS
00048 #include <typeinfo>
00049 #endif
00050 
00051 namespace tbb {
00052 
00054 namespace internal {
00056     void* __TBB_EXPORTED_FUNC itt_load_pointer_with_acquire_v3( const void* src );
00058     void __TBB_EXPORTED_FUNC itt_store_pointer_with_release_v3( void* dst, void* src );
00060     void* __TBB_EXPORTED_FUNC itt_load_pointer_v3( const void* src );
00061 }
00063 
00065 template<typename Key>
00066 struct tbb_hash_compare {
00067     static size_t hash( const Key& a ) { return tbb_hasher(a); }
00068     static bool equal( const Key& a, const Key& b ) { return a == b; }
00069 };
00070 
00071 namespace interface4 {
00072 
00073     template<typename Key, typename T, typename HashCompare = tbb_hash_compare<Key>, typename A = tbb_allocator<std::pair<Key, T> > >
00074     class concurrent_hash_map;
00075 
00077     namespace internal {
00078 
00079 
00081     typedef size_t hashcode_t;
00083     struct hash_map_node_base : tbb::internal::no_copy {
00085         typedef spin_rw_mutex mutex_t;
00087         typedef mutex_t::scoped_lock scoped_t;
00089         hash_map_node_base *next;
00090         mutex_t mutex;
00091     };
00093     static hash_map_node_base *const rehash_req = reinterpret_cast<hash_map_node_base*>(size_t(3));
00095     static hash_map_node_base *const empty_rehashed = reinterpret_cast<hash_map_node_base*>(size_t(0));
00097     class hash_map_base {
00098     public:
00100         typedef size_t size_type;
00102         typedef size_t hashcode_t;
00104         typedef size_t segment_index_t;
00106         typedef hash_map_node_base node_base;
00108         struct bucket : tbb::internal::no_copy {
00110             typedef spin_rw_mutex mutex_t;
00112             typedef mutex_t::scoped_lock scoped_t;
00113             mutex_t mutex;
00114             node_base *node_list;
00115         };
00117         static size_type const embedded_block = 1;
00119         static size_type const embedded_buckets = 1<<embedded_block;
00121         static size_type const first_block = 8; //including embedded_block. perfect with bucket size 16, so the allocations are power of 4096
00123         static size_type const pointers_per_table = sizeof(segment_index_t) * 8; // one segment per bit
00125         typedef bucket *segment_ptr_t;
00127         typedef segment_ptr_t segments_table_t[pointers_per_table];
00129         atomic<hashcode_t> my_mask;
00131         segments_table_t my_table;
00133         atomic<size_type> my_size; // It must be in separate cache line from my_mask due to performance effects
00135         bucket my_embedded_segment[embedded_buckets];
00136 
00138         hash_map_base() {
00139             std::memset( this, 0, pointers_per_table*sizeof(segment_ptr_t) // 32*4=128   or 64*8=512
00140                 + sizeof(my_size) + sizeof(my_mask)  // 4+4 or 8+8
00141                 + embedded_buckets*sizeof(bucket) ); // n*8 or n*16
00142             for( size_type i = 0; i < embedded_block; i++ ) // fill the table
00143                 my_table[i] = my_embedded_segment + segment_base(i);
00144             my_mask = embedded_buckets - 1;
00145             __TBB_ASSERT( embedded_block <= first_block, "The first block number must include embedded blocks");
00146         }
00147 
00149         static segment_index_t segment_index_of( size_type index ) {
00150             return segment_index_t( __TBB_Log2( index|1 ) );
00151         }
00152 
00154         static segment_index_t segment_base( segment_index_t k ) {
00155             return (segment_index_t(1)<<k & ~segment_index_t(1));
00156         }
00157 
00159         static size_type segment_size( segment_index_t k ) {
00160             return size_type(1)<<k; // fake value for k==0
00161         }
00162         
00164         static bool is_valid( void *ptr ) {
00165             return reinterpret_cast<size_t>(ptr) > size_t(63);
00166         }
00167 
00169         static void init_buckets( segment_ptr_t ptr, size_type sz, bool is_initial ) {
00170             if( is_initial ) std::memset(ptr, 0, sz*sizeof(bucket) );
00171             else for(size_type i = 0; i < sz; i++, ptr++) {
00172                     *reinterpret_cast<intptr_t*>(&ptr->mutex) = 0;
00173                     ptr->node_list = rehash_req;
00174                 }
00175         }
00176         
00178         static void add_to_bucket( bucket *b, node_base *n ) {
00179             __TBB_ASSERT(b->node_list != rehash_req, NULL);
00180             n->next = b->node_list;
00181             b->node_list = n; // its under lock and flag is set
00182         }
00183 
00185         struct enable_segment_failsafe {
00186             segment_ptr_t *my_segment_ptr;
00187             enable_segment_failsafe(segments_table_t &table, segment_index_t k) : my_segment_ptr(&table[k]) {}
00188             ~enable_segment_failsafe() {
00189                 if( my_segment_ptr ) *my_segment_ptr = 0; // indicate no allocation in progress
00190             }
00191         };
00192 
00194         void enable_segment( segment_index_t k, bool is_initial = false ) {
00195             __TBB_ASSERT( k, "Zero segment must be embedded" );
00196             enable_segment_failsafe watchdog( my_table, k );
00197             cache_aligned_allocator<bucket> alloc;
00198             size_type sz;
00199             __TBB_ASSERT( !is_valid(my_table[k]), "Wrong concurrent assignment");
00200             if( k >= first_block ) {
00201                 sz = segment_size( k );
00095     static hash_map_node_base *const empty_rehashed = reinterpret_cast<hash_map_node_base*>(size_t(0));
00097     class hash_map_base {
00098     public:
00100         typedef size_t size_type;
00102         typedef size_t hashcode_t;
00104         typedef size_t segment_index_t;
00106         typedef hash_map_node_base node_base;
00108         struct bucket : tbb::internal::no_copy {
00110             typedef spin_rw_mutex mutex_t;
00112             typedef mutex_t::scoped_lock scoped_t;
00113             mutex_t mutex;
00114             node_base *node_list;
00115         };
00117         static size_type const embedded_block = 1;
00119         static size_type const embedded_buckets = 1<<embedded_block;
00121         static size_type const first_block = 8; //including embedded_block. perfect with bucket size 16, so the allocations are power of 4096
00123         static size_type const pointers_per_table = sizeof(segment_index_t) * 8; // one segment per bit
00125         typedef bucket *segment_ptr_t;
00127         typedef segment_ptr_t segments_table_t[pointers_per_table];
00129         atomic<hashcode_t> my_mask;
00131         segments_table_t my_table;
00133         atomic<size_type> my_size; // It must be in separate cache line from my_mask due to performance effects
00135         bucket my_embedded_segment[embedded_buckets];
00136 
00138         hash_map_base() {
00139             std::memset( this, 0, pointers_per_table*sizeof(segment_ptr_t) // 32*4=128   or 64*8=512
00140                 + sizeof(my_size) + sizeof(my_mask)  // 4+4 or 8+8
00141                 + embedded_buckets*sizeof(bucket) ); // n*8 or n*16
00142             for( size_type i = 0; i < embedded_block; i++ ) // fill the table
00143                 my_table[i] = my_embedded_segment + segment_base(i);
00144             my_mask = embedded_buckets - 1;
00145             __TBB_ASSERT( embedded_block <= first_block, "The first block number must include embedded blocks");
00146         }
00147 
00149         static segment_index_t segment_index_of( size_type index ) {
00150             return segment_index_t( __TBB_Log2( index|1 ) );
00151         }
00152 
00154         static segment_index_t segment_base( segment_index_t k ) {
00155             return (segment_index_t(1)<<k & ~segment_index_t(1));
00156         }
00157 
00159         static size_type segment_size( segment_index_t k ) {
00160             return size_type(1)<<k; // fake value for k==0
00161         }
00162         
00164         static bool is_valid( void *ptr ) {
00165             return reinterpret_cast<size_t>(ptr) > size_t(63);
00166         }
00167 
00169         static void init_buckets( segment_ptr_t ptr, size_type sz, bool is_initial ) {
00170             if( is_initial ) std::memset(ptr, 0, sz*sizeof(bucket) );
00171             else for(size_type i = 0; i < sz; i++, ptr++) {
00172                     *reinterpret_cast<intptr_t*>(&ptr->mutex) = 0;
00173                     ptr->node_list = rehash_req;
00174                 }
00175         }
00176         
00178         static void add_to_bucket( bucket *b, node_base *n ) {
00179             __TBB_ASSERT(b->node_list != rehash_req, NULL);
00180             n->next = b->node_list;
00181             b->node_list = n; // its under lock and flag is set
00182         }
00183 
00185         struct enable_segment_failsafe {
00186             segment_ptr_t *my_segment_ptr;
00187             enable_segment_failsafe(segments_table_t &table, segment_index_t k) : my_segment_ptr(&table[k]) {}
00188             ~enable_segment_failsafe() {
00189                 if( my_segment_ptr ) *my_segment_ptr = 0; // indicate no allocation in progress
00190             }
00191         };
00192 
00194         void enable_segment( segment_index_t k, bool is_initial = false ) {
00195             __TBB_ASSERT( k, "Zero segment must be embedded" );
00196             enable_segment_failsafe watchdog( my_table, k );
00197             cache_aligned_allocator<bucket> alloc;
00198             size_type sz;
00199             __TBB_ASSERT( !is_valid(my_table[k]), "Wrong concurrent assignment");
00200             if( k >= first_block ) {
00201                 sz = segment_size( k );
00095     static hash_map_node_base *const empty_rehashed = reinterpret_cast<hash_map_node_base*>(size_t(0));
00097     class hash_map_base {
00098     public:
00100         typedef size_t size_type;
00102         typedef size_t hashcode_t;
00104         typedef size_t segment_index_t;
00106         typedef hash_map_node_base node_base;
00108         struct bucket : tbb::internal::no_copy {
00110             typedef spin_rw_mutex mutex_t;
00112             typedef mutex_t::scoped_lock scoped_t;
00113             mutex_t mutex;
00114             node_base *node_list;
00115         };
00117         static size_type const embedded_block = 1;
00119         static size_type const embedded_buckets = 1<<embedded_block;
00121         static size_type const first_block = 8; //including embedded_block. perfect with bucket size 16, so the allocations are power of 4096
00123         static size_type const pointers_per_table = sizeof(segment_index_t) * 8; // one segment per bit
00125         typedef bucket *segment_ptr_t;
00127         typedef segment_ptr_t segments_table_t[pointers_per_table];
00129         atomic<hashcode_t> my_mask;
00131         segments_table_t my_table;
00133         atomic<size_type> my_size; // It must be in separate cache line from my_mask due to performance effects
00135         bucket my_embedded_segment[embedded_buckets];
00136 
00138         hash_map_base() {
00139             std::memset( this, 0, pointers_per_table*sizeof(segment_ptr_t) // 32*4=128   or 64*8=512
00140                 + sizeof(my_size) + sizeof(my_mask)  // 4+4 or 8+8
00141                 + embedded_buckets*sizeof(bucket) ); // n*8 or n*16
00142             for( size_type i = 0; i < embedded_block; i++ ) // fill the table
00143                 my_table[i] = my_embedded_segment + segment_base(i);
00144             my_mask = embedded_buckets - 1;
00145             __TBB_ASSERT( embedded_block <= first_block, "The first block number must include embedded blocks");
00146         }
00147 
00149         static segment_index_t segment_index_of( size_type index ) {
00150             return segment_index_t( __TBB_Log2( index|1 ) );
00151         }
00152 
00154         static segment_index_t segment_base( segment_index_t k ) {
00155             return (segment_index_t(1)<<k & ~segment_index_t(1));
00156         }
00157 
00159         static size_type segment_size( segment_index_t k ) {
00160             return size_type(1)<<k; // fake value for k==0
00161         }
00162         
00164         static bool is_valid( void *ptr ) {
00165             return reinterpret_cast<size_t>(ptr) > size_t(63);
00166         }
00167 
00169         static void init_buckets( segment_ptr_t ptr, size_type sz, bool is_initial ) {
00170             if( is_initial ) std::memset(ptr, 0, sz*sizeof(bucket) );
00171             else for(size_type i = 0; i < sz; i++, ptr++) {
00172                     *reinterpret_cast<intptr_t*>(&ptr->mutex) = 0;
00173                     ptr->node_list = rehash_req;
00174                 }
00175         }
00176         
00178         static void add_to_bucket( bucket *b, node_base *n ) {
00179             __TBB_ASSERT(b->node_list != rehash_req, NULL);
00180             n->next = b->node_list;
00181             b->node_list = n; // its under lock and flag is set
00182         }
00183 
00185         struct enable_segment_failsafe {
00186             segment_ptr_t *my_segment_ptr;
00187             enable_segment_failsafe(segments_table_t &table, segment_index_t k) : my_segment_ptr(&table[k]) {}
00188             ~enable_segment_failsafe() {
00189                 if( my_segment_ptr ) *my_segment_ptr = 0; // indicate no allocation in progress
00190             }
00191         };
00192 
00194         void enable_segment( segment_index_t k, bool is_initial = false ) {
00195             __TBB_ASSERT( k, "Zero segment must be embedded" );
00196             enable_segment_failsafe watchdog( my_table, k );
00197             cache_aligned_allocator<bucket> alloc;
00198             size_type sz;
00199             __TBB_ASSERT( !is_valid(my_table[k]), "Wrong concurrent assignment");
00200             if( k >= first_block ) {
00201                 sz = segment_size( k );
00095     static hash_map_node_base *const empty_rehashed = reinterpret_cast<hash_map_node_base*>(size_t(0));
00097     class hash_map_base {
00098     public:
00100         typedef size_t size_type;
00102         typedef size_t hashcode_t;
00104         typedef size_t segment_index_t;
00106         typedef hash_map_node_base node_base;
00108         struct bucket : tbb::internal::no_copy {
00110             typedef spin_rw_mutex mutex_t;
00112             typedef mutex_t::scoped_lock scoped_t;
00113             mutex_t mutex;
00114             node_base *node_list;
00115         };
00117         static size_type const embedded_block = 1;
00119         static size_type const embedded_buckets = 1<<embedded_block;
00121         static size_type const first_block = 8; //including embedded_block. perfect with bucket size 16, so the allocations are power of 4096
00123         static size_type const pointers_per_table = sizeof(segment_index_t) * 8; // one segment per bit
00125         typedef bucket *segment_ptr_t;
00127         typedef segment_ptr_t segments_table_t[pointers_per_table];
00129         atomic<hashcode_t> my_mask;
00131         segments_table_t my_table;
00133         atomic<size_type> my_size; // It must be in separate cache line from my_mask due to performance effects
00135         bucket my_embedded_segment[embedded_buckets];
00136 
00138         hash_map_base() {
00139             std::memset( this, 0, pointers_per_table*sizeof(segment_ptr_t) // 32*4=128   or 64*8=512
00140                 + sizeof(my_size) + sizeof(my_mask)  // 4+4 or 8+8
00141                 + embedded_buckets*sizeof(bucket) ); // n*8 or n*16
00142             for( size_type i = 0; i < embedded_block; i++ ) // fill the table
00143                 my_table[i] = my_embedded_segment + segment_base(i);
00144             my_mask = embedded_buckets - 1;
00145             __TBB_ASSERT( embedded_block <= first_block, "The first block number must include embedded blocks");
00146         }
00147 
00149         static segment_index_t segment_index_of( size_type index ) {
00150             return segment_index_t( __TBB_Log2( index|1 ) );
00151         }
00152 
00154         static segment_index_t segment_base( segment_index_t k ) {
00155             return (segment_index_t(1)<<k & ~segment_index_t(1));
00156         }
00157 
00159         static size_type segment_size( segment_index_t k ) {
00160             return size_type(1)<<k; // fake value for k==0
00161         }
00162         
00164         static bool is_valid( void *ptr ) {
00165             return reinterpret_cast<size_t>(ptr) > size_t(63);
00166         }
00167 
00169         static void init_buckets( segment_ptr_t ptr, size_type sz, bool is_initial ) {
00170             if( is_initial ) std::memset(ptr, 0, sz*sizeof(bucket) );
00171             else for(size_type i = 0; i < sz; i++, ptr++) {
00172                     *reinterpret_cast<intptr_t*>(&ptr->mutex) = 0;
00173                     ptr->node_list = rehash_req;
00174                 }
00175         }
00176         
00178         static void add_to_bucket( bucket *b, node_base *n ) {
00179             __TBB_ASSERT(b->node_list != rehash_req, NULL);
00180             n->next = b->node_list;
00181             b->node_list = n; // its under lock and flag is set
00182         }
00183 
00185         struct enable_segment_failsafe {
00186             segment_ptr_t *my_segment_ptr;
00187             enable_segment_failsafe(segments_table_t &table, segment_index_t k) : my_segment_ptr(&table[k]) {}
00188             ~enable_segment_failsafe() {
00189                 if( my_segment_ptr ) *my_segment_ptr = 0; // indicate no allocation in progress
00190             }
00191         };
00192 
00194         void enable_segment( segment_index_t k, bool is_initial = false ) {
00195             __TBB_ASSERT( k, "Zero segment must be embedded" );
00196             enable_segment_failsafe watchdog( my_table, k );
00197             cache_aligned_allocator<bucket> alloc;
00198             size_type sz;
00199             __TBB_ASSERT( !is_valid(my_table[k]), "Wrong concurrent assignment");
00200             if( k >= first_block ) {
00201                 sz = segment_size( k );
00095     static hash_map_node_base *const empty_rehashed = reinterpret_cast<hash_map_node_base*>(size_t(0));
00097     class hash_map_base {
00098     public:
00100         typedef size_t size_type;
00102         typedef size_t hashcode_t;
00104         typedef size_t segment_index_t;
00106         typedef hash_map_node_base node_base;
00108         struct bucket : tbb::internal::no_copy {
00110             typedef spin_rw_mutex mutex_t;
00112             typedef mutex_t::scoped_lock scoped_t;
00113             mutex_t mutex;
00114             node_base *node_list;
00115         };
00117         static size_type const embedded_block = 1;
00119         static size_type const embedded_buckets = 1<<embedded_block;
00121         static size_type const first_block = 8; //including embedded_block. perfect with bucket size 16, so the allocations are power of 4096
00123         static size_type const pointers_per_table = sizeof(segment_index_t) * 8; // one segment per bit
00125         typedef bucket *segment_ptr_t;
00127         typedef segment_ptr_t segments_table_t[pointers_per_table];
00129         atomic<hashcode_t> my_mask;
00131         segments_table_t my_table;
00133         atomic<size_type> my_size; // It must be in separate cache line from my_mask due to performance effects
00135         bucket my_embedded_segment[embedded_buckets];
00136 
00138         hash_map_base() {
00139             std::memset( this, 0, pointers_per_table*sizeof(segment_ptr_t) // 32*4=128   or 64*8=512
00140                 + sizeof(my_size) + sizeof(my_mask)  // 4+4 or 8+8
00141                 + embedded_buckets*sizeof(bucket) ); // n*8 or n*16
00142             for( size_type i = 0; i < embedded_block; i++ ) // fill the table
00143                 my_table[i] = my_embedded_segment + segment_base(i);
00144             my_mask = embedded_buckets - 1;
00145             __TBB_ASSERT( embedded_block <= first_block, "The first block number must include embedded blocks");
00146         }
00147 
00149         static segment_index_t segment_index_of( size_type index ) {
00150             return segment_index_t( __TBB_Log2( index|1 ) );
00151         }
00152 
00154         static segment_index_t segment_base( segment_index_t k ) {
00155             return (segment_index_t(1)<<k & ~segment_index_t(1));
00156         }
00157 
00159         static size_type segment_size( segment_index_t k ) {
00160             return size_type(1)<<k; // fake value for k==0
00161         }
00162         
00164         static bool is_valid( void *ptr ) {
00165             return reinterpret_cast<size_t>(ptr) > size_t(63);
00166         }
00167 
00169         static void init_buckets( segment_ptr_t ptr, size_type sz, bool is_initial ) {
00170             if( is_initial ) std::memset(ptr, 0, sz*sizeof(bucket) );
00171             else for(size_type i = 0; i < sz; i++, ptr++) {
00172                     *reinterpret_cast<intptr_t*>(&ptr->mutex) = 0;
00173                     ptr->node_list = rehash_req;
00174                 }
00175         }
00176         
00178         static void add_to_bucket( bucket *b, node_base *n ) {
00179             __TBB_ASSERT(b->node_list != rehash_req, NULL);
00180             n->next = b->node_list;
00181             b->node_list = n; // its under lock and flag is set
00182         }
00183 
00185         struct enable_segment_failsafe {
00186             segment_ptr_t *my_segment_ptr;
00187             enable_segment_failsafe(segments_table_t &table, segment_index_t k) : my_segment_ptr(&table[k]) {}
00188             ~enable_segment_failsafe() {
00189                 if( my_segment_ptr ) *my_segment_ptr = 0; // indicate no allocation in progress
00190             }
00191         };
00192 
00194         void enable_segment( segment_index_t k, bool is_initial = false ) {
00195             __TBB_ASSERT( k, "Zero segment must be embedded" );
00196             enable_segment_failsafe watchdog( my_table, k );
00197             cache_aligned_allocator<bucket> alloc;
00198             size_type sz;
00199             __TBB_ASSERT( !is_valid(my_table[k]), "Wrong concurrent assignment");
00200             if( k >= first_block ) {
00201                 sz = segment_size( k );
00095     static hash_map_node_base *const empty_rehashed = reinterpret_cast<hash_map_node_base*>(size_t(0));
00097     class hash_map_base {
00098     public:
00100         typedef size_t size_type;
00102         typedef size_t hashcode_t;
00104         typedef size_t segment_index_t;
00106         typedef hash_map_node_base node_base;
00108         struct bucket : tbb::internal::no_copy {
00110             typedef spin_rw_mutex mutex_t;
00112             typedef mutex_t::scoped_lock scoped_t;
00113             mutex_t mutex;
00114             node_base *node_list;
00115         };
00117         static size_type const embedded_block = 1;
00119         static size_type const embedded_buckets = 1<<embedded_block;
00121         static size_type const first_block = 8; //including embedded_block. perfect with bucket size 16, so the allocations are power of 4096
00123         static size_type const pointers_per_table = sizeof(segment_index_t) * 8; // one segment per bit
00125         typedef bucket *segment_ptr_t;
00127         typedef segment_ptr_t segments_table_t[pointers_per_table];
00129         atomic<hashcode_t> my_mask;
00131         segments_table_t my_table;
00133         atomic<size_type> my_size; // It must be in separate cache line from my_mask due to performance effects
00135         bucket my_embedded_segment[embedded_buckets];
00136 
00138         hash_map_base() {
00139             std::memset( this, 0, pointers_per_table*sizeof(segment_ptr_t) // 32*4=128   or 64*8=512
00140                 + sizeof(my_size) + sizeof(my_mask)  // 4+4 or 8+8
00141                 + embedded_buckets*sizeof(bucket) ); // n*8 or n*16
00142             for( size_type i = 0; i < embedded_block; i++ ) // fill the table
00143                 my_table[i] = my_embedded_segment + segment_base(i);
00144             my_mask = embedded_buckets - 1;
00145             __TBB_ASSERT( embedded_block <= first_block, "The first block number must include embedded blocks");
00146         }
00147 
00149         static segment_index_t segment_index_of( size_type index ) {
00150             return segment_index_t( __TBB_Log2( index|1 ) );
00151         }
00152 
00154         static segment_index_t segment_base( segment_index_t k ) {
00155             return (segment_index_t(1)<<k & ~segment_index_t(1));
00156         }
00157 
00159         static size_type segment_size( segment_index_t k ) {
00160             return size_type(1)<<k; // fake value for k==0
00161         }
00162         
00164         static bool is_valid( void *ptr ) {
00165             return reinterpret_cast<size_t>(ptr) > size_t(63);
00166         }
00167 
00169         static void init_buckets( segment_ptr_t ptr, size_type sz, bool is_initial ) {
00170             if( is_initial ) std::memset(ptr, 0, sz*sizeof(bucket) );
00171             else for(size_type i = 0; i < sz; i++, ptr++) {
00172                     *reinterpret_cast<intptr_t*>(&ptr->mutex) = 0;
00173                     ptr->node_list = rehash_req;
00174                 }
00175         }
00176         
00178         static void add_to_bucket( bucket *b, node_base *n ) {
00179             __TBB_ASSERT(b->node_list != rehash_req, NULL);
00180             n->next = b->node_list;
00181             b->node_list = n; // its under lock and flag is set
00182         }
00183 
00185         struct enable_segment_failsafe {
00186             segment_ptr_t *my_segment_ptr;
00187             enable_segment_failsafe(segments_table_t &table, segment_index_t k) : my_segment_ptr(&table[k]) {}
00188             ~enable_segment_failsafe() {
00189                 if( my_segment_ptr ) *my_segment_ptr = 0; // indicate no allocation in progress
00190             }
00191         };
00192 
00194         void enable_segment( segment_index_t k, bool is_initial = false ) {
00195             __TBB_ASSERT( k, "Zero segment must be embedded" );
00196             enable_segment_failsafe watchdog( my_table, k );
00197             cache_aligned_allocator<bucket> alloc;
00198             size_type sz;
00199             __TBB_ASSERT( !is_valid(my_table[k]), "Wrong concurrent assignment");
00200             if( k >= first_block ) {
00201                 sz = segment_size( k );
00095     static hash_map_node_base *const empty_rehashed = reinterpret_cast<hash_map_node_base*>(size_t(0));
00097     class hash_map_base {
00098     public:
00100         typedef size_t size_type;
00102         typedef size_t hashcode_t;
00104         typedef size_t segment_index_t;
00106         typedef hash_map_node_base node_base;
00108         struct bucket : tbb::internal::no_copy {
00110             typedef spin_rw_mutex mutex_t;
00112             typedef mutex_t::scoped_lock scoped_t;
00113             mutex_t mutex;
00114             node_base *node_list;
00115         };
00117         static size_type const embedded_block = 1;
00119         static size_type const embedded_buckets = 1<<embedded_block;
00121         static size_type const first_block = 8; //including embedded_block. perfect with bucket size 16, so the allocations are power of 4096
00123         static size_type const pointers_per_table = sizeof(segment_index_t) * 8; // one segment per bit
00125         typedef bucket *segment_ptr_t;
00127         typedef segment_ptr_t segments_table_t[pointers_per_table];
00129         atomic<hashcode_t> my_mask;
00131         segments_table_t my_table;
00133         atomic<size_type> my_size; // It must be in separate cache line from my_mask due to performance effects
00135         bucket my_embedded_segment[embedded_buckets];
00136 
00138         hash_map_base() {
00139             std::memset( this, 0, pointers_per_table*sizeof(segment_ptr_t) // 32*4=128   or 64*8=512
00140                 + sizeof(my_size) + sizeof(my_mask)  // 4+4 or 8+8
00141                 + embedded_buckets*sizeof(bucket) ); // n*8 or n*16
00142             for( size_type i = 0; i < embedded_block; i++ ) // fill the table
00143                 my_table[i] = my_embedded_segment + segment_base(i);
00144             my_mask = embedded_buckets - 1;
00145             __TBB_ASSERT( embedded_block <= first_block, "The first block number must include embedded blocks");
00146         }
00147 
00149         static segment_index_t segment_index_of( size_type index ) {
00150             return segment_index_t( __TBB_Log2( index|1 ) );
00151         }
00152 
00154         static segment_index_t segment_base( segment_index_t k ) {
00155             return (segment_index_t(1)<<k & ~segment_index_t(1));
00156         }
00157 
00159         static size_type segment_size( segment_index_t k ) {
00160             return size_type(1)<<k; // fake value for k==0
00161         }
00162         
00164         static bool is_valid( void *ptr ) {
00165             return reinterpret_cast<size_t>(ptr) > size_t(63);
00166         }
00167 
00169         static void init_buckets( segment_ptr_t ptr, size_type sz, bool is_initial ) {
00170             if( is_initial ) std::memset(ptr, 0, sz*sizeof(bucket) );
00171             else for(size_type i = 0; i < sz; i++, ptr++) {
00172                     *reinterpret_cast<intptr_t*>(&ptr->mutex) = 0;
00173                     ptr->node_list = rehash_req;
00174                 }
00175         }
00176         
00178         static void add_to_bucket( bucket *b, node_base *n ) {
00179             __TBB_ASSERT(b->node_list != rehash_req, NULL);
00180             n->next = b->node_list;
00181             b->node_list = n; // its under lock and flag is set
00182         }
00183 
00185         struct enable_segment_failsafe {
00186             segment_ptr_t *my_segment_ptr;
00187             enable_segment_failsafe(segments_table_t &table, segment_index_t k) : my_segment_ptr(&table[k]) {}
00188             ~enable_segment_failsafe() {
00189                 if( my_segment_ptr ) *my_segment_ptr = 0; // indicate no allocation in progress
00190             }
00191         };
00192 
00194         void enable_segment( segment_index_t k, bool is_initial = false ) {
00195             __TBB_ASSERT( k, "Zero segment must be embedded" );
00196             enable_segment_failsafe watchdog( my_table, k );
00197             cache_aligned_allocator<bucket> alloc;
00198             size_type sz;
00199             __TBB_ASSERT( !is_valid(my_table[k]), "Wrong concurrent assignment");
00200             if( k >= first_block ) {
00201                 sz = segment_size( k );
00095     static hash_map_node_base *const empty_rehashed = reinterpret_cast<hash_map_node_base*>(size_t(0));
00097     class hash_map_base {
00098     public:
00100         typedef size_t size_type;
00102         typedef size_t hashcode_t;
00104         typedef size_t segment_index_t;
00106         typedef hash_map_node_base node_base;
00108         struct bucket : tbb::internal::no_copy {
00110             typedef spin_rw_mutex mutex_t;
00112             typedef mutex_t::scoped_lock scoped_t;
00113             mutex_t mutex;
00114             node_base *node_list;
00115         };
00117         static size_type const embedded_block = 1;
00119         static size_type const embedded_buckets = 1<<embedded_block;
00121         static size_type const first_block = 8; //including embedded_block. perfect with bucket size 16, so the allocations are power of 4096
00123         static size_type const pointers_per_table = sizeof(segment_index_t) * 8; // one segment per bit
00125         typedef bucket *segment_ptr_t;
00127         typedef segment_ptr_t segments_table_t[pointers_per_table];
00129         atomic<hashcode_t> my_mask;
00131         segments_table_t my_table;
00133         atomic<size_type> my_size; // It must be in separate cache line from my_mask due to performance effects
00135         bucket my_embedded_segment[embedded_buckets];
00136 
00138         hash_map_base() {
00139             std::memset( this, 0, pointers_per_table*sizeof(segment_ptr_t) // 32*4=128   or 64*8=512
00140                 + sizeof(my_size) + sizeof(my_mask)  // 4+4 or 8+8
00141                 + embedded_buckets*sizeof(bucket) ); // n*8 or n*16
00142             for( size_type i = 0; i < embedded_block; i++ ) // fill the table
00143                 my_table[i] = my_embedded_segment + segment_base(i);
00144             my_mask = embedded_buckets - 1;
00145             __TBB_ASSERT( embedded_block <= first_block, "The first block number must include embedded blocks");
00146         }
00147 
00149         static segment_index_t segment_index_of( size_type index ) {
00150             return segment_index_t( __TBB_Log2( index|1 ) );
00151         }
00152 
00154         static segment_index_t segment_base( segment_index_t k ) {
00155             return (segment_index_t(1)<<k & ~segment_index_t(1));
00156         }
00157 
00159         static size_type segment_size( segment_index_t k ) {
00160             return size_type(1)<<k; // fake value for k==0
00161         }
00162         
00164         static bool is_valid( void *ptr ) {
00165             return reinterpret_cast<size_t>(ptr) > size_t(63);
00166         }
00167 
00169         static void init_buckets( segment_ptr_t ptr, size_type sz, bool is_initial ) {
00170             if( is_initial ) std::memset(ptr, 0, sz*sizeof(bucket) );
00171             else for(size_type i = 0; i < sz; i++, ptr++) {
00172                     *reinterpret_cast<intptr_t*>(&ptr->mutex) = 0;
00173                     ptr->node_list = rehash_req;
00174                 }
00175         }
00176         
00178         static void add_to_bucket( bucket *b, node_base *n ) {
00179             __TBB_ASSERT(b->node_list != rehash_req, NULL);
00180             n->next = b->node_list;
00181             b->node_list = n; // its under lock and flag is set
00182         }
00183 
00185         struct enable_segment_failsafe {
00186             segment_ptr_t *my_segment_ptr;
00187             enable_segment_failsafe(segments_table_t &table, segment_index_t k) : my_segment_ptr(&table[k]) {}
00188             ~enable_segment_failsafe() {
00189                 if( my_segment_ptr ) *my_segment_ptr = 0; // indicate no allocation in progress
00190             }
00191         };
00192 
00194         void enable_segment( segment_index_t k, bool is_initial = false ) {
00195             __TBB_ASSERT( k, "Zero segment must be embedded" );
00196             enable_segment_failsafe watchdog( my_table, k );
00197             cache_aligned_allocator<bucket> alloc;
00198             size_type sz;
00199             __TBB_ASSERT( !is_valid(my_table[k]), "Wrong concurrent assignment");
00200             if( k >= first_block ) {
00201                 sz = segment_size( k );
00095     static hash_map_node_base *const empty_rehashed = reinterpret_cast<hash_map_node_base*>(size_t(0));
00097     class hash_map_base {
00098     public:
00100         typedef size_t size_type;
00102         typedef size_t hashcode_t;
00104         typedef size_t segment_index_t;
00106         typedef hash_map_node_base node_base;
00108         struct bucket : tbb::internal::no_copy {
00110             typedef spin_rw_mutex mutex_t;
00112             typedef mutex_t::scoped_lock scoped_t;
00113             mutex_t mutex;
00114             node_base *node_list;
00115         };
00117         static size_type const embedded_block = 1;
00119         static size_type const embedded_buckets = 1<<embedded_block;
00121         static size_type const first_block = 8; //including embedded_block. perfect with bucket size 16, so the allocations are power of 4096
00123         static size_type const pointers_per_table = sizeof(segment_index_t) * 8; // one segment per bit
00125         typedef bucket *segment_ptr_t;
00127         typedef segment_ptr_t segments_table_t[pointers_per_table];
00129         atomic<hashcode_t> my_mask;
00131         segments_table_t my_table;
00133         atomic<size_type> my_size; // It must be in separate cache line from my_mask due to performance effects
00135         bucket my_embedded_segment[embedded_buckets];
00136 
00138         hash_map_base() {
00139             std::memset( this, 0, pointers_per_table*sizeof(segment_ptr_t) // 32*4=128   or 64*8=512
00140                 + sizeof(my_size) + sizeof(my_mask)  // 4+4 or 8+8
00141                 + embedded_buckets*sizeof(bucket) ); // n*8 or n*16
00142             for( size_type i = 0; i < embedded_block; i++ ) // fill the table
00143                 my_table[i] = my_embedded_segment + segment_base(i);
00144             my_mask = embedded_buckets - 1;
00145             __TBB_ASSERT( embedded_block <= first_block, "The first block number must include embedded blocks");
00146         }
00147 
00149         static segment_index_t segment_index_of( size_type index ) {
00150             return segment_index_t( __TBB_Log2( index|1 ) );
00151         }
00152 
00154         static segment_index_t segment_base( segment_index_t k ) {
00155             return (segment_index_t(1)<<k & ~segment_index_t(1));
00156         }
00157 
00159         static size_type segment_size( segment_index_t k ) {
00160             return size_type(1)<<k; // fake value for k==0
00161         }
00162         
00164         static bool is_valid( void *ptr ) {
00165             return reinterpret_cast<size_t>(ptr) > size_t(63);
00166         }
00167 
00169         static void init_buckets( segment_ptr_t ptr, size_type sz, bool is_initial ) {
00170             if( is_initial ) std::memset(ptr, 0, sz*sizeof(bucket) );
00171             else for(size_type i = 0; i < sz; i++, ptr++) {
00172                     *reinterpret_cast<intptr_t*>(&ptr->mutex) = 0;
00173                     ptr->node_list = rehash_req;
00174                 }
00175         }
00176         
00178         static void add_to_bucket( bucket *b, node_base *n ) {
00179             __TBB_ASSERT(b->node_list != rehash_req, NULL);
00180             n->next = b->node_list;
00181             b->node_list = n; // its under lock and flag is set
00182         }
00183 
00185         struct enable_segment_failsafe {
00186             segment_ptr_t *my_segment_ptr;
00187             enable_segment_failsafe(segments_table_t &table, segment_index_t k) : my_segment_ptr(&table[k]) {}
00188             ~enable_segment_failsafe() {
00189                 if( my_segment_ptr ) *my_segment_ptr = 0; // indicate no allocation in progress
00190             }
00191         };
00192 
00194         void enable_segment( segment_index_t k, bool is_initial = false ) {
00195             __TBB_ASSERT( k, "Zero segment must be embedded" );
00196             enable_segment_failsafe watchdog( my_table, k );
00197             cache_aligned_allocator<bucket> alloc;
00198             size_type sz;
00199             __TBB_ASSERT( !is_valid(my_table[k]), "Wrong concurrent assignment");
00200             if( k >= first_block ) {
00201                 sz = segment_size( k );
00095     static hash_map_node_base *const empty_rehashed = reinterpret_cast<hash_map_node_base*>(size_t(0));
00097     class hash_map_base {
00098     public:
00100         typedef size_t size_type;
00102         typedef size_t hashcode_t;
00104         typedef size_t segment_index_t;
00106         typedef hash_map_node_base node_base;
00108         struct bucket : tbb::internal::no_copy {
00110             typedef spin_rw_mutex mutex_t;
00112             typedef mutex_t::scoped_lock scoped_t;
00113             mutex_t mutex;
00114             node_base *node_list;
00115         };
00117         static size_type const embedded_block = 1;
00119         static size_type const embedded_buckets = 1<<embedded_block;
00121         static size_type const first_block = 8; //including embedded_block. perfect with bucket size 16, so the allocations are power of 4096
00123         static size_type const pointers_per_table = sizeof(segment_index_t) * 8; // one segment per bit
00125         typedef bucket *segment_ptr_t;
00127         typedef segment_ptr_t segments_table_t[pointers_per_table];
00129         atomic<hashcode_t> my_mask;
00131         segments_table_t my_table;
00133         atomic<size_type> my_size; // It must be in separate cache line from my_mask due to performance effects
00135         bucket my_embedded_segment[embedded_buckets];
00136 
00138         hash_map_base() {
00139             std::memset( this, 0, pointers_per_table*sizeof(segment_ptr_t) // 32*4=128   or 64*8=512
00140                 + sizeof(my_size) + sizeof(my_mask)  // 4+4 or 8+8
00141                 + embedded_buckets*sizeof(bucket) ); // n*8 or n*16
00142             for( size_type i = 0; i < embedded_block; i++ ) // fill the table
00143                 my_table[i] = my_embedded_segment + segment_base(i);
00144             my_mask = embedded_buckets - 1;
00145             __TBB_ASSERT( embedded_block <= first_block, "The first block number must include embedded blocks");
00146         }
00147 
00149         static segment_index_t segment_index_of( size_type index ) {
00150             return segment_index_t( __TBB_Log2( index|1 ) );
00151         }
00152 
00154         static segment_index_t segment_base( segment_index_t k ) {
00155             return (segment_index_t(1)<<k & ~segment_index_t(1));
00156         }
00157 
00159         static size_type segment_size( segment_index_t k ) {
00160             return size_type(1)<<k; // fake value for k==0
00161         }
00162         
00164         static bool is_valid( void *ptr ) {
00165             return reinterpret_cast<size_t>(ptr) > size_t(63);
00166         }
00167 
00169         static void init_buckets( segment_ptr_t ptr, size_type sz, bool is_initial ) {
00170             if( is_initial ) std::memset(ptr, 0, sz*sizeof(bucket) );
00171             else for(size_type i = 0; i < sz; i++, ptr++) {
00172                     *reinterpret_cast<intptr_t*>(&ptr->mutex) = 0;
00173                     ptr->node_list = rehash_req;
00174                 }
00175         }
00176         
00178         static void add_to_bucket( bucket *b, node_base *n ) {
00179             __TBB_ASSERT(b->node_list != rehash_req, NULL);
00180             n->next = b->node_list;
00181             b->node_list = n; // its under lock and flag is set
00182         }
00183 
00185         struct enable_segment_failsafe {
00186             segment_ptr_t *my_segment_ptr;
00187             enable_segment_failsafe(segments_table_t &table, segment_index_t k) : my_segment_ptr(&table[k]) {}
00188             ~enable_segment_failsafe() {
00189                 if( my_segment_ptr ) *my_segment_ptr = 0; // indicate no allocation in progress
00190             }
00191         };
00192 
00194         void enable_segment( segment_index_t k, bool is_initial = false ) {
00195             __TBB_ASSERT( k, "Zero segment must be embedded" );
00196             enable_segment_failsafe watchdog( my_table, k );
00197             cache_aligned_allocator<bucket> alloc;
00198             size_type sz;
00199             __TBB_ASSERT( !is_valid(my_table[k]), "Wrong concurrent assignment");
00200             if( k >= first_block ) {
00201                 sz = segment_size( k );
00095     static hash_map_node_base *const empty_rehashed = reinterpret_cast<hash_map_node_base*>(size_t(0));
00097     class hash_map_base {
00098     public:
00100         typedef size_t size_type;
00102         typedef size_t hashcode_t;
00104         typedef size_t segment_index_t;
00106         typedef hash_map_node_base node_base;
00108         struct bucket : tbb::internal::no_copy {
00110             typedef spin_rw_mutex mutex_t;
00112             typedef mutex_t::scoped_lock scoped_t;
00113             mutex_t mutex;
00114             node_base *node_list;
00115         };
00117         static size_type const embedded_block = 1;
00119         static size_type const embedded_buckets = 1<<embedded_block;
00121         static size_type const first_block = 8; //including embedded_block. perfect with bucket size 16, so the allocations are power of 4096
00123         static size_type const pointers_per_table = sizeof(segment_index_t) * 8; // one segment per bit
00125         typedef bucket *segment_ptr_t;
00127         typedef segment_ptr_t segments_table_t[pointers_per_table];
00129         atomic<hashcode_t> my_mask;
00131         segments_table_t my_table;
00133         atomic<size_type> my_size; // It must be in separate cache line from my_mask due to performance effects
00135         bucket my_embedded_segment[embedded_buckets];
00136 
00138         hash_map_base() {
00139             std::memset( this, 0, pointers_per_table*sizeof(segment_ptr_t) // 32*4=128   or 64*8=512
00140                 + sizeof(my_size) + sizeof(my_mask)  // 4+4 or 8+8
00141                 + embedded_buckets*sizeof(bucket) ); // n*8 or n*16
00142             for( size_type i = 0; i < embedded_block; i++ ) // fill the table
00143                 my_table[i] = my_embedded_segment + segment_base(i);
00144             my_mask = embedded_buckets - 1;
00145             __TBB_ASSERT( embedded_block <= first_block, "The first block number must include embedded blocks");
00146         }
00147 
00149         static segment_index_t segment_index_of( size_type index ) {
00150             return segment_index_t( __TBB_Log2( index|1 ) );
00151         }
00152 
00154         static segment_index_t segment_base( segment_index_t k ) {
00155             return (segment_index_t(1)<<k & ~segment_index_t(1));
00156         }
00157 
00159         static size_type segment_size( segment_index_t k ) {
00160             return size_type(1)<<k; // fake value for k==0
00161         }
00162         
00164         static bool is_valid( void *ptr ) {
00165             return reinterpret_cast<size_t>(ptr) > size_t(63);
00166         }
00167 
00169         static void init_buckets( segment_ptr_t ptr, size_type sz, bool is_initial ) {
00170             if( is_initial ) std::memset(ptr, 0, sz*sizeof(bucket) );
00171             else for(size_type i = 0; i < sz; i++, ptr++) {
00172                     *reinterpret_cast<intptr_t*>(&ptr->mutex) = 0;
00173                     ptr->node_list = rehash_req;
00174                 }
00175         }
00176         
00178         static void add_to_bucket( bucket *b, node_base *n ) {
00179             __TBB_ASSERT(b->node_list != rehash_req, NULL);
00180             n->next = b->node_list;
00181             b->node_list = n; // its under lock and flag is set
00182         }
00183 
00185         struct enable_segment_failsafe {
00186             segment_ptr_t *my_segment_ptr;
00187             enable_segment_failsafe(segments_table_t &table, segment_index_t k) : my_segment_ptr(&table[k]) {}
00188             ~enable_segment_failsafe() {
00189                 if( my_segment_ptr ) *my_segment_ptr = 0; // indicate no allocation in progress
00190             }
00191         };
00192 
00194         void enable_segment( segment_index_t k, bool is_initial = false ) {
00195             __TBB_ASSERT( k, "Zero segment must be embedded" );
00196             enable_segment_failsafe watchdog( my_table, k );
00197             cache_aligned_allocator<bucket> alloc;
00198             size_type sz;
00199             __TBB_ASSERT( !is_valid(my_table[k]), "Wrong concurrent assignment");
00200             if( k >= first_block ) {
00201                 sz = segment_size( k );
00095     static hash_map_node_base *const empty_rehashed = reinterpret_cast<hash_map_node_base*>(size_t(0));
00097     class hash_map_base {
00098     public:
00100         typedef size_t size_type;
00102         typedef size_t hashcode_t;
00104         typedef size_t segment_index_t;
00106         typedef hash_map_node_base node_base;
00108         struct bucket : tbb::internal::no_copy {
00110             typedef spin_rw_mutex mutex_t;
00112             typedef mutex_t::scoped_lock scoped_t;
00113             mutex_t mutex;
00114             node_base *node_list;
00115         };
00117         static size_type const embedded_block = 1;
00119         static size_type const embedded_buckets = 1<<embedded_block;
00121         static size_type const first_block = 8; //including embedded_block. perfect with bucket size 16, so the allocations are power of 4096
00123         static size_type const pointers_per_table = sizeof(segment_index_t) * 8; // one segment per bit
00125         typedef bucket *segment_ptr_t;
00127         typedef segment_ptr_t segments_table_t[pointers_per_table];
00129         atomic<hashcode_t> my_mask;
00131         segments_table_t my_table;
00133         atomic<size_type> my_size; // It must be in separate cache line from my_mask due to performance effects
00135         bucket my_embedded_segment[embedded_buckets];
00136 
00138         hash_map_base() {
00139             std::memset( this, 0, pointers_per_table*sizeof(segment_ptr_t) // 32*4=128   or 64*8=512
00140                 + sizeof(my_size) + sizeof(my_mask)  // 4+4 or 8+8
00141                 + embedded_buckets*sizeof(bucket) ); // n*8 or n*16
00142             for( size_type i = 0; i < embedded_block; i++ ) // fill the table
00143                 my_table[i] = my_embedded_segment + segment_base(i);
00144             my_mask = embedded_buckets - 1;
00145             __TBB_ASSERT( embedded_block <= first_block, "The first block number must include embedded blocks");
00146         }
00147 
00149         static segment_index_t segment_index_of( size_type index ) {
00150             return segment_index_t( __TBB_Log2( index|1 ) );
00151         }
00152 
00154         static segment_index_t segment_base( segment_index_t k ) {
00155             return (segment_index_t(1)<<k & ~segment_index_t(1));
00156         }
00157 
00159         static size_type segment_size( segment_index_t k ) {
00160             return size_type(1)<<k; // fake value for k==0
00161         }
00162         
00164         static bool is_valid( void *ptr ) {
00165             return reinterpret_cast<size_t>(ptr) > size_t(63);
00166         }
00167 
00169         static void init_buckets( segment_ptr_t ptr, size_type sz, bool is_initial ) {
00170             if( is_initial ) std::memset(ptr, 0, sz*sizeof(bucket) );
00171             else for(size_type i = 0; i < sz; i++, ptr++) {
00172                     *reinterpret_cast<intptr_t*>(&ptr->mutex) = 0;
00173                     ptr->node_list = rehash_req;
00174                 }
00175         }
00176         
00178         static void add_to_bucket( bucket *b, node_base *n ) {
00179             __TBB_ASSERT(b->node_list != rehash_req, NULL);
00180             n->next = b->node_list;
00181             b->node_list = n; // its under lock and flag is set
00182         }
00183 
00185         struct enable_segment_failsafe {
00186             segment_ptr_t *my_segment_ptr;
00187             enable_segment_failsafe(segments_table_t &table, segment_index_t k) : my_segment_ptr(&table[k]) {}
00188             ~enable_segment_failsafe() {
00189                 if( my_segment_ptr ) *my_segment_ptr = 0; // indicate no allocation in progress
00190             }
00191         };
00192 
00194         void enable_segment( segment_index_t k, bool is_initial = false ) {
00195             __TBB_ASSERT( k, "Zero segment must be embedded" );
00196             enable_segment_failsafe watchdog( my_table, k );
00197             cache_aligned_allocator<bucket> alloc;
00198             size_type sz;
00199             __TBB_ASSERT( !is_valid(my_table[k]), "Wrong concurrent assignment");
00200             if( k >= first_block ) {
00201                 sz = segment_size( k );
00095     static hash_map_node_base *const empty_rehashed = reinterpret_cast<hash_map_node_base*>(size_t(0));
00097     class hash_map_base {
00098     public:
00100         typedef size_t size_type;
00102         typedef size_t hashcode_t;
00104         typedef size_t segment_index_t;
00106         typedef hash_map_node_base node_base;
00108         struct bucket : tbb::internal::no_copy {
00110             typedef spin_rw_mutex mutex_t;
00112             typedef mutex_t::scoped_lock scoped_t;
00113             mutex_t mutex;
00114             node_base *node_list;
00115         };
00117         static size_type const embedded_block = 1;
00119         static size_type const embedded_buckets = 1<<embedded_block;
00121         static size_type const first_block = 8; //including embedded_block. perfect with bucket size 16, so the allocations are power of 4096
00123         static size_type const pointers_per_table = sizeof(segment_index_t) * 8; // one segment per bit
00125         typedef bucket *segment_ptr_t;
00127         typedef segment_ptr_t segments_table_t[pointers_per_table];
00129         atomic<hashcode_t> my_mask;
00131         segments_table_t my_table;
00133         atomic<size_type> my_size; // It must be in separate cache line from my_mask due to performance effects
00135         bucket my_embedded_segment[embedded_buckets];
00136 
00138         hash_map_base() {
00139             std::memset( this, 0, pointers_per_table*sizeof(segment_ptr_t) // 32*4=128   or 64*8=512
00140                 + sizeof(my_size) + sizeof(my_mask)  // 4+4 or 8+8
00141                 + embedded_buckets*sizeof(bucket) ); // n*8 or n*16
00142             for( size_type i = 0; i < embedded_block; i++ ) // fill the table
00143                 my_table[i] = my_embedded_segment + segment_base(i);
00144             my_mask = embedded_buckets - 1;
00145             __TBB_ASSERT( embedded_block <= first_block, "The first block number must include embedded blocks");
00146         }
00147 
00149         static segment_index_t segment_index_of( size_type index ) {
00150             return segment_index_t( __TBB_Log2( index|1 ) );
00151         }
00152 
00154         static segment_index_t segment_base( segment_index_t k ) {
00155             return (segment_index_t(1)<<k & ~segment_index_t(1));
00156         }
00157 
00159         static size_type segment_size( segment_index_t k ) {
00160             return size_type(1)<<k; // fake value for k==0
00161         }
00162         
00164         static bool is_valid( void *ptr ) {
00165             return reinterpret_cast<size_t>(ptr) > size_t(63);
00166         }
00167 
00169         static void init_buckets( segment_ptr_t ptr, size_type sz, bool is_initial ) {
00170             if( is_initial ) std::memset(ptr, 0, sz*sizeof(bucket) );
00171             else for(size_type i = 0; i < sz; i++, ptr++) {
00172                     *reinterpret_cast<intptr_t*>(&ptr->mutex) = 0;
00173                     ptr->node_list = rehash_req;
00174                 }
00175         }
00176         
00178         static void add_to_bucket( bucket *b, node_base *n ) {
00179             __TBB_ASSERT(b->node_list != rehash_req, NULL);
00180             n->next = b->node_list;
00181             b->node_list = n; // its under lock and flag is set
00182         }
00183 
00185         struct enable_segment_failsafe {
00186             segment_ptr_t *my_segment_ptr;
00187             enable_segment_failsafe(segments_table_t &table, segment_index_t k) : my_segment_ptr(&table[k]) {}
00188             ~enable_segment_failsafe() {
00189                 if( my_segment_ptr ) *my_segment_ptr = 0; // indicate no allocation in progress
00190             }
00191         };
00192 
00194         void enable_segment( segment_index_t k, bool is_initial = false ) {
00195             __TBB_ASSERT( k, "Zero segment must be embedded" );
00196             enable_segment_failsafe watchdog( my_table, k );
00197