concurrent_hash_map.h
Go to the documentation of this file.
/*
Copyright (c) 2005-2020 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef __TBB_concurrent_hash_map_H
#define __TBB_concurrent_hash_map_H
#define __TBB_concurrent_hash_map_H_include_area
#include "internal/_warning_suppress_enable_notice.h"
#include "tbb_stddef.h"
#include <iterator>
#include <utility> // Need std::pair
#include <cstring> // Need std::memset
#include __TBB_STD_SWAP_HEADER
#include "tbb_allocator.h"
#include "spin_rw_mutex.h"
#include "atomic.h"
#include "tbb_exception.h"
#include "tbb_profiling.h"
#include "aligned_space.h"
#include "internal/_tbb_hash_compare_impl.h"
#include "internal/_template_helpers.h"
#include "internal/_allocator_traits.h"
#if __TBB_INITIALIZER_LISTS_PRESENT
#include <initializer_list>
#endif
#if TBB_USE_PERFORMANCE_WARNINGS || __TBB_STATISTICS
#include <typeinfo>
#endif
#if __TBB_STATISTICS
#include <stdio.h>
#endif
#if __TBB_CPP11_RVALUE_REF_PRESENT && __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT && __TBB_CPP11_TUPLE_PRESENT
// Definition of __TBB_CPP11_RVALUE_REF_PRESENT includes __TBB_CPP11_TUPLE_PRESENT
// for most of platforms, tuple present macro was added for logical correctness
#include <tuple>
#endif
namespace tbb {
namespace interface5 {
template<typename Key, typename T, typename HashCompare = tbb_hash_compare<Key>, typename A = tbb_allocator<std::pair<const Key, T> > >
class concurrent_hash_map;
namespace internal {
using namespace tbb::internal;
typedef size_t hashcode_t;
struct hash_map_node_base : tbb::internal::no_copy {
typedef spin_rw_mutex mutex_t;
typedef mutex_t::scoped_lock scoped_t;
hash_map_node_base *next;
mutex_t mutex;
};
static hash_map_node_base *const rehash_req = reinterpret_cast<hash_map_node_base*>(size_t(3));
static hash_map_node_base *const empty_rehashed = reinterpret_cast<hash_map_node_base*>(size_t(0));
class hash_map_base {
public:
typedef size_t size_type;
typedef size_t hashcode_t;
typedef size_t segment_index_t;
typedef hash_map_node_base node_base;
struct bucket : tbb::internal::no_copy {
typedef spin_rw_mutex mutex_t;
typedef mutex_t::scoped_lock scoped_t;
mutex_t mutex;
node_base *node_list;
};
static size_type const embedded_block = 1;
static size_type const embedded_buckets = 1<<embedded_block;
static size_type const first_block = 8; //including embedded_block. perfect with bucket size 16, so the allocations are power of 4096
static size_type const pointers_per_table = sizeof(segment_index_t) * 8; // one segment per bit
typedef bucket *segment_ptr_t;
typedef segment_ptr_t segments_table_t[pointers_per_table];
atomic<hashcode_t> my_mask;
segments_table_t my_table;
atomic<size_type> my_size; // It must be in separate cache line from my_mask due to performance effects
bucket my_embedded_segment[embedded_buckets];
#if __TBB_STATISTICS
atomic<unsigned> my_info_resizes; // concurrent ones
mutable atomic<unsigned> my_info_restarts; // race collisions
atomic<unsigned> my_info_rehashes; // invocations of rehash_bucket
#endif
hash_map_base() {
std::memset(my_table, 0, sizeof(my_table));
my_mask = 0;
my_size = 0;
std::memset(my_embedded_segment, 0, sizeof(my_embedded_segment));
for( size_type i = 0; i < embedded_block; i++ ) // fill the table
my_table[i] = my_embedded_segment + segment_base(i);
my_mask = embedded_buckets - 1;
__TBB_ASSERT( embedded_block <= first_block, "The first block number must include embedded blocks");
#if __TBB_STATISTICS
my_info_resizes = 0; // concurrent ones
my_info_restarts = 0; // race collisions
my_info_rehashes = 0; // invocations of rehash_bucket
#endif
}
static segment_index_t segment_index_of( size_type index ) {
return segment_index_t( __TBB_Log2( index|1 ) );
}
static segment_index_t segment_base( segment_index_t k ) {
return (segment_index_t(1)<<k & ~segment_index_t(1));
}
static size_type segment_size( segment_index_t k ) {
return size_type(1)<<k; // fake value for k==0
}
static bool is_valid( void *ptr ) {
return reinterpret_cast<uintptr_t>(ptr) > uintptr_t(63);
}
static void init_buckets( segment_ptr_t ptr, size_type sz, bool is_initial ) {
if( is_initial ) std::memset( static_cast<void*>(ptr), 0, sz*sizeof(bucket) );
else for(size_type i = 0; i < sz; i++, ptr++) {
*reinterpret_cast<intptr_t*>(&ptr->mutex) = 0;
ptr->node_list = rehash_req;
}
}
static void add_to_bucket( bucket *b, node_base *n ) {
__TBB_ASSERT(b->node_list != rehash_req, NULL);
n->next = b->node_list;
b->node_list = n; // its under lock and flag is set
}
struct enable_segment_failsafe : tbb::internal::no_copy {
segment_ptr_t *my_segment_ptr;
enable_segment_failsafe(segments_table_t &table, segment_index_t k) : my_segment_ptr(&table[k]) {}
~enable_segment_failsafe() {
if( my_segment_ptr ) *my_segment_ptr = 0; // indicate no allocation in progress
}
};
template<typename Allocator>
void enable_segment( segment_index_t k, const Allocator& allocator, bool is_initial = false ) {
typedef typename tbb::internal::allocator_rebind<Allocator, bucket>::type bucket_allocator_type;
typedef tbb::internal::allocator_traits<bucket_allocator_type> bucket_allocator_traits;
bucket_allocator_type bucket_allocator(allocator);
__TBB_ASSERT( k, "Zero segment must be embedded" );
enable_segment_failsafe watchdog( my_table, k );
size_type sz;
__TBB_ASSERT( !is_valid(my_table[k]), "Wrong concurrent assignment");
if( k >= first_block ) {
sz = segment_size( k );
segment_ptr_t ptr = bucket_allocator_traits::allocate(bucket_allocator, sz);
init_buckets( ptr, sz, is_initial );
itt_hide_store_word( my_table[k], ptr );
sz <<= 1;// double it to get entire capacity of the container
} else { // the first block
__TBB_ASSERT( k == embedded_block, "Wrong segment index" );
sz = segment_size( first_block );
segment_ptr_t ptr = bucket_allocator_traits::allocate(bucket_allocator, sz - embedded_buckets);
init_buckets( ptr, sz - embedded_buckets, is_initial );
ptr -= segment_base(embedded_block);
for(segment_index_t i = embedded_block; i < first_block; i++) // calc the offsets
itt_hide_store_word( my_table[i], ptr + segment_base(i) );
}
itt_store_word_with_release( my_mask, sz-1 );
watchdog.my_segment_ptr = 0;
}
template<typename Allocator>
void delete_segment(segment_index_t s, const Allocator& allocator) {
typedef typename tbb::internal::allocator_rebind<Allocator, bucket>::type bucket_allocator_type;
typedef tbb::internal::allocator_traits<bucket_allocator_type> bucket_allocator_traits;
bucket_allocator_type bucket_allocator(allocator);
segment_ptr_t buckets_ptr = my_table[s];
size_type sz = segment_size( s ? s : 1 );
if( s >= first_block) // the first segment or the next
bucket_allocator_traits::deallocate(bucket_allocator, buckets_ptr, sz);
else if( s == embedded_block && embedded_block != first_block )
bucket_allocator_traits::deallocate(bucket_allocator, buckets_ptr,
segment_size(first_block) - embedded_buckets);
if( s >= embedded_block ) my_table[s] = 0;
}
bucket *get_bucket( hashcode_t h ) const throw() { // TODO: add throw() everywhere?
segment_index_t s = segment_index_of( h );
h -= segment_base(s);
segment_ptr_t seg = my_table[s];
__TBB_ASSERT( is_valid(seg), "hashcode must be cut by valid mask for allocated segments" );
return &seg[h];
}
// internal serial rehashing helper
void mark_rehashed_levels( hashcode_t h ) throw () {
segment_index_t s = segment_index_of( h );
while( segment_ptr_t seg = my_table[++s] )
if( seg[h].node_list == rehash_req ) {
seg[h].node_list = empty_rehashed;
mark_rehashed_levels( h + ((hashcode_t)1<<s) ); // optimized segment_base(s)
}
}
// Splitting into two functions should help inlining
inline bool check_mask_race( const hashcode_t h, hashcode_t &m ) const {
hashcode_t m_now, m_old = m;
m_now = (hashcode_t) itt_load_word_with_acquire( my_mask );
if( m_old != m_now )
return check_rehashing_collision( h, m_old, m = m_now );
return false;
}
bool check_rehashing_collision( const hashcode_t h, hashcode_t m_old, hashcode_t m ) const {
__TBB_ASSERT(m_old != m, NULL); // TODO?: m arg could be optimized out by passing h = h&m
if( (h & m_old) != (h & m) ) { // mask changed for this hashcode, rare event
// condition above proves that 'h' has some other bits set beside 'm_old'
// find next applicable mask after m_old //TODO: look at bsl instruction
for( ++m_old; !(h & m_old); m_old <<= 1 ) // at maximum few rounds depending on the first block size
;
m_old = (m_old<<1) - 1; // get full mask from a bit
__TBB_ASSERT((m_old&(m_old+1))==0 && m_old <= m, NULL);
// check whether it is rehashing/ed
if( itt_load_word_with_acquire(get_bucket(h & m_old)->node_list) != rehash_req )
{
#if __TBB_STATISTICS
my_info_restarts++; // race collisions
#endif
return true;
}
}
return false;
}
segment_index_t insert_new_node( bucket *b, node_base *n, hashcode_t mask ) {
size_type sz = ++my_size; // prefix form is to enforce allocation after the first item inserted
add_to_bucket( b, n );
// check load factor
if( sz >= mask ) { // TODO: add custom load_factor
segment_index_t new_seg = __TBB_Log2( mask+1 ); //optimized segment_index_of
__TBB_ASSERT( is_valid(my_table[new_seg-1]), "new allocations must not publish new mask until segment has allocated");
static const segment_ptr_t is_allocating = (segment_ptr_t)2;
if( !itt_hide_load_word(my_table[new_seg])
&& as_atomic(my_table[new_seg]).compare_and_swap(is_allocating, NULL) == NULL )
return new_seg; // The value must be processed
}
return 0;
}
template<typename Allocator>
void reserve(size_type buckets, const Allocator& allocator) {
if( !buckets-- ) return;
bool is_initial = !my_size;
for( size_type m = my_mask; buckets > m; m = my_mask )
enable_segment( segment_index_of( m+1 ), allocator, is_initial );
}
void internal_swap(hash_map_base &table) {
using std::swap;
swap(this->my_mask, table.my_mask);
swap(this->my_size, table.my_size);
for(size_type i = 0; i < embedded_buckets; i++)
swap(this->my_embedded_segment[i].node_list, table.my_embedded_segment[i].node_list);
for(size_type i = embedded_block; i < pointers_per_table; i++)
swap(this->my_table[i], table.my_table[i]);
}
#if __TBB_CPP11_RVALUE_REF_PRESENT
void internal_move(hash_map_base&& other) {
my_mask = other.my_mask;
other.my_mask = embedded_buckets - 1;
my_size = other.my_size;
other.my_size = 0;
for(size_type i = 0; i < embedded_buckets; ++i) {
my_embedded_segment[i].node_list = other.my_embedded_segment[i].node_list;
other.my_embedded_segment[i].node_list = NULL;
}
for(size_type i = embedded_block; i < pointers_per_table; ++i) {
my_table[i] = other.my_table[i];
other.my_table[i] = NULL;
}
}
#endif // __TBB_CPP11_RVALUE_REF_PRESENT
};
template<typename Iterator>
class hash_map_range;
template<typename Container, typename Value>
class hash_map_iterator
: public std::iterator<std::forward_iterator_tag,Value>
{
typedef Container map_type;
typedef typename Container::node node;
typedef hash_map_base::node_base node_base;
typedef hash_map_base::bucket bucket;
template<typename C, typename T, typename U>
friend bool operator==( const hash_map_iterator<C,T>& i, const hash_map_iterator<C,U>& j );
template<typename C, typename T, typename U>
friend bool operator!=( const hash_map_iterator<C,T>& i, const hash_map_iterator<C,U>& j );
template<typename C, typename T, typename U>
friend ptrdiff_t operator-( const hash_map_iterator<C,T>& i, const hash_map_iterator<C,U>& j );
template<typename C, typename U>
friend class hash_map_iterator;
template<typename I>
friend class hash_map_range;
void advance_to_next_bucket() { // TODO?: refactor to iterator_base class
size_t k = my_index+1;
__TBB_ASSERT( my_bucket, "advancing an invalid iterator?");
while( k <= my_map->my_mask ) {
// Following test uses 2's-complement wizardry
if( k&(k-2) ) // not the beginning of a segment
++my_bucket;
else my_bucket = my_map->get_bucket( k );
my_node = static_cast<node*>( my_bucket->node_list );
if( hash_map_base::is_valid(my_node) ) {
my_index = k; return;
}
++k;
}
my_bucket = 0; my_node = 0; my_index = k; // the end
}
#if !defined(_MSC_VER) || defined(__INTEL_COMPILER)
template<typename Key, typename T, typename HashCompare, typename A>
friend class interface5::concurrent_hash_map;
#else
public: // workaround
#endif
const Container *my_map;
size_t my_index;
const bucket *my_bucket;
node *my_node;
hash_map_iterator( const Container &map, size_t index, const bucket *b, node_base *n );
public:
hash_map_iterator(): my_map(), my_index(), my_bucket(), my_node() {}
hash_map_iterator( const hash_map_iterator<Container,typename Container::value_type> &other ) :
my_map(other.my_map),
my_index(other.my_index),
my_bucket(other.my_bucket),
my_node(other.my_node)
{}
hash_map_iterator& operator=( const hash_map_iterator<Container,typename Container::value_type> &other ) {
my_map = other.my_map;
my_index = other.my_index;
my_bucket = other.my_bucket;
my_node = other.my_node;
return *this;
}
Value& operator*() const {
__TBB_ASSERT( hash_map_base::is_valid(my_node), "iterator uninitialized or at end of container?" );
return my_node->value();
}
Value* operator->() const {return &operator*();}
hash_map_iterator& operator++();
hash_map_iterator operator++(int) {
hash_map_iterator old(*this);
operator++();
return old;
}
};
template<typename Container, typename Value>
hash_map_iterator<Container,Value>::hash_map_iterator( const Container &map, size_t index, const bucket *b, node_base *n ) :
my_map(&map),
my_index(index),
my_bucket(b),
my_node( static_cast<node*>(n) )
{
if( b && !hash_map_base::is_valid(n) )
advance_to_next_bucket();
}
template<typename Container, typename Value>
hash_map_iterator<Container,Value>& hash_map_iterator<Container,Value>::operator++() {
my_node = static_cast<node*>( my_node->next );
if( !my_node ) advance_to_next_bucket();
return *this;
}
template<typename Container, typename T, typename U>
bool operator==( const hash_map_iterator<Container,T>& i, const hash_map_iterator<Container,U>& j ) {
return i.my_node == j.my_node && i.my_map == j.my_map;
}
template<typename Container, typename T, typename U>
bool operator!=( const hash_map_iterator<Container,T>& i, const hash_map_iterator<Container,U>& j ) {
return i.my_node != j.my_node || i.my_map != j.my_map;
}
template<typename Iterator>
class hash_map_range {
typedef typename Iterator::map_type map_type;
Iterator my_begin;
Iterator my_end;
mutable Iterator my_midpoint;
size_t my_grainsize;
void set_midpoint() const;
template<typename U> friend class hash_map_range;
public:
typedef std::size_t size_type;
typedef typename Iterator::value_type value_type;
typedef typename Iterator::reference reference;
typedef typename Iterator::difference_type difference_type;
typedef Iterator iterator;
bool empty() const {return my_begin==my_end;}
bool is_divisible() const {
return my_midpoint!=my_end;
}
hash_map_range( hash_map_range& r, split ) :
my_end(r.my_end),
my_grainsize(r.my_grainsize)
{
r.my_end = my_begin = r.my_midpoint;
__TBB_ASSERT( !empty(), "Splitting despite the range is not divisible" );
__TBB_ASSERT( !r.empty(), "Splitting despite the range is not divisible" );
set_midpoint();
r.set_midpoint();
}
template<typename U>
hash_map_range( hash_map_range<U>& r) :
my_begin(r.my_begin),
my_end(r.my_end),
my_midpoint(r.my_midpoint),
my_grainsize(r.my_grainsize)
{}
hash_map_range( const map_type &map, size_type grainsize_ = 1 ) :
my_begin( Iterator( map, 0, map.my_embedded_segment, map.my_embedded_segment->node_list ) ),
my_end( Iterator( map, map.my_mask + 1, 0, 0 ) ),
my_grainsize( grainsize_ )
{
__TBB_ASSERT( grainsize_>0, "grainsize must be positive" );
set_midpoint();
}
const Iterator& begin() const {return my_begin;}
const Iterator& end() const {return my_end;}
size_type grainsize() const {return my_grainsize;}
};
template<typename Iterator>
void hash_map_range<Iterator>::set_midpoint() const {
// Split by groups of nodes
size_t m = my_end.my_index-my_begin.my_index;
if( m > my_grainsize ) {
m = my_begin.my_index + m/2u;
hash_map_base::bucket *b = my_begin.my_map->get_bucket(m);
my_midpoint = Iterator(*my_begin.my_map,m,b,b->node_list);
} else {
my_midpoint = my_end;
}
__TBB_ASSERT( my_begin.my_index <= my_midpoint.my_index,
"my_begin is after my_midpoint" );
__TBB_ASSERT( my_midpoint.my_index <= my_end.my_index,
"my_midpoint is after my_end" );
__TBB_ASSERT( my_begin != my_midpoint || my_begin == my_end,
"[my_begin, my_midpoint) range should not be empty" );
}
} // internal
#if _MSC_VER && !defined(__INTEL_COMPILER)
// Suppress "conditional expression is constant" warning.
#pragma warning( push )
#pragma warning( disable: 4127 )
#endif
template<typename Key, typename T, typename HashCompare, typename Allocator>
class concurrent_hash_map : protected internal::hash_map_base {
template<typename Container, typename Value>
friend class internal::hash_map_iterator;
template<typename I>
friend class internal::hash_map_range;
public:
typedef Key key_type;
typedef T mapped_type;
typedef std::pair<const Key,T> value_type;
typedef hash_map_base::size_type size_type;
typedef ptrdiff_t difference_type;
typedef value_type *pointer;
typedef const value_type *const_pointer;
typedef value_type &reference;
typedef const value_type &const_reference;
typedef internal::hash_map_iterator<concurrent_hash_map,value_type> iterator;
typedef internal::hash_map_iterator<concurrent_hash_map,const value_type> const_iterator;
typedef internal::hash_map_range<iterator> range_type;
typedef internal::hash_map_range<const_iterator> const_range_type;
typedef Allocator allocator_type;
protected:
friend class const_accessor;
class node;
typedef typename tbb::internal::allocator_rebind<Allocator, node>::type node_allocator_type;
typedef tbb::internal::allocator_traits<node_allocator_type> node_allocator_traits;
node_allocator_type my_allocator;
HashCompare my_hash_compare;
class node : public node_base {
tbb::aligned_space<value_type> my_value;
public:
value_type* storage() { return my_value.begin(); }
value_type& value() { return *storage(); }
};
void delete_node( node_base *n ) {
node_allocator_traits::destroy(my_allocator, static_cast<node*>(n)->storage());
node_allocator_traits::destroy(my_allocator, static_cast<node*>(n));
node_allocator_traits::deallocate(my_allocator, static_cast<node*>(n), 1);
}
struct node_scoped_guard : tbb::internal::no_copy {
node* my_node;
node_allocator_type& my_alloc;
node_scoped_guard(node* n, node_allocator_type& alloc) : my_node(n), my_alloc(alloc) {}
~node_scoped_guard() {
if(my_node) {
node_allocator_traits::destroy(my_alloc, my_node);
node_allocator_traits::deallocate(my_alloc, my_node, 1);
}
}
void dismiss() { my_node = NULL; }
};
#if __TBB_CPP11_RVALUE_REF_PRESENT && __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
template<typename... Args>
static node* create_node(node_allocator_type& allocator, Args&&... args)
#else
template<typename Arg1, typename Arg2>
static node* create_node(node_allocator_type& allocator, __TBB_FORWARDING_REF(Arg1) arg1, __TBB_FORWARDING_REF(Arg2) arg2)
#endif
{
node* node_ptr = node_allocator_traits::allocate(allocator, 1);
node_scoped_guard guard(node_ptr, allocator);
node_allocator_traits::construct(allocator, node_ptr);
#if __TBB_CPP11_RVALUE_REF_PRESENT && __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
node_allocator_traits::construct(allocator, node_ptr->storage(), std::forward<Args>(args)...);
#else
node_allocator_traits::construct(allocator, node_ptr->storage(), tbb::internal::forward<Arg1>(arg1), tbb::internal::forward<Arg2>(arg2));
#endif
guard.dismiss();
return node_ptr;
}
static node* allocate_node_copy_construct(node_allocator_type& allocator, const Key &key, const T * t){
return create_node(allocator, key, *t);
}
#if __TBB_CPP11_RVALUE_REF_PRESENT
static node* allocate_node_move_construct(node_allocator_type& allocator, const Key &key, const T * t){
return create_node(allocator, key, std::move(*const_cast<T*>(t)));
}
#endif
static node* allocate_node_default_construct(node_allocator_type& allocator, const Key &key, const T * ){
#if __TBB_CPP11_RVALUE_REF_PRESENT && __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT && __TBB_CPP11_TUPLE_PRESENT
// Emplace construct an empty T object inside the pair
return create_node(allocator, std::piecewise_construct,
std::forward_as_tuple(key), std::forward_as_tuple());
#else
// Use of a temporary object is impossible, because create_node takes a non-const reference.
// copy-initialization is possible because T is already required to be CopyConstructible.
T obj = T();
return create_node(allocator, key, tbb::internal::move(obj));
#endif
}
static node* do_not_allocate_node(node_allocator_type& , const Key &, const T * ){
__TBB_ASSERT(false,"this dummy function should not be called");
return NULL;
}
node *search_bucket( const key_type &key, bucket *b ) const {
node *n = static_cast<node*>( b->node_list );
while( is_valid(n) && !my_hash_compare.equal(key, n->value().first) )
n = static_cast<node*>( n->next );
__TBB_ASSERT(n != internal::rehash_req, "Search can be executed only for rehashed bucket");
return n;
}
class bucket_accessor : public bucket::scoped_t {
bucket *my_b;
public:
bucket_accessor( concurrent_hash_map *base, const hashcode_t h, bool writer = false ) { acquire( base, h, writer ); }
inline void acquire( concurrent_hash_map *base, const hashcode_t h, bool writer = false ) {
my_b = base->get_bucket( h );
// TODO: actually, notification is unnecessary here, just hiding double-check
if( itt_load_word_with_acquire(my_b->node_list) == internal::rehash_req
&& try_acquire( my_b->mutex, /*write=*/true ) )
{
if( my_b->node_list == internal::rehash_req ) base->rehash_bucket( my_b, h ); //recursive rehashing
}
else bucket::scoped_t::acquire( my_b->mutex, writer );
__TBB_ASSERT( my_b->node_list != internal::rehash_req, NULL);
}
bool is_writer() { return bucket::scoped_t::is_writer; }
bucket *operator() () { return my_b; }
};
// TODO refactor to hash_base
void rehash_bucket( bucket *b_new, const hashcode_t h ) {
__TBB_ASSERT( *(intptr_t*)(&b_new->mutex), "b_new must be locked (for write)");
__TBB_ASSERT( h > 1, "The lowermost buckets can't be rehashed" );
__TBB_store_with_release(b_new->node_list, internal::empty_rehashed); // mark rehashed
hashcode_t mask = ( 1u<<__TBB_Log2( h ) ) - 1; // get parent mask from the topmost bit
#if __TBB_STATISTICS
my_info_rehashes++; // invocations of rehash_bucket
#endif
bucket_accessor b_old( this, h & mask );
mask = (mask<<1) | 1; // get full mask for new bucket
__TBB_ASSERT( (mask&(mask+1))==0 && (h & mask) == h, NULL );
restart:
for( node_base **p = &b_old()->node_list, *n = __TBB_load_with_acquire(*p); is_valid(n); n = *p ) {
hashcode_t c = my_hash_compare.hash( static_cast<node*>(n)->value().first );
#if TBB_USE_ASSERT
hashcode_t bmask = h & (mask>>1);
bmask = bmask==0? 1 : ( 1u<<(__TBB_Log2( bmask )+1 ) ) - 1; // minimal mask of parent bucket
__TBB_ASSERT( (c & bmask) == (h & bmask), "hash() function changed for key in table" );
#endif
if( (c & mask) == h ) {
if( !b_old.is_writer() )
if( !b_old.upgrade_to_writer() ) {
goto restart; // node ptr can be invalid due to concurrent erase
}
*p = n->next; // exclude from b_old
add_to_bucket( b_new, n );
} else p = &n->next; // iterate to next item
}
}
struct call_clear_on_leave {
concurrent_hash_map* my_ch_map;
call_clear_on_leave( concurrent_hash_map* a_ch_map ) : my_ch_map(a_ch_map) {}
void dismiss() {my_ch_map = 0;}
~call_clear_on_leave(){
if (my_ch_map){
my_ch_map->clear();
}
}
};
public:
class accessor;
class const_accessor : private node::scoped_t /*which derived from no_copy*/ {
friend class concurrent_hash_map<Key,T,HashCompare,Allocator>;
friend class accessor;
public:
typedef const typename concurrent_hash_map::value_type value_type;
bool empty() const { return !my_node; }
void release() {
if( my_node ) {
node::scoped_t::release();
my_node = 0;
}
}
const_reference operator*() const {
__TBB_ASSERT( my_node, "attempt to dereference empty accessor" );
return my_node->value();
}
const_pointer operator->() const {
return &operator*();
}
const_accessor() : my_node(NULL) {}
~const_accessor() {
my_node = NULL; // scoped lock's release() is called in its destructor
}
protected:
bool is_writer() { return node::scoped_t::is_writer; }
node *my_node;
hashcode_t my_hash;
};
class accessor: public const_accessor {
public:
typedef typename concurrent_hash_map::value_type value_type;
reference operator*() const {
__TBB_ASSERT( this->my_node, "attempt to dereference empty accessor" );
return this->my_node->value();
}
pointer operator->() const {
return &operator*();
}
};
explicit concurrent_hash_map( const allocator_type &a = allocator_type() )
: internal::hash_map_base(), my_allocator(a)
{}
explicit concurrent_hash_map( const HashCompare& compare, const allocator_type& a = allocator_type() )
: internal::hash_map_base(), my_allocator(a), my_hash_compare(compare)
{}
concurrent_hash_map( size_type n, const allocator_type &a = allocator_type() )
: internal::hash_map_base(), my_allocator(a)
{
reserve( n, my_allocator );
}
concurrent_hash_map( size_type n, const HashCompare& compare, const allocator_type& a = allocator_type() )
: internal::hash_map_base(), my_allocator(a), my_hash_compare(compare)
{
reserve( n, my_allocator );
}
concurrent_hash_map( const concurrent_hash_map &table )
: internal::hash_map_base(),
my_allocator(node_allocator_traits::select_on_container_copy_construction(table.get_allocator()))
{
call_clear_on_leave scope_guard(this);
internal_copy(table);
scope_guard.dismiss();
}
concurrent_hash_map( const concurrent_hash_map &table, const allocator_type &a)
: internal::hash_map_base(), my_allocator(a)
{
call_clear_on_leave scope_guard(this);
internal_copy(table);
scope_guard.dismiss();
}
#if __TBB_CPP11_RVALUE_REF_PRESENT
concurrent_hash_map( concurrent_hash_map &&table )
: internal::hash_map_base(), my_allocator(std::move(table.get_allocator()))
{
internal_move(std::move(table));
}
concurrent_hash_map( concurrent_hash_map &&table, const allocator_type &a )
: internal::hash_map_base(), my_allocator(a)
{
if (a == table.get_allocator()){
internal_move(std::move(table));
}else{
call_clear_on_leave scope_guard(this);
internal_copy(std::make_move_iterator(table.begin()), std::make_move_iterator(table.end()), table.size());
scope_guard.dismiss();
}
}
#endif //__TBB_CPP11_RVALUE_REF_PRESENT
template<typename I>
concurrent_hash_map( I first, I last, const allocator_type &a = allocator_type() )
: internal::hash_map_base(), my_allocator(a)
{
call_clear_on_leave scope_guard(this);
internal_copy(first, last, std::distance(first, last));
scope_guard.dismiss();
}
template<typename I>
concurrent_hash_map( I first, I last, const HashCompare& compare, const allocator_type& a = allocator_type() )
: internal::hash_map_base(), my_allocator(a), my_hash_compare(compare)
{
call_clear_on_leave scope_guard(this);
internal_copy(first, last, std::distance(first, last));
scope_guard.dismiss();
}
#if __TBB_INITIALIZER_LISTS_PRESENT
concurrent_hash_map( std::initializer_list<value_type> il, const allocator_type &a = allocator_type() )
: internal::hash_map_base(), my_allocator(a)
{
call_clear_on_leave scope_guard(this);
internal_copy(il.begin(), il.end(), il.size());
scope_guard.dismiss();
}
concurrent_hash_map( std::initializer_list<value_type> il, const HashCompare& compare, const allocator_type& a = allocator_type() )
: internal::hash_map_base(), my_allocator(a), my_hash_compare(compare)
{
call_clear_on_leave scope_guard(this);
internal_copy(il.begin(), il.end(), il.size());
scope_guard.dismiss();
}
#endif //__TBB_INITIALIZER_LISTS_PRESENT
concurrent_hash_map& operator=( const concurrent_hash_map &table ) {
if( this!=&table ) {
typedef typename node_allocator_traits::propagate_on_container_copy_assignment pocca_type;
clear();
tbb::internal::allocator_copy_assignment(my_allocator, table.my_allocator, pocca_type());
internal_copy(table);
}
return *this;
}
#if __TBB_CPP11_RVALUE_REF_PRESENT
concurrent_hash_map& operator=( concurrent_hash_map &&table ) {
if(this != &table) {
typedef typename node_allocator_traits::propagate_on_container_move_assignment pocma_type;
internal_move_assign(std::move(table), pocma_type());
}
return *this;
}
#endif //__TBB_CPP11_RVALUE_REF_PRESENT
#if __TBB_INITIALIZER_LISTS_PRESENT
concurrent_hash_map& operator=( std::initializer_list<value_type> il ) {
clear();
internal_copy(il.begin(), il.end(), il.size());
return *this;
}
#endif //__TBB_INITIALIZER_LISTS_PRESENT
void rehash(size_type n = 0);
void clear();
~concurrent_hash_map() { clear(); }
//------------------------------------------------------------------------
// Parallel algorithm support
//------------------------------------------------------------------------
range_type range( size_type grainsize=1 ) {
return range_type( *this, grainsize );
}
const_range_type range( size_type grainsize=1 ) const {
return const_range_type( *this, grainsize );
}
//------------------------------------------------------------------------
// STL support - not thread-safe methods
//------------------------------------------------------------------------
iterator begin() { return iterator( *this, 0, my_embedded_segment, my_embedded_segment->node_list ); }
iterator end() { return iterator( *this, 0, 0, 0 ); }
const_iterator begin() const { return const_iterator( *this, 0, my_embedded_segment, my_embedded_segment->node_list ); }
const_iterator end() const { return const_iterator( *this, 0, 0, 0 ); }
std::pair<iterator, iterator> equal_range( const Key& key ) { return internal_equal_range( key, end() ); }
std::pair<const_iterator, const_iterator> equal_range( const Key& key ) const { return internal_equal_range( key, end() ); }
size_type size() const { return my_size; }
bool empty() const { return my_size == 0; }
size_type max_size() const {return (~size_type(0))/sizeof(node);}
size_type bucket_count() const { return my_mask+1; }
allocator_type get_allocator() const { return this->my_allocator; }
void swap( concurrent_hash_map &table );
//------------------------------------------------------------------------
// concurrent map operations
//------------------------------------------------------------------------
size_type count( const Key &key ) const {
return const_cast<concurrent_hash_map*>(this)->lookup(/*insert*/false, key, NULL, NULL, /*write=*/false, &do_not_allocate_node );
}
bool find( const_accessor &result, const Key &key ) const {
result.release();
return const_cast<concurrent_hash_map*>(this)->lookup(/*insert*/false, key, NULL, &result, /*write=*/false, &do_not_allocate_node );
}
bool find( accessor &result, const Key &key ) {
result.release();
return lookup(/*insert*/false, key, NULL, &result, /*write=*/true, &do_not_allocate_node );
}
bool insert( const_accessor &result, const Key &key ) {
result.release();
return lookup(/*insert*/true, key, NULL, &result, /*write=*/false, &allocate_node_default_construct );
}
bool insert( accessor &result, const Key &key ) {
result.release();
return lookup(/*insert*/true, key, NULL, &result, /*write=*/true, &allocate_node_default_construct );
}
bool insert( const_accessor &result, const value_type &value ) {
result.release();
return lookup(/*insert*/true, value.first, &value.second, &result, /*write=*/false, &allocate_node_copy_construct );
}
bool insert( accessor &result, const value_type &value ) {
result.release();
return lookup(/*insert*/true, value.first, &value.second, &result, /*write=*/true, &allocate_node_copy_construct );
}
bool insert( const value_type &value ) {
return lookup(/*insert*/true, value.first, &value.second, NULL, /*write=*/false, &allocate_node_copy_construct );
}
#if __TBB_CPP11_RVALUE_REF_PRESENT
bool insert( const_accessor &result, value_type && value ) {
return generic_move_insert(result, std::move(value));
}
bool insert( accessor &result, value_type && value ) {
return generic_move_insert(result, std::move(value));
}
bool insert( value_type && value ) {
return generic_move_insert(accessor_not_used(), std::move(value));
}
#if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
template<typename... Args>
bool emplace( const_accessor &result, Args&&... args ) {
return generic_emplace(result, std::forward<Args>(args)...);
}
template<typename... Args>
bool emplace( accessor &result, Args&&... args ) {
return generic_emplace(result, std::forward<Args>(args)...);
}
template<typename... Args>
bool emplace( Args&&... args ) {
return generic_emplace(accessor_not_used(), std::forward<Args>(args)...);
}
#endif //__TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
#endif //__TBB_CPP11_RVALUE_REF_PRESENT
template<typename I>
void insert( I first, I last ) {
for ( ; first != last; ++first )
insert( *first );
}
#if __TBB_INITIALIZER_LISTS_PRESENT
void insert( std::initializer_list<value_type> il ) {
insert( il.begin(), il.end() );
}
#endif //__TBB_INITIALIZER_LISTS_PRESENT
bool erase( const Key& key );
bool erase( const_accessor& item_accessor ) {
return exclude( item_accessor );
}
bool erase( accessor& item_accessor ) {
return exclude( item_accessor );
}
protected:
bool lookup(bool op_insert, const Key &key, const T *t, const_accessor *result, bool write, node* (*allocate_node)(node_allocator_type& , const Key &, const T * ), node *tmp_n = 0 ) ;
struct accessor_not_used { void release(){}};
friend const_accessor* accessor_location( accessor_not_used const& ){ return NULL;}
friend const_accessor* accessor_location( const_accessor & a ) { return &a;}
friend bool is_write_access_needed( accessor const& ) { return true;}
friend bool is_write_access_needed( const_accessor const& ) { return false;}
friend bool is_write_access_needed( accessor_not_used const& ) { return false;}
#if __TBB_CPP11_RVALUE_REF_PRESENT
template<typename Accessor>
bool generic_move_insert( Accessor && result, value_type && value ) {
result.release();
return lookup(/*insert*/true, value.first, &value.second, accessor_location(result), is_write_access_needed(result), &allocate_node_move_construct );
}
#if __TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
template<typename Accessor, typename... Args>
bool generic_emplace( Accessor && result, Args &&... args ) {
result.release();
node * node_ptr = create_node(my_allocator, std::forward<Args>(args)...);
return lookup(/*insert*/true, node_ptr->value().first, NULL, accessor_location(result), is_write_access_needed(result), &do_not_allocate_node, node_ptr );
}
#endif //__TBB_CPP11_VARIADIC_TEMPLATES_PRESENT
#endif //__TBB_CPP11_RVALUE_REF_PRESENT
bool exclude( const_accessor &item_accessor );
template<typename I>
std::pair<I, I> internal_equal_range( const Key& key, I end ) const;
void internal_copy( const concurrent_hash_map& source );
template<typename I>
void internal_copy( I first, I last, size_type reserve_size );
#if __TBB_CPP11_RVALUE_REF_PRESENT
// A compile-time dispatch to allow move assignment of containers with non-movable value_type if POCMA is true_type
void internal_move_assign(concurrent_hash_map&& other, tbb::internal::traits_true_type) {
tbb::internal::allocator_move_assignment(my_allocator, other.my_allocator, tbb::internal::traits_true_type());
internal_move(std::move(other));
}
void internal_move_assign(concurrent_hash_map&& other, tbb::internal::traits_false_type) {
if (this->my_allocator == other.my_allocator) {
internal_move(std::move(other));
} else {
//do per element move
internal_copy(std::make_move_iterator(other.begin()), std::make_move_iterator(other.end()), other.size());
}
}
#endif
const_pointer internal_fast_find( const Key& key ) const {
hashcode_t h = my_hash_compare.hash( key );
hashcode_t m = (hashcode_t) itt_load_word_with_acquire( my_mask );
node *n;
restart:
__TBB_ASSERT((m&(m+1))==0, "data structure is invalid");
bucket *b = get_bucket( h & m );
// TODO: actually, notification is unnecessary here, just hiding double-check
if( itt_load_word_with_acquire(b->node_list) == internal::rehash_req )
{
bucket::scoped_t lock;
if( lock.try_acquire( b->mutex, /*write=*/true ) ) {
if( b->node_list == internal::rehash_req)
const_cast<concurrent_hash_map*>(this)->rehash_bucket( b, h & m ); //recursive rehashing
}
else lock.acquire( b->mutex, /*write=*/false );
__TBB_ASSERT(b->node_list!=internal::rehash_req,NULL);
}
n = search_bucket( key, b );
if( n )
return n->storage();
else if( check_mask_race( h, m ) )
goto restart;
return 0;
}
};
#if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
namespace internal {
using namespace tbb::internal;
template<template<typename...> typename Map, typename Key, typename T, typename... Args>
using hash_map_t = Map<
Key, T,
std::conditional_t< (sizeof...(Args)>0) && !is_allocator_v< pack_element_t<0, Args...> >,
pack_element_t<0, Args...>, tbb_hash_compare<Key> >,
std::conditional_t< (sizeof...(Args)>0) && is_allocator_v< pack_element_t<sizeof...(Args)-1, Args...> >,
pack_element_t<sizeof...(Args)-1, Args...>, tbb_allocator<std::pair<const Key, T> > >
>;
}
// Deduction guide for the constructor from two iterators and hash_compare/ allocator
template<typename I, typename... Args>
concurrent_hash_map(I, I, Args...)
-> internal::hash_map_t<concurrent_hash_map, internal::iterator_key_t<I>,internal::iterator_mapped_t<I>, Args...>;
// Deduction guide for the constructor from an initializer_list and hash_compare/ allocator
// Deduction guide for an initializer_list, hash_compare and allocator is implicit
template<typename Key, typename T, typename CompareOrAllocator>
concurrent_hash_map(std::initializer_list<std::pair<const Key, T>>, CompareOrAllocator)
-> internal::hash_map_t<concurrent_hash_map, Key, T, CompareOrAllocator>;
#endif /* __TBB_CPP17_DEDUCTION_GUIDES_PRESENT */
template<typename Key, typename T, typename HashCompare, typename A>
bool concurrent_hash_map<Key,T,HashCompare,A>::lookup( bool op_insert, const Key &key, const T *t, const_accessor *result, bool write, node* (*allocate_node)(node_allocator_type& , const Key&, const T*), node *tmp_n ) {
__TBB_ASSERT( !result || !result->my_node, NULL );
bool return_value;
hashcode_t const h = my_hash_compare.hash( key );
hashcode_t m = (hashcode_t) itt_load_word_with_acquire( my_mask );
segment_index_t grow_segment = 0;
node *n;
restart:
{//lock scope
__TBB_ASSERT((m&(m+1))==0, "data structure is invalid");
return_value = false;
// get bucket
bucket_accessor b( this, h & m );
// find a node
n = search_bucket( key, b() );
if( op_insert ) {
// [opt] insert a key
if( !n ) {
if( !tmp_n ) {
tmp_n = allocate_node(my_allocator, key, t);
}
if( !b.is_writer() && !b.upgrade_to_writer() ) { // TODO: improved insertion
// Rerun search_list, in case another thread inserted the item during the upgrade.
n = search_bucket( key, b() );
if( is_valid(n) ) { // unfortunately, it did
b.downgrade_to_reader();
goto exists;
}
}
if( check_mask_race(h, m) )
goto restart; // b.release() is done in ~b().
// insert and set flag to grow the container
grow_segment = insert_new_node( b(), n = tmp_n, m );
tmp_n = 0;
return_value = true;
}
} else { // find or count
if( !n ) {
if( check_mask_race( h, m ) )
goto restart; // b.release() is done in ~b(). TODO: replace by continue
return false;
}
return_value = true;
}
exists:
if( !result ) goto check_growth;
// TODO: the following seems as generic/regular operation
// acquire the item
if( !result->try_acquire( n->mutex, write ) ) {
for( tbb::internal::atomic_backoff backoff(true);; ) {
if( result->try_acquire( n->mutex, write ) ) break;
if( !backoff.bounded_pause() ) {
// the wait takes really long, restart the operation
b.release();
__TBB_ASSERT( !op_insert || !return_value, "Can't acquire new item in locked bucket?" );
__TBB_Yield();
m = (hashcode_t) itt_load_word_with_acquire( my_mask );
goto restart;
}
}
}
}//lock scope
result->my_node = n;
result->my_hash = h;
check_growth:
// [opt] grow the container
if( grow_segment ) {
#if __TBB_STATISTICS
my_info_resizes++; // concurrent ones
#endif
enable_segment( grow_segment, my_allocator );
}
if( tmp_n ) // if op_insert only
delete_node( tmp_n );
return return_value;
}
template<typename Key, typename T, typename HashCompare, typename A>
template<typename I>
std::pair<I, I> concurrent_hash_map<Key,T,HashCompare,A>::internal_equal_range( const Key& key, I end_ ) const {
hashcode_t h = my_hash_compare.hash( key );
hashcode_t m = my_mask;
__TBB_ASSERT((m&(m+1))==0, "data structure is invalid");
h &= m;
bucket *b = get_bucket( h );
while( b->node_list == internal::rehash_req ) {
m = ( 1u<<__TBB_Log2( h ) ) - 1; // get parent mask from the topmost bit
b = get_bucket( h &= m );
}
node *n = search_bucket( key, b );
if( !n )
return std::make_pair(end_, end_);
iterator lower(*this, h, b, n), upper(lower);
return std::make_pair(lower, ++upper);
}
template<typename Key, typename T, typename HashCompare, typename A>
bool concurrent_hash_map<Key,T,HashCompare,A>::exclude( const_accessor &item_accessor ) {
__TBB_ASSERT( item_accessor.my_node, NULL );
node_base *const n = item_accessor.my_node;
hashcode_t const h = item_accessor.my_hash;
hashcode_t m = (hashcode_t) itt_load_word_with_acquire( my_mask );
do {
// get bucket
bucket_accessor b( this, h & m, /*writer=*/true );
node_base **p = &b()->node_list;
while( *p && *p != n )
p = &(*p)->next;
if( !*p ) { // someone else was first
if( check_mask_race( h, m ) )
continue;
item_accessor.release();
return false;
}
__TBB_ASSERT( *p == n, NULL );
*p = n->next; // remove from container
my_size--;
break;
} while(true);
if( !item_accessor.is_writer() ) // need to get exclusive lock
item_accessor.upgrade_to_writer(); // return value means nothing here
item_accessor.release();
delete_node( n ); // Only one thread can delete it
return true;
}
template<typename Key, typename T, typename HashCompare, typename A>
bool concurrent_hash_map<Key,T,HashCompare,A>::erase( const Key &key ) {
node_base *n;
hashcode_t const h = my_hash_compare.hash( key );
hashcode_t m = (hashcode_t) itt_load_word_with_acquire( my_mask );
restart:
{//lock scope
// get bucket
bucket_accessor b( this, h & m );
search:
node_base **p = &b()->node_list;
n = *p;
while( is_valid(n) && !my_hash_compare.equal(key, static_cast<node*>(n)->value().first ) ) {
p = &n->next;
n = *p;
}
if( !n ) { // not found, but mask could be changed
if( check_mask_race( h, m ) )
goto restart;
return false;
}
else if( !b.is_writer() && !b.upgrade_to_writer() ) {
if( check_mask_race( h, m ) ) // contended upgrade, check mask
goto restart;
goto search;
}
*p = n->next;
my_size--;
}
{
typename node::scoped_t item_locker( n->mutex, /*write=*/true );
}
// note: there should be no threads pretending to acquire this mutex again, do not try to upgrade const_accessor!
delete_node( n ); // Only one thread can delete it due to write lock on the bucket
return true;
}
template<typename Key, typename T, typename HashCompare, typename A>
void concurrent_hash_map<Key,T,HashCompare,A>::swap(concurrent_hash_map<Key,T,HashCompare,A> &table) {
typedef typename node_allocator_traits::propagate_on_container_swap pocs_type;
if (this != &table && (pocs_type::value || my_allocator == table.my_allocator)) {
using std::swap;
tbb::internal::allocator_swap(this->my_allocator, table.my_allocator, pocs_type());
swap(this->my_hash_compare, table.my_hash_compare);
internal_swap(table);
}
}
template<typename Key, typename T, typename HashCompare, typename A>
void concurrent_hash_map<Key,T,HashCompare,A>::rehash(size_type sz) {
reserve( sz, my_allocator ); // TODO: add reduction of number of buckets as well
hashcode_t mask = my_mask;
hashcode_t b = (mask+1)>>1; // size or first index of the last segment
__TBB_ASSERT((b&(b-1))==0, NULL); // zero or power of 2
bucket *bp = get_bucket( b ); // only the last segment should be scanned for rehashing
for(; b <= mask; b++, bp++ ) {
node_base *n = bp->node_list;
__TBB_ASSERT( is_valid(n) || n == internal::empty_rehashed || n == internal::rehash_req, "Broken internal structure" );
__TBB_ASSERT( *reinterpret_cast<intptr_t*>(&bp->mutex) == 0, "concurrent or unexpectedly terminated operation during rehash() execution" );
if( n == internal::rehash_req ) { // rehash bucket, conditional because rehashing of a previous bucket may affect this one
hashcode_t h = b; bucket *b_old = bp;
do {
__TBB_ASSERT( h > 1, "The lowermost buckets can't be rehashed" );
hashcode_t m = ( 1u<<__TBB_Log2( h ) ) - 1; // get parent mask from the topmost bit
b_old = get_bucket( h &= m );
} while( b_old->node_list == internal::rehash_req );
// now h - is index of the root rehashed bucket b_old
mark_rehashed_levels( h ); // mark all non-rehashed children recursively across all segments
for( node_base **p = &b_old->node_list, *q = *p; is_valid(q); q = *p ) {
hashcode_t c = my_hash_compare.hash( static_cast<node*>(q)->value().first );
if( (c & mask) != h ) { // should be rehashed
*p = q->next; // exclude from b_old
bucket *b_new = get_bucket( c & mask );
__TBB_ASSERT( b_new->node_list != internal::rehash_req, "hash() function changed for key in table or internal error" );
add_to_bucket( b_new, q );
} else p = &q->next; // iterate to next item
}
}
}
#if TBB_USE_PERFORMANCE_WARNINGS
int current_size = int(my_size), buckets = int(mask)+1, empty_buckets = 0, overpopulated_buckets = 0; // usage statistics
static bool reported = false;
#endif
#if TBB_USE_ASSERT || TBB_USE_PERFORMANCE_WARNINGS
for( b = 0; b <= mask; b++ ) {// only last segment should be scanned for rehashing
if( b & (b-2) ) ++bp; // not the beginning of a segment
else bp = get_bucket( b );
node_base *n = bp->node_list;
__TBB_ASSERT( *reinterpret_cast<intptr_t*>(&bp->mutex) == 0, "concurrent or unexpectedly terminated operation during rehash() execution" );
__TBB_ASSERT( is_valid(n) || n == internal::empty_rehashed, "Broken internal structure" );
#if TBB_USE_PERFORMANCE_WARNINGS
if( n == internal::empty_rehashed ) empty_buckets++;
else if( n->next ) overpopulated_buckets++;
#endif
#if TBB_USE_ASSERT
for( ; is_valid(n); n = n->next ) {
hashcode_t h = my_hash_compare.hash( static_cast<node*>(n)->value().first ) & mask;
__TBB_ASSERT( h == b, "hash() function changed for key in table or internal error" );
}
#endif
}
#endif // TBB_USE_ASSERT || TBB_USE_PERFORMANCE_WARNINGS
#if TBB_USE_PERFORMANCE_WARNINGS
if( buckets > current_size) empty_buckets -= buckets - current_size;
else overpopulated_buckets -= current_size - buckets; // TODO: load_factor?
if( !reported && buckets >= 512 && ( 2*empty_buckets > current_size || 2*overpopulated_buckets > current_size ) ) {
tbb::internal::runtime_warning(
"Performance is not optimal because the hash function produces bad randomness in lower bits in %s.\nSize: %d Empties: %d Overlaps: %d",
#if __TBB_USE_OPTIONAL_RTTI
typeid(*this).name(),
#else
"concurrent_hash_map",
#endif
current_size, empty_buckets, overpopulated_buckets );
reported = true;
}
#endif
}
template<typename Key, typename T, typename HashCompare, typename A>
void concurrent_hash_map<Key,T,HashCompare,A>::clear() {
hashcode_t m = my_mask;
__TBB_ASSERT((m&(m+1))==0, "data structure is invalid");
#if TBB_USE_ASSERT || TBB_USE_PERFORMANCE_WARNINGS || __TBB_STATISTICS
#if TBB_USE_PERFORMANCE_WARNINGS || __TBB_STATISTICS
int current_size = int(my_size), buckets = int(m)+1, empty_buckets = 0, overpopulated_buckets = 0; // usage statistics
static bool reported = false;
#endif
bucket *bp = 0;
// check consistency
for( segment_index_t b = 0; b <= m; b++ ) {
if( b & (b-2) ) ++bp; // not the beginning of a segment
else bp = get_bucket( b );
node_base *n = bp->node_list;
__TBB_ASSERT( is_valid(n) || n == internal::empty_rehashed || n == internal::rehash_req, "Broken internal structure" );
__TBB_ASSERT( *reinterpret_cast<intptr_t*>(&bp->mutex) == 0, "concurrent or unexpectedly terminated operation during clear() execution" );
#if TBB_USE_PERFORMANCE_WARNINGS || __TBB_STATISTICS
if( n == internal::empty_rehashed ) empty_buckets++;
else if( n == internal::rehash_req ) buckets--;
else if( n->next ) overpopulated_buckets++;
#endif
#if __TBB_EXTRA_DEBUG
for(; is_valid(n); n = n->next ) {
hashcode_t h = my_hash_compare.hash( static_cast<node*>(n)->value().first );
h &= m;
__TBB_ASSERT( h == b || get_bucket(h)->node_list == internal::rehash_req, "hash() function changed for key in table or internal error" );
}
#endif
}
#if TBB_USE_PERFORMANCE_WARNINGS || __TBB_STATISTICS
#if __TBB_STATISTICS
printf( "items=%d buckets: capacity=%d rehashed=%d empty=%d overpopulated=%d"
" concurrent: resizes=%u rehashes=%u restarts=%u\n",
current_size, int(m+1), buckets, empty_buckets, overpopulated_buckets,
unsigned(my_info_resizes), unsigned(my_info_rehashes), unsigned(my_info_restarts) );
my_info_resizes = 0; // concurrent ones
my_info_restarts = 0; // race collisions
my_info_rehashes = 0; // invocations of rehash_bucket
#endif
if( buckets > current_size) empty_buckets -= buckets - current_size;
else overpopulated_buckets -= current_size - buckets; // TODO: load_factor?
if( !reported && buckets >= 512 && ( 2*empty_buckets > current_size || 2*overpopulated_buckets > current_size ) ) {
tbb::internal::runtime_warning(
"Performance is not optimal because the hash function produces bad randomness in lower bits in %s.\nSize: %d Empties: %d Overlaps: %d",
#if __TBB_USE_OPTIONAL_RTTI
typeid(*this).name(),
#else
"concurrent_hash_map",
#endif
current_size, empty_buckets, overpopulated_buckets );
reported = true;
}
#endif
#endif // TBB_USE_ASSERT || TBB_USE_PERFORMANCE_WARNINGS || __TBB_STATISTICS
my_size = 0;
segment_index_t s = segment_index_of( m );
__TBB_ASSERT( s+1 == pointers_per_table || !my_table[s+1], "wrong mask or concurrent grow" );
do {
__TBB_ASSERT( is_valid( my_table[s] ), "wrong mask or concurrent grow" );
segment_ptr_t buckets_ptr = my_table[s];
size_type sz = segment_size( s ? s : 1 );
for( segment_index_t i = 0; i < sz; i++ )
for( node_base *n = buckets_ptr[i].node_list; is_valid(n); n = buckets_ptr[i].node_list ) {
buckets_ptr[i].node_list = n->next;
delete_node( n );
}
delete_segment(s, my_allocator);
} while(s-- > 0);
my_mask = embedded_buckets - 1;
}
template<typename Key, typename T, typename HashCompare, typename A>
void concurrent_hash_map<Key,T,HashCompare,A>::internal_copy( const concurrent_hash_map& source ) {
hashcode_t mask = source.my_mask;
if( my_mask == mask ) { // optimized version
reserve( source.my_size, my_allocator ); // TODO: load_factor?
bucket *dst = 0, *src = 0;
bool rehash_required = false;
for( hashcode_t k = 0; k <= mask; k++ ) {
if( k & (k-2) ) ++dst,src++; // not the beginning of a segment
else { dst = get_bucket( k ); src = source.get_bucket( k ); }
__TBB_ASSERT( dst->node_list != internal::rehash_req, "Invalid bucket in destination table");
node *n = static_cast<node*>( src->node_list );
if( n == internal::rehash_req ) { // source is not rehashed, items are in previous buckets
rehash_required = true;
dst->node_list = internal::rehash_req;
} else for(; n; n = static_cast<node*>( n->next ) ) {
node* node_ptr = create_node(my_allocator, n->value().first, n->value().second);
add_to_bucket( dst, node_ptr);
++my_size; // TODO: replace by non-atomic op
}
}
if( rehash_required ) rehash();
} else internal_copy( source.begin(), source.end(), source.my_size );
}
template<typename Key, typename T, typename HashCompare, typename A>
template<typename I>
void concurrent_hash_map<Key,T,HashCompare,A>::internal_copy(I first, I last, size_type reserve_size) {
reserve( reserve_size, my_allocator ); // TODO: load_factor?
hashcode_t m = my_mask;
for(; first != last; ++first) {
hashcode_t h = my_hash_compare.hash( (*first).first );
bucket *b = get_bucket( h & m );
__TBB_ASSERT( b->node_list != internal::rehash_req, "Invalid bucket in destination table");
node* node_ptr = create_node(my_allocator, (*first).first, (*first).second);
add_to_bucket( b, node_ptr );
++my_size; // TODO: replace by non-atomic op
}
}
} // namespace interface5
using interface5::concurrent_hash_map;
template<typename Key, typename T, typename HashCompare, typename A1, typename A2>
inline bool operator==(const concurrent_hash_map<Key, T, HashCompare, A1> &a, const concurrent_hash_map<Key, T, HashCompare, A2> &b) {
if(a.size() != b.size()) return false;
typename concurrent_hash_map<Key, T, HashCompare, A1>::const_iterator i(a.begin()), i_end(a.end());
typename concurrent_hash_map<Key, T, HashCompare, A2>::const_iterator j, j_end(b.end());
for(; i != i_end; ++i) {
j = b.equal_range(i->first).first;
if( j == j_end || !(i->second == j->second) ) return false;
}
return true;
}
template<typename Key, typename T, typename HashCompare, typename A1, typename A2>
inline bool operator!=(const concurrent_hash_map<Key, T, HashCompare, A1> &a, const concurrent_hash_map<Key, T, HashCompare, A2> &b)
{ return !(a == b); }
template<typename Key, typename T, typename HashCompare, typename A>
inline void swap(concurrent_hash_map<Key, T, HashCompare, A> &a, concurrent_hash_map<Key, T, HashCompare, A> &b)
{ a.swap( b ); }
#if _MSC_VER && !defined(__INTEL_COMPILER)
#pragma warning( pop )
#endif // warning 4127 is back
} // namespace tbb
#include "internal/_warning_suppress_disable_notice.h"
#undef __TBB_concurrent_hash_map_H_include_area
#endif /* __TBB_concurrent_hash_map_H */
tbb::interface5::internal::hash_map_base::delete_segment
void delete_segment(segment_index_t s, const Allocator &allocator)
Definition: concurrent_hash_map.h:218
tbb::interface5::internal::hash_map_base::init_buckets
static void init_buckets(segment_ptr_t ptr, size_type sz, bool is_initial)
Initialize buckets.
Definition: concurrent_hash_map.h:164
tbb::interface5::internal::hash_map_base::my_size
atomic< size_type > my_size
Size of container in stored items.
Definition: concurrent_hash_map.h:118
tbb::interface5::internal::hash_map_base::enable_segment_failsafe::~enable_segment_failsafe
~enable_segment_failsafe()
Definition: concurrent_hash_map.h:183
tbb::interface5::concurrent_hash_map::bucket_accessor::bucket_accessor
bucket_accessor(concurrent_hash_map *base, const hashcode_t h, bool writer=false)
Definition: concurrent_hash_map.h:703
tbb::interface5::internal::hash_map_base::my_table
segments_table_t my_table
Segment pointers table. Also prevents false sharing between my_mask and my_size.
Definition: concurrent_hash_map.h:116
tbb::interface5::concurrent_hash_map::lookup
bool lookup(bool op_insert, const Key &key, const T *t, const_accessor *result, bool write, node *(*allocate_node)(node_allocator_type &, const Key &, const T *), node *tmp_n=0)
Insert or find item and optionally acquire a lock on the item.
Definition: concurrent_hash_map.h:1258
tbb::internal::allocator_traits::allocate
static pointer allocate(Alloc &a, size_type n)
Definition: _allocator_traits.h:102
tbb::interface5::internal::hash_map_base::bucket::scoped_t
mutex_t::scoped_lock scoped_t
Scoped lock type for mutex.
Definition: concurrent_hash_map.h:97
tbb::internal::__TBB_load_with_acquire
T __TBB_load_with_acquire(const volatile T &location)
Definition: tbb_machine.h:712
tbb::acquire
Acquire.
Definition: atomic.h:57
tbb::interface5::concurrent_hash_map::accessor::value_type
concurrent_hash_map::value_type value_type
Type of value.
Definition: concurrent_hash_map.h:815
tbb::interface5::internal::hash_map_base::internal_swap
void internal_swap(hash_map_base &table)
Swap hash_map_bases.
Definition: concurrent_hash_map.h:309
tbb::interface5::concurrent_hash_map::difference_type
ptrdiff_t difference_type
Definition: concurrent_hash_map.h:597
tbb::interface5::internal::hash_map_base::mark_rehashed_levels
void mark_rehashed_levels(hashcode_t h)
Definition: concurrent_hash_map.h:243
tbb::interface5::concurrent_hash_map::clear
void clear()
Clear table.
Definition: concurrent_hash_map.h:1505
tbb::interface5::concurrent_hash_map::do_not_allocate_node
static node * do_not_allocate_node(node_allocator_type &, const Key &, const T *)
Definition: concurrent_hash_map.h:686
tbb::interface5::internal::hash_map_iterator::operator=
hash_map_iterator & operator=(const hash_map_iterator< Container, typename Container::value_type > &other)
Definition: concurrent_hash_map.h:415
internal
Definition: _flow_graph_async_msg_impl.h:24
tbb::interface5::concurrent_hash_map::end
const_iterator end() const
Definition: concurrent_hash_map.h:989
tbb::interface5::internal::hash_map_base::check_mask_race
bool check_mask_race(const hashcode_t h, hashcode_t &m) const
Check for mask race.
Definition: concurrent_hash_map.h:254
tbb::interface5::concurrent_hash_map::insert
bool insert(accessor &result, const value_type &value)
Insert item by copying if there is no such key present already and acquire a write lock on the item.
Definition: concurrent_hash_map.h:1057
tbb::interface5::concurrent_hash_map::const_accessor::my_hash
hashcode_t my_hash
Definition: concurrent_hash_map.h:808
tbb::interface5::internal::hash_map_range::difference_type
Iterator::difference_type difference_type
Definition: concurrent_hash_map.h:482
tbb::interface5::concurrent_hash_map::const_reference
const typedef value_type & const_reference
Definition: concurrent_hash_map.h:601
tbb::interface5::internal::hash_map_base
base class of concurrent_hash_map
Definition: concurrent_hash_map.h:82
tbb::interface5::internal::hash_map_node_base::next
hash_map_node_base * next
Next node in chain.
Definition: concurrent_hash_map.h:74
tbb::interface5::internal::hash_map_iterator::bucket
hash_map_base::bucket bucket
Definition: concurrent_hash_map.h:352
tbb::interface5::concurrent_hash_map::allocate_node_copy_construct
static node * allocate_node_copy_construct(node_allocator_type &allocator, const Key &key, const T *t)
Definition: concurrent_hash_map.h:663
tbb::interface5::internal::hash_map_range::iterator
Iterator iterator
Definition: concurrent_hash_map.h:483
tbb::internal::allocator_copy_assignment
void allocator_copy_assignment(MyAlloc &my_allocator, OtherAlloc &other_allocator, traits_true_type)
Definition: _allocator_traits.h:49
tbb::interface5::concurrent_hash_map::~concurrent_hash_map
~concurrent_hash_map()
Clear table and destroy it.
Definition: concurrent_hash_map.h:971
__TBB_ASSERT
#define __TBB_ASSERT(predicate, comment)
No-op version of __TBB_ASSERT.
Definition: tbb_stddef.h:165
tbb::interface5::internal::hash_map_base::segment_ptr_t
bucket * segment_ptr_t
Segment pointer.
Definition: concurrent_hash_map.h:110
__TBB_FORWARDING_REF
#define __TBB_FORWARDING_REF(A)
Definition: tbb_stddef.h:517
tbb::interface5::internal::hash_map_base::my_embedded_segment
bucket my_embedded_segment[embedded_buckets]
Zero segment.
Definition: concurrent_hash_map.h:120
tbb::internal::no_copy
Base class for types that should not be copied or assigned.
Definition: tbb_stddef.h:330
tbb::interface5::internal::hash_map_range::is_divisible
bool is_divisible() const
True if range can be partitioned into two subranges.
Definition: concurrent_hash_map.h:489
tbb::internal::allocator_traits::destroy
static void destroy(Alloc &, T *p)
Definition: _allocator_traits.h:133
tbb::interface5::concurrent_hash_map::node::storage
value_type * storage()
Definition: concurrent_hash_map.h:619
tbb::internal::itt_store_word_with_release
void itt_store_word_with_release(tbb::atomic< T > &dst, U src)
Definition: tbb_profiling.h:157
tbb::internal::itt_load_word_with_acquire
T itt_load_word_with_acquire(const tbb::atomic< T > &src)
Definition: tbb_profiling.h:168
tbb::internal::allocator_move_assignment
void allocator_move_assignment(MyAlloc &my_allocator, OtherAlloc &other_allocator, traits_true_type)
Definition: _allocator_traits.h:59
tbb::tbb_allocator
Meets "allocator" requirements of ISO C++ Standard, Section 20.1.5.
Definition: tbb_allocator.h:58
tbb::interface5::concurrent_hash_map::iterator
internal::hash_map_iterator< concurrent_hash_map, value_type > iterator
Definition: concurrent_hash_map.h:602
tbb::interface5::concurrent_hash_map::const_accessor::operator*
const_reference operator*() const
Return reference to associated value in hash table.
Definition: concurrent_hash_map.h:788
tbb::interface5::internal::hash_map_iterator::node_base
hash_map_base::node_base node_base
Definition: concurrent_hash_map.h:351
tbb::interface5::concurrent_hash_map::const_accessor
friend class const_accessor
Definition: concurrent_hash_map.h:609
tbb::interface5::concurrent_hash_map::insert
bool insert(accessor &result, const Key &key)
Insert item (if not already present) and acquire a write lock on the item.
Definition: concurrent_hash_map.h:1043
tbb::internal::atomic_backoff
Class that implements exponential backoff.
Definition: tbb_machine.h:348
tbb::interface5::internal::hash_map_iterator::my_bucket
const bucket * my_bucket
Pointer to bucket.
Definition: concurrent_hash_map.h:398
tbb::interface5::internal::hash_map_range::size_type
std::size_t size_type
Type for size of a range.
Definition: concurrent_hash_map.h:479
tbb::interface5::concurrent_hash_map::insert
bool insert(accessor &result, value_type &&value)
Insert item by copying if there is no such key present already and acquire a write lock on the item.
Definition: concurrent_hash_map.h:1077
tbb::interface5::concurrent_hash_map
Unordered map from Key to T.
Definition: concurrent_hash_map.h:58
tbb::interface5::internal::hash_map_node_base::scoped_t
mutex_t::scoped_lock scoped_t
Scoped lock type for mutex.
Definition: concurrent_hash_map.h:72
tbb::interface5::concurrent_hash_map::my_hash_compare
HashCompare my_hash_compare
Definition: concurrent_hash_map.h:614
tbb::interface5::internal::hash_map_base::insert_new_node
segment_index_t insert_new_node(bucket *b, node_base *n, hashcode_t mask)
Insert a node and check for load factor.
Definition: concurrent_hash_map.h:285
tbb::interface5::concurrent_hash_map::rehash
void rehash(size_type n=0)
Rehashes and optionally resizes the whole table.
Definition: concurrent_hash_map.h:1434
tbb::interface5::internal::hash_map_base::segment_base
static segment_index_t segment_base(segment_index_t k)
Definition: concurrent_hash_map.h:149
tbb::interface5::concurrent_hash_map::concurrent_hash_map
concurrent_hash_map(const concurrent_hash_map &table)
Copy constructor.
Definition: concurrent_hash_map.h:852
tbb::interface5::concurrent_hash_map::search_bucket
node * search_bucket(const key_type &key, bucket *b) const
Definition: concurrent_hash_map.h:691
tbb::interface5::concurrent_hash_map::const_accessor
Combines data access, locking, and garbage collection.
Definition: concurrent_hash_map.h:769
tbb::interface5::concurrent_hash_map::create_node
static node * create_node(node_allocator_type &allocator, Args &&... args)
Definition: concurrent_hash_map.h:645
tbb::interface5::concurrent_hash_map::insert
bool insert(const_accessor &result, value_type &&value)
Insert item by copying if there is no such key present already and acquire a read lock on the item.
Definition: concurrent_hash_map.h:1071
tbb
The graph class.
Definition: serial/tbb/parallel_for.h:46
tbb::interface5::internal::hash_map_range::my_midpoint
Iterator my_midpoint
Definition: concurrent_hash_map.h:472
tbb::interface5::internal::hash_map_base::internal_move
void internal_move(hash_map_base &&other)
Definition: concurrent_hash_map.h:320
tbb::interface5::concurrent_hash_map::begin
const_iterator begin() const
Definition: concurrent_hash_map.h:988
tbb::internal::allocator_swap
void allocator_swap(MyAlloc &my_allocator, OtherAlloc &other_allocator, traits_true_type)
Definition: _allocator_traits.h:69
tbb::interface5::concurrent_hash_map::exclude
bool exclude(const_accessor &item_accessor)
delete item by accessor
Definition: concurrent_hash_map.h:1356
tbb::interface5::internal::empty_rehashed
static hash_map_node_base *const empty_rehashed
Rehashed empty bucket flag.
Definition: concurrent_hash_map.h:80
tbb::internal::runtime_warning
void __TBB_EXPORTED_FUNC runtime_warning(const char *format,...)
Report a runtime warning.
Definition: tbb_assert_impl.h:85
tbb::interface5::internal::hash_map_iterator::operator->
Value * operator->() const
Definition: concurrent_hash_map.h:426
tbb::interface5::internal::hash_map_base::get_bucket
bucket * get_bucket(hashcode_t h) const
Get bucket by (masked) hashcode.
Definition: concurrent_hash_map.h:234
tbb::interface5::concurrent_hash_map::swap
void swap(concurrent_hash_map &table)
swap two instances. Iterators are invalidated
Definition: concurrent_hash_map.h:1423
spin_rw_mutex.h
tbb::operator!=
bool operator!=(const cache_aligned_allocator< T > &, const cache_aligned_allocator< U > &)
Definition: cache_aligned_allocator.h:132
tbb::internal::bool_constant
Definition: tbb_stddef.h:486
tbb::interface5::internal::hash_map_iterator::hash_map_iterator
hash_map_iterator(const hash_map_iterator< Container, typename Container::value_type > &other)
Definition: concurrent_hash_map.h:408
tbb::interface5::internal::hash_map_base::enable_segment
void enable_segment(segment_index_t k, const Allocator &allocator, bool is_initial=false)
Enable segment.
Definition: concurrent_hash_map.h:190
tbb::interface5::concurrent_hash_map::concurrent_hash_map
concurrent_hash_map(const allocator_type &a=allocator_type())
Construct empty table.
Definition: concurrent_hash_map.h:830
tbb::interface5::internal::hash_map_base::bucket::node_list
node_base * node_list
Definition: concurrent_hash_map.h:99
tbb::interface5::concurrent_hash_map::call_clear_on_leave::call_clear_on_leave
call_clear_on_leave(concurrent_hash_map *a_ch_map)
Definition: concurrent_hash_map.h:757
tbb::interface5::concurrent_hash_map::const_accessor::my_node
node * my_node
Definition: concurrent_hash_map.h:807
tbb::interface5::concurrent_hash_map::bucket_accessor
bucket accessor is to find, rehash, acquire a lock, and access a bucket
Definition: concurrent_hash_map.h:700
tbb::interface5::internal::hash_map_range::grainsize
size_type grainsize() const
The grain size for this range.
Definition: concurrent_hash_map.h:523
tbb::interface5::concurrent_hash_map::mapped_type
T mapped_type
Definition: concurrent_hash_map.h:594
tbb::interface5::internal::hash_map_base::reserve
void reserve(size_type buckets, const Allocator &allocator)
Prepare enough segments for number of buckets.
Definition: concurrent_hash_map.h:302
tbb::interface5::concurrent_hash_map::delete_node
void delete_node(node_base *n)
Definition: concurrent_hash_map.h:623
tbb::interface5::internal::hash_map_iterator
Meets requirements of a forward iterator for STL *‍/.
Definition: concurrent_hash_map.h:346
tbb::interface5::internal::hash_map_range::map_type
Iterator::map_type map_type
Definition: concurrent_hash_map.h:469
tbb::interface5::concurrent_hash_map::is_write_access_needed
friend bool is_write_access_needed(accessor_not_used const &)
Definition: concurrent_hash_map.h:1151
tbb::interface5::concurrent_hash_map::allocate_node_move_construct
static node * allocate_node_move_construct(node_allocator_type &allocator, const Key &key, const T *t)
Definition: concurrent_hash_map.h:668
tbb::interface5::concurrent_hash_map::allocator_type
Allocator allocator_type
Definition: concurrent_hash_map.h:606
tbb::interface5::concurrent_hash_map::end
iterator end()
Definition: concurrent_hash_map.h:987
tbb::interface5::internal::hash_map_range::my_end
Iterator my_end
Definition: concurrent_hash_map.h:471
tbb::spin_rw_mutex_v3
Fast, unfair, spinning reader-writer lock with backoff and writer-preference.
Definition: spin_rw_mutex.h:38
tbb::interface5::internal::hash_map_base::my_mask
atomic< hashcode_t > my_mask
Hash mask = sum of allocated segment sizes - 1.
Definition: concurrent_hash_map.h:114
tbb::interface5::internal::hash_map_range::my_begin
Iterator my_begin
Definition: concurrent_hash_map.h:470
tbb::interface5::concurrent_hash_map::call_clear_on_leave::dismiss
void dismiss()
Definition: concurrent_hash_map.h:758
tbb::interface5::internal::hash_map_base::segment_size
static size_type segment_size(segment_index_t k)
Definition: concurrent_hash_map.h:154
tbb_profiling.h
tbb::interface5::concurrent_hash_map::concurrent_hash_map
concurrent_hash_map(std::initializer_list< value_type > il, const HashCompare &compare, const allocator_type &a=allocator_type())
Definition: concurrent_hash_map.h:920
tbb::interface5::concurrent_hash_map::key_type
Key key_type
Definition: concurrent_hash_map.h:593
tbb::interface5::internal::hash_map_iterator::node
Container::node node
Definition: concurrent_hash_map.h:350
_template_helpers.h
tbb::internal::allocator_traits::construct
static void construct(Alloc &, PT *p)
Definition: _allocator_traits.h:111
tbb::operator-
tick_count::interval_t operator-(const tick_count &t1, const tick_count &t0)
Definition: tick_count.h:126
tbb::interface5::internal::operator!=
bool operator!=(const hash_map_iterator< Container, T > &i, const hash_map_iterator< Container, U > &j)
Definition: concurrent_hash_map.h:461
tbb::interface5::concurrent_hash_map::concurrent_hash_map
concurrent_hash_map(I first, I last, const allocator_type &a=allocator_type())
Construction with copying iteration range and given allocator instance.
Definition: concurrent_hash_map.h:893
tbb::interface5::concurrent_hash_map::internal_copy
void internal_copy(const concurrent_hash_map &source)
Copy "source" to *this, where *this must start out empty.
Definition: concurrent_hash_map.h:1577
tbb::interface5::internal::hash_map_iterator::my_node
node * my_node
Pointer to node that has current item.
Definition: concurrent_hash_map.h:401
tbb::interface5::internal::hash_map_base::enable_segment_failsafe
Exception safety helper.
Definition: concurrent_hash_map.h:180
_tbb_hash_compare_impl.h
aligned_space.h
tbb::interface5::concurrent_hash_map::accessor_location
friend const_accessor * accessor_location(accessor_not_used const &)
Definition: concurrent_hash_map.h:1146
tbb::interface5::concurrent_hash_map::const_accessor::~const_accessor
~const_accessor()
Destroy result after releasing the underlying reference.
Definition: concurrent_hash_map.h:802
tbb::interface5::internal::hash_map_iterator::operator++
hash_map_iterator operator++(int)
Post increment.
Definition: concurrent_hash_map.h:430
tbb::interface5::internal::hash_map_range::begin
const Iterator & begin() const
Definition: concurrent_hash_map.h:520
tbb::interface5::internal::hash_map_base::segment_index_of
static segment_index_t segment_index_of(size_type index)
Definition: concurrent_hash_map.h:144
tbb::interface5::concurrent_hash_map::erase
bool erase(accessor &item_accessor)
Erase item by accessor.
Definition: concurrent_hash_map.h:1137
tbb::interface5::concurrent_hash_map::const_iterator
internal::hash_map_iterator< concurrent_hash_map, const value_type > const_iterator
Definition: concurrent_hash_map.h:603
tbb::interface5::concurrent_hash_map::concurrent_hash_map
concurrent_hash_map(const HashCompare &compare, const allocator_type &a=allocator_type())
Definition: concurrent_hash_map.h:834
tbb::interface5::internal::hash_map_base::size_type
size_t size_type
Size type.
Definition: concurrent_hash_map.h:85
tbb::interface5::concurrent_hash_map::rehash_bucket
void rehash_bucket(bucket *b_new, const hashcode_t h)
Definition: concurrent_hash_map.h:723
tbb::interface5::concurrent_hash_map::get_allocator
allocator_type get_allocator() const
return allocator object
Definition: concurrent_hash_map.h:1006
tbb::interface5::internal::hash_map_node_base::mutex
mutex_t mutex
Definition: concurrent_hash_map.h:75
tbb::interface5::concurrent_hash_map::bucket_accessor::operator()
bucket * operator()()
get bucket pointer
Definition: concurrent_hash_map.h:719
tbb::interface5::concurrent_hash_map::insert
void insert(I first, I last)
Insert range [first, last)
Definition: concurrent_hash_map.h:1113
tbb::internal::allocator_traits::propagate_on_container_move_assignment
tbb::internal::false_type propagate_on_container_move_assignment
Definition: _allocator_traits.h:86
tbb::internal::allocator_rebind::type
allocator_traits< Alloc >::template rebind_alloc< T >::other type
Definition: _allocator_traits.h:149
tbb::interface5::concurrent_hash_map::is_write_access_needed
friend bool is_write_access_needed(const_accessor const &)
Definition: concurrent_hash_map.h:1150
tbb::interface5::concurrent_hash_map::accessor_not_used
Definition: concurrent_hash_map.h:1145
tbb::interface5::concurrent_hash_map::internal_equal_range
std::pair< I, I > internal_equal_range(const Key &key, I end) const
Returns an iterator for an item defined by the key, or for the next item after it (if upper==true)
Definition: concurrent_hash_map.h:1338
tbb::interface5::internal::hash_map_range::hash_map_range
hash_map_range(const map_type &map, size_type grainsize_=1)
Init range with container and grainsize specified.
Definition: concurrent_hash_map.h:512
tbb::interface5::concurrent_hash_map::const_accessor::value_type
const typedef concurrent_hash_map::value_type value_type
Type of value.
Definition: concurrent_hash_map.h:774
tbb::interface5::internal::hash_map_base::check_rehashing_collision
bool check_rehashing_collision(const hashcode_t h, hashcode_t m_old, hashcode_t m) const
Process mask race, check for rehashing collision.
Definition: concurrent_hash_map.h:263
tbb::operator==
bool operator==(const cache_aligned_allocator< T > &, const cache_aligned_allocator< U > &)
Definition: cache_aligned_allocator.h:129
tbb::interface5::internal::hash_map_iterator::hash_map_iterator
hash_map_iterator()
Construct undefined iterator.
Definition: concurrent_hash_map.h:407
tbb::interface5::internal::hash_map_base::node_base
hash_map_node_base node_base
Node base type.
Definition: concurrent_hash_map.h:91
tbb::interface5::concurrent_hash_map::const_accessor::operator->
const_pointer operator->() const
Return pointer to associated value in hash table.
Definition: concurrent_hash_map.h:794
tbb::interface5::concurrent_hash_map::my_allocator
node_allocator_type my_allocator
Definition: concurrent_hash_map.h:613
tbb_exception.h
int
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d void ITT_FORMAT p void ITT_FORMAT p __itt_model_site __itt_model_site_instance ITT_FORMAT p __itt_model_task __itt_model_task_instance ITT_FORMAT p void ITT_FORMAT p void ITT_FORMAT p void size_t ITT_FORMAT d void ITT_FORMAT p const wchar_t ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s no args void ITT_FORMAT p size_t ITT_FORMAT d no args const wchar_t const wchar_t ITT_FORMAT s __itt_heap_function void size_t int ITT_FORMAT d __itt_heap_function void ITT_FORMAT p __itt_heap_function void void size_t int ITT_FORMAT d no args no args unsigned int ITT_FORMAT u const __itt_domain __itt_id ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain __itt_id ITT_FORMAT p const __itt_domain __itt_id __itt_timestamp __itt_timestamp ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain ITT_FORMAT p const __itt_domain __itt_string_handle unsigned long long ITT_FORMAT lu const __itt_domain __itt_string_handle unsigned long long ITT_FORMAT lu const __itt_domain __itt_id __itt_string_handle __itt_metadata_type size_t void ITT_FORMAT p const __itt_domain __itt_id __itt_string_handle const wchar_t size_t ITT_FORMAT lu const __itt_domain __itt_id __itt_relation __itt_id ITT_FORMAT p const wchar_t int ITT_FORMAT __itt_group_mark d int
Definition: ittnotify_static.h:217
tbb::move
void move(tbb_thread &t1, tbb_thread &t2)
Definition: tbb_thread.h:319
tbb::interface5::concurrent_hash_map::generic_emplace
bool generic_emplace(Accessor &&result, Args &&... args)
Definition: concurrent_hash_map.h:1162
tbb::interface5::concurrent_hash_map::concurrent_hash_map
concurrent_hash_map(const concurrent_hash_map &table, const allocator_type &a)
Definition: concurrent_hash_map.h:861
tbb::internal::allocator_traits::deallocate
static void deallocate(Alloc &a, pointer p, size_type n)
Definition: _allocator_traits.h:106
tbb::interface5::concurrent_hash_map::generic_move_insert
bool generic_move_insert(Accessor &&result, value_type &&value)
Definition: concurrent_hash_map.h:1155
key
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d void ITT_FORMAT p void ITT_FORMAT p __itt_model_site __itt_model_site_instance ITT_FORMAT p __itt_model_task __itt_model_task_instance ITT_FORMAT p void ITT_FORMAT p void ITT_FORMAT p void size_t ITT_FORMAT d void ITT_FORMAT p const wchar_t ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s no args void ITT_FORMAT p size_t ITT_FORMAT d no args const wchar_t const wchar_t ITT_FORMAT s __itt_heap_function void size_t int ITT_FORMAT d __itt_heap_function void ITT_FORMAT p __itt_heap_function void void size_t int ITT_FORMAT d no args no args unsigned int ITT_FORMAT u const __itt_domain __itt_id ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain __itt_id ITT_FORMAT p const __itt_domain __itt_id __itt_timestamp __itt_timestamp ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain ITT_FORMAT p const __itt_domain __itt_string_handle unsigned long long ITT_FORMAT lu const __itt_domain __itt_string_handle unsigned long long ITT_FORMAT lu const __itt_domain __itt_id __itt_string_handle * key
Definition: ittnotify_static.h:198
tbb::interface5::internal::hash_map_base::enable_segment_failsafe::my_segment_ptr
segment_ptr_t * my_segment_ptr
Definition: concurrent_hash_map.h:181
tbb::interface5::internal::rehash_req
static hash_map_node_base *const rehash_req
Incompleteness flag value.
Definition: concurrent_hash_map.h:78
tbb::internal::__TBB_store_with_release
void __TBB_store_with_release(volatile T &location, V value)
Definition: tbb_machine.h:716
tbb::interface5::concurrent_hash_map::node_scoped_guard::my_node
node * my_node
Definition: concurrent_hash_map.h:630
tbb::interface5::concurrent_hash_map::node_allocator_type
tbb::internal::allocator_rebind< Allocator, node >::type node_allocator_type
Definition: concurrent_hash_map.h:610
tbb::interface5::concurrent_hash_map::find
bool find(const_accessor &result, const Key &key) const
Find item and acquire a read lock on the item.
Definition: concurrent_hash_map.h:1022
tbb::interface5::concurrent_hash_map::max_size
size_type max_size() const
Upper bound on size.
Definition: concurrent_hash_map.h:1000
tbb::interface5::concurrent_hash_map::equal_range
std::pair< iterator, iterator > equal_range(const Key &key)
Definition: concurrent_hash_map.h:990
tbb::interface5::concurrent_hash_map::accessor_not_used::release
void release()
Definition: concurrent_hash_map.h:1145
tbb::interface5::concurrent_hash_map::node_scoped_guard::dismiss
void dismiss()
Definition: concurrent_hash_map.h:640
tbb::interface5::internal::hash_map_range::empty
bool empty() const
True if range is empty.
Definition: concurrent_hash_map.h:486
tbb::interface5::concurrent_hash_map::insert
bool insert(const_accessor &result, const value_type &value)
Insert item by copying if there is no such key present already and acquire a read lock on the item.
Definition: concurrent_hash_map.h:1050
tbb::interface5::internal::hash_map_iterator::my_map
const Container * my_map
concurrent_hash_map over which we are iterating.
Definition: concurrent_hash_map.h:392
tbb::interface5::concurrent_hash_map::node_scoped_guard
Definition: concurrent_hash_map.h:629
lock
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d void ITT_FORMAT p void ITT_FORMAT p __itt_model_site __itt_model_site_instance ITT_FORMAT p __itt_model_task __itt_model_task_instance ITT_FORMAT p void * lock
Definition: ittnotify_static.h:121
atomic.h
__TBB_Yield
#define __TBB_Yield()
Definition: ibm_aix51.h:44
__TBB_USE_OPTIONAL_RTTI
#define __TBB_USE_OPTIONAL_RTTI
Definition: tbb_config.h:125
tbb::interface5::concurrent_hash_map::range
range_type range(size_type grainsize=1)
Definition: concurrent_hash_map.h:976
tbb::interface5::internal::hash_map_iterator::advance_to_next_bucket
void advance_to_next_bucket()
Definition: concurrent_hash_map.h:369
tbb::interface5::concurrent_hash_map::range
const_range_type range(size_type grainsize=1) const
Definition: concurrent_hash_map.h:979
tbb::interface5::internal::hash_map_base::bucket::mutex
mutex_t mutex
Definition: concurrent_hash_map.h:98
tbb::interface5::concurrent_hash_map::accessor
Allows write access to elements and combines data access, locking, and garbage collection.
Definition: concurrent_hash_map.h:812
tbb::interface5::concurrent_hash_map::internal_move_assign
void internal_move_assign(concurrent_hash_map &&other, tbb::internal::traits_false_type)
Definition: concurrent_hash_map.h:1190
tbb::interface5::concurrent_hash_map::size_type
hash_map_base::size_type size_type
Definition: concurrent_hash_map.h:596
tbb::tbb_hash_compare
hash_compare that is default argument for concurrent_hash_map
Definition: _tbb_hash_compare_impl.h:99
tbb::interface5::concurrent_hash_map::call_clear_on_leave::~call_clear_on_leave
~call_clear_on_leave()
Definition: concurrent_hash_map.h:759
tbb::interface5::concurrent_hash_map::call_clear_on_leave::my_ch_map
concurrent_hash_map * my_ch_map
Definition: concurrent_hash_map.h:756
tbb::interface5::concurrent_hash_map::pointer
value_type * pointer
Definition: concurrent_hash_map.h:598
tbb::interface5::concurrent_hash_map::concurrent_hash_map
concurrent_hash_map(I first, I last, const HashCompare &compare, const allocator_type &a=allocator_type())
Definition: concurrent_hash_map.h:902
tbb::interface5::concurrent_hash_map::bucket_count
size_type bucket_count() const
Returns the current number of buckets.
Definition: concurrent_hash_map.h:1003
tbb::interface5::internal::hash_map_base::enable_segment_failsafe::enable_segment_failsafe
enable_segment_failsafe(segments_table_t &table, segment_index_t k)
Definition: concurrent_hash_map.h:182
tbb::interface5::internal::hash_map_iterator::my_index
size_t my_index
Index in hash table for current item.
Definition: concurrent_hash_map.h:395
tbb::interface5::concurrent_hash_map::node::value
value_type & value()
Definition: concurrent_hash_map.h:620
tbb::interface5::internal::hash_map_range::set_midpoint
void set_midpoint() const
Set my_midpoint to point approximately half way between my_begin and my_end.
Definition: concurrent_hash_map.h:527
tbb::interface5::concurrent_hash_map::const_range_type
internal::hash_map_range< const_iterator > const_range_type
Definition: concurrent_hash_map.h:605
_warning_suppress_disable_notice.h
tbb::interface5::concurrent_hash_map::node_scoped_guard::~node_scoped_guard
~node_scoped_guard()
Definition: concurrent_hash_map.h:634
tbb::interface5::internal::hash_map_range::hash_map_range
hash_map_range(hash_map_range< U > &r)
type conversion
Definition: concurrent_hash_map.h:505
tbb::internal::first
auto first(Container &c) -> decltype(begin(c))
Definition: _range_iterator.h:34
tbb::interface5::internal::hash_map_range::value_type
Iterator::value_type value_type
Definition: concurrent_hash_map.h:480
tbb::interface5::internal::hashcode_t
size_t hashcode_t
Type of a hash code.
Definition: concurrent_hash_map.h:66
tbb::interface5::concurrent_hash_map::empty
bool empty() const
True if size()==0.
Definition: concurrent_hash_map.h:997
tbb::interface5::internal::hash_map_base::segment_index_t
size_t segment_index_t
Segment index type.
Definition: concurrent_hash_map.h:89
tbb::interface5::internal::hash_map_base::hashcode_t
size_t hashcode_t
Type of a hash code.
Definition: concurrent_hash_map.h:87
tbb::interface5::internal::hash_map_base::is_valid
static bool is_valid(void *ptr)
Definition: concurrent_hash_map.h:159
tbb::interface5::concurrent_hash_map::const_accessor::is_writer
bool is_writer()
Definition: concurrent_hash_map.h:806
_allocator_traits.h
s
void const char const char int ITT_FORMAT __itt_group_sync s
Definition: ittnotify_static.h:91
tbb::interface5::internal::hash_map_node_base
Node base type.
Definition: concurrent_hash_map.h:68
tbb::interface5::concurrent_hash_map::call_clear_on_leave
Definition: concurrent_hash_map.h:755
tbb::interface5::concurrent_hash_map::bucket_accessor::my_b
bucket * my_b
Definition: concurrent_hash_map.h:701
tbb::interface5::concurrent_hash_map::size
size_type size() const
Number of items in table.
Definition: concurrent_hash_map.h:994
tbb::interface5::concurrent_hash_map::operator=
concurrent_hash_map & operator=(const concurrent_hash_map &table)
Assignment.
Definition: concurrent_hash_map.h:931
tbb::interface5::concurrent_hash_map::reference
value_type & reference
Definition: concurrent_hash_map.h:600
tbb::internal::itt_hide_store_word
void itt_hide_store_word(T &dst, T src)
Definition: tbb_profiling.h:210
tbb::interface5::concurrent_hash_map::concurrent_hash_map
concurrent_hash_map(size_type n, const allocator_type &a=allocator_type())
Construct empty table with n preallocated buckets. This number serves also as initial concurrency lev...
Definition: concurrent_hash_map.h:839
tbb::interface5::internal::hash_map_base::add_to_bucket
static void add_to_bucket(bucket *b, node_base *n)
Add node.
Definition: concurrent_hash_map.h:173
tbb::interface5::concurrent_hash_map::insert
bool insert(const value_type &value)
Insert item by copying if there is no such key present already.
Definition: concurrent_hash_map.h:1064
__TBB_Log2
intptr_t __TBB_Log2(uintptr_t x)
Definition: tbb_machine.h:863
tbb::interface5::concurrent_hash_map::const_pointer
const typedef value_type * const_pointer
Definition: concurrent_hash_map.h:599
tbb::interface5::concurrent_hash_map::const_accessor::const_accessor
const_accessor()
Create empty result.
Definition: concurrent_hash_map.h:799
tbb::interface5::concurrent_hash_map::node
Definition: concurrent_hash_map.h:616
tbb::interface5::concurrent_hash_map::emplace
bool emplace(Args &&... args)
Insert item by copying if there is no such key present already.
Definition: concurrent_hash_map.h:1105
tbb::interface5::internal::hash_map_range::hash_map_range
hash_map_range(hash_map_range &r, split)
Split range.
Definition: concurrent_hash_map.h:493
tbb::interface5::concurrent_hash_map::insert
bool insert(const_accessor &result, const Key &key)
Insert item (if not already present) and acquire a read lock on the item.
Definition: concurrent_hash_map.h:1036
tbb::interface5::concurrent_hash_map::equal_range
std::pair< const_iterator, const_iterator > equal_range(const Key &key) const
Definition: concurrent_hash_map.h:991
tbb::interface5::concurrent_hash_map::internal_move_assign
void internal_move_assign(concurrent_hash_map &&other, tbb::internal::traits_true_type)
Definition: concurrent_hash_map.h:1185
tbb::interface5::internal::hash_map_iterator::operator*
Value & operator*() const
Definition: concurrent_hash_map.h:422
tbb::interface5::internal::hash_map_node_base::mutex_t
spin_rw_mutex mutex_t
Mutex type.
Definition: concurrent_hash_map.h:70
tbb::interface5::internal::hash_map_range::reference
Iterator::reference reference
Definition: concurrent_hash_map.h:481
tbb::internal::allocator_type
Class for determining type of std::allocator<T>::value_type.
Definition: tbb_stddef.h:471
tbb::spin_rw_mutex_v3::scoped_lock
The scoped locking pattern.
Definition: spin_rw_mutex.h:86
tbb::interface5::concurrent_hash_map::internal_fast_find
const_pointer internal_fast_find(const Key &key) const
Fast find when no concurrent erasure is used. For internal use inside TBB only!
Definition: concurrent_hash_map.h:1203
tbb::interface5::internal::hash_map_range::my_grainsize
size_t my_grainsize
Definition: concurrent_hash_map.h:473
tbb::interface5::internal::hash_map_base::bucket::mutex_t
spin_rw_mutex mutex_t
Mutex type for buckets.
Definition: concurrent_hash_map.h:95
tbb::interface5::internal::hash_map_base::hash_map_base
hash_map_base()
Constructor.
Definition: concurrent_hash_map.h:127
tbb::interface5::concurrent_hash_map::accessor::operator->
pointer operator->() const
Return pointer to associated value in hash table.
Definition: concurrent_hash_map.h:824
tbb::interface5::internal::hash_map_range
Range class used with concurrent_hash_map.
Definition: concurrent_hash_map.h:340
tbb::internal::as_atomic
atomic< T > & as_atomic(T &t)
Definition: atomic.h:572
tbb::interface5::concurrent_hash_map::const_accessor::empty
bool empty() const
True if result is empty.
Definition: concurrent_hash_map.h:777
tbb::interface5::concurrent_hash_map::erase
bool erase(const Key &key)
Erase item.
Definition: concurrent_hash_map.h:1386
value
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d void ITT_FORMAT p void ITT_FORMAT p __itt_model_site __itt_model_site_instance ITT_FORMAT p __itt_model_task __itt_model_task_instance ITT_FORMAT p void ITT_FORMAT p void ITT_FORMAT p void size_t ITT_FORMAT d void ITT_FORMAT p const wchar_t ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s no args void ITT_FORMAT p size_t ITT_FORMAT d no args const wchar_t const wchar_t ITT_FORMAT s __itt_heap_function void size_t int ITT_FORMAT d __itt_heap_function void ITT_FORMAT p __itt_heap_function void void size_t int ITT_FORMAT d no args no args unsigned int ITT_FORMAT u const __itt_domain __itt_id ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain __itt_id ITT_FORMAT p const __itt_domain __itt_id __itt_timestamp __itt_timestamp ITT_FORMAT lu const __itt_domain __itt_id __itt_id __itt_string_handle ITT_FORMAT p const __itt_domain ITT_FORMAT p const __itt_domain __itt_string_handle unsigned long long value
Definition: ittnotify_static.h:192
tbb::interface5::concurrent_hash_map::emplace
bool emplace(accessor &result, Args &&... args)
Insert item by copying if there is no such key present already and acquire a write lock on the item.
Definition: concurrent_hash_map.h:1098
tbb::interface5::concurrent_hash_map::concurrent_hash_map
concurrent_hash_map(concurrent_hash_map &&table, const allocator_type &a)
Move constructor.
Definition: concurrent_hash_map.h:878
tbb::interface5::internal::hash_map_range::end
const Iterator & end() const
Definition: concurrent_hash_map.h:521
tbb::interface5::concurrent_hash_map::node::my_value
tbb::aligned_space< value_type > my_value
Definition: concurrent_hash_map.h:617
tbb::interface5::internal::hash_map_iterator::operator++
hash_map_iterator & operator++()
Definition: concurrent_hash_map.h:449
tbb::interface5::concurrent_hash_map::emplace
bool emplace(const_accessor &result, Args &&... args)
Insert item by copying if there is no such key present already and acquire a read lock on the item.
Definition: concurrent_hash_map.h:1091
tbb::internal
Identifiers declared inside namespace internal should never be used directly by client code.
Definition: atomic.h:65
tbb::interface5::concurrent_hash_map::erase
bool erase(const_accessor &item_accessor)
Erase item by const_accessor.
Definition: concurrent_hash_map.h:1131
tbb_stddef.h
tbb::interface5::concurrent_hash_map::find
bool find(accessor &result, const Key &key)
Find item and acquire a write lock on the item.
Definition: concurrent_hash_map.h:1029
tbb::interface5::concurrent_hash_map::concurrent_hash_map
concurrent_hash_map(size_type n, const HashCompare &compare, const allocator_type &a=allocator_type())
Definition: concurrent_hash_map.h:845
tbb::interface5::concurrent_hash_map::is_write_access_needed
friend bool is_write_access_needed(accessor const &)
Definition: concurrent_hash_map.h:1149
tbb::internal::last
auto last(Container &c) -> decltype(begin(c))
Definition: _range_iterator.h:40
_warning_suppress_enable_notice.h
tbb::interface5::concurrent_hash_map::range_type
internal::hash_map_range< iterator > range_type
Definition: concurrent_hash_map.h:604
tbb::interface5::concurrent_hash_map::insert
bool insert(value_type &&value)
Insert item by copying if there is no such key present already.
Definition: concurrent_hash_map.h:1083
p
void const char const char int ITT_FORMAT __itt_group_sync p
Definition: ittnotify_static.h:91
tbb::internal::itt_hide_load_word
T itt_hide_load_word(const T &src)
Definition: tbb_profiling.h:222
tbb::interface5::internal::hash_map_iterator::map_type
Container map_type
Definition: concurrent_hash_map.h:349
tbb::interface5::concurrent_hash_map::node_scoped_guard::my_alloc
node_allocator_type & my_alloc
Definition: concurrent_hash_map.h:631
tbb::internal::allocator_traits
Definition: _allocator_traits.h:82
tbb::interface5::concurrent_hash_map::accessor_location
friend const_accessor * accessor_location(const_accessor &a)
Definition: concurrent_hash_map.h:1147
tbb::interface5::concurrent_hash_map::accessor::operator*
reference operator*() const
Return reference to associated value in hash table.
Definition: concurrent_hash_map.h:818
tbb::interface5::concurrent_hash_map::begin
iterator begin()
Definition: concurrent_hash_map.h:986
tbb::interface5::concurrent_hash_map::bucket_accessor::is_writer
bool is_writer()
check whether bucket is locked for write
Definition: concurrent_hash_map.h:717
tbb::swap
void swap(concurrent_hash_map< Key, T, HashCompare, A > &a, concurrent_hash_map< Key, T, HashCompare, A > &b)
Definition: concurrent_hash_map.h:1638
tbb::release
Release.
Definition: atomic.h:59
tbb::interface5::concurrent_hash_map::node_scoped_guard::node_scoped_guard
node_scoped_guard(node *n, node_allocator_type &alloc)
Definition: concurrent_hash_map.h:633
tbb::split
Dummy type that distinguishes splitting constructor from copy constructor.
Definition: tbb_stddef.h:416
tbb_allocator.h
tbb::interface5::concurrent_hash_map::node_allocator_traits
tbb::internal::allocator_traits< node_allocator_type > node_allocator_traits
Definition: concurrent_hash_map.h:612
tbb::interface5::internal::operator==
bool operator==(const hash_map_iterator< Container, T > &i, const hash_map_iterator< Container, U > &j)
Definition: concurrent_hash_map.h:456
tbb::interface5::concurrent_hash_map::count
size_type count(const Key &key) const
Return count of items (0 or 1)
Definition: concurrent_hash_map.h:1016
tbb::interface5::concurrent_hash_map::const_accessor::release
void release()
Set to null.
Definition: concurrent_hash_map.h:780
mask
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int mask
Definition: ittnotify_static.h:109
tbb::interface5::concurrent_hash_map::allocate_node_default_construct
static node * allocate_node_default_construct(node_allocator_type &allocator, const Key &key, const T *)
Definition: concurrent_hash_map.h:673
h
void const char const char int ITT_FORMAT __itt_group_sync x void const char ITT_FORMAT __itt_group_sync s void ITT_FORMAT __itt_group_sync p void ITT_FORMAT p void ITT_FORMAT p no args __itt_suppress_mode_t unsigned int void size_t ITT_FORMAT d void ITT_FORMAT p void ITT_FORMAT p __itt_model_site __itt_model_site_instance ITT_FORMAT p __itt_model_task __itt_model_task_instance ITT_FORMAT p void ITT_FORMAT p void ITT_FORMAT p void size_t ITT_FORMAT d void ITT_FORMAT p const wchar_t ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s const char ITT_FORMAT s no args void ITT_FORMAT p size_t ITT_FORMAT d no args const wchar_t const wchar_t ITT_FORMAT s __itt_heap_function h
Definition: ittnotify_static.h:159
tbb::interface5::internal::hash_map_base::bucket
Bucket type.
Definition: concurrent_hash_map.h:93
tbb::interface5::concurrent_hash_map::bucket_accessor::acquire
void acquire(concurrent_hash_map *base, const hashcode_t h, bool writer=false)
find a bucket by masked hashcode, optionally rehash, and acquire the lock
Definition: concurrent_hash_map.h:705
tbb::interface5::concurrent_hash_map::value_type
std::pair< const Key, T > value_type
Definition: concurrent_hash_map.h:595

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

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

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