1 /*
2     Copyright (c) 2005-2021 Intel Corporation
3 
4     Licensed under the Apache License, Version 2.0 (the "License");
5     you may not use this file except in compliance with the License.
6     You may obtain a copy of the License at
7 
8         http://www.apache.org/licenses/LICENSE-2.0
9 
10     Unless required by applicable law or agreed to in writing, software
11     distributed under the License is distributed on an "AS IS" BASIS,
12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13     See the License for the specific language governing permissions and
14     limitations under the License.
15 */
16 
17 #ifndef __TBB_concurrent_hash_map_H
18 #define __TBB_concurrent_hash_map_H
19 
20 #include "detail/_namespace_injection.h"
21 #include "detail/_utils.h"
22 #include "detail/_assert.h"
23 #include "detail/_allocator_traits.h"
24 #include "detail/_containers_helpers.h"
25 #include "detail/_template_helpers.h"
26 #include "detail/_hash_compare.h"
27 #include "detail/_range_common.h"
28 #include "tbb_allocator.h"
29 #include "spin_rw_mutex.h"
30 
31 #include <atomic>
32 #include <initializer_list>
33 #include <tuple>
34 #include <iterator>
35 #include <utility>      // Need std::pair
36 #include <cstring>      // Need std::memset
37 
38 namespace tbb {
39 namespace detail {
40 namespace d2 {
41 
42 template <typename MutexType>
43 struct hash_map_node_base : no_copy {
44     using mutex_type = MutexType;
45     // Scoped lock type for mutex
46     using scoped_type = typename MutexType::scoped_lock;
47     // Next node in chain
48     hash_map_node_base* next;
49     mutex_type mutex;
50 };
51 
52 // Incompleteness flag value
53 static void* const rehash_req_flag = reinterpret_cast<void*>(std::size_t(3));
54 // Rehashed empty bucket flag
55 static void* const empty_rehashed_flag = reinterpret_cast<void*>(std::size_t(0));
56 
57 template <typename MutexType>
58 bool rehash_required( hash_map_node_base<MutexType>* node_ptr ) {
59     return reinterpret_cast<void*>(node_ptr) == rehash_req_flag;
60 }
61 
62 template <typename MutexType>
63 bool empty_rehashed( hash_map_node_base<MutexType>* node_ptr ) {
64     return reinterpret_cast<void*>(node_ptr) == empty_rehashed_flag;
65 }
66 
67 // base class of concurrent_hash_map
68 
69 template <typename Allocator, typename MutexType>
70 class hash_map_base {
71 public:
72     using size_type = std::size_t;
73     using hashcode_type = std::size_t;
74     using segment_index_type = std::size_t;
75     using node_base = hash_map_node_base<MutexType>;
76 
77     struct bucket : no_copy {
78         using mutex_type = MutexType;
79         using scoped_type = typename mutex_type::scoped_lock;
80 
81         bucket() : node_list(nullptr) {}
82         bucket( node_base* ptr ) : node_list(ptr) {}
83 
84         mutex_type mutex;
85         std::atomic<node_base*> node_list;
86     };
87 
88     using allocator_type = Allocator;
89     using allocator_traits_type = tbb::detail::allocator_traits<allocator_type>;
90     using bucket_allocator_type = typename allocator_traits_type::template rebind_alloc<bucket>;
91     using bucket_allocator_traits = tbb::detail::allocator_traits<bucket_allocator_type>;
92 
93     // Count of segments in the first block
94     static constexpr size_type embedded_block = 1;
95     // Count of segments in the first block
96     static constexpr size_type embedded_buckets = 1 << embedded_block;
97     // Count of segments in the first block
98     static constexpr size_type first_block = 8; //including embedded_block. perfect with bucket size 16, so the allocations are power of 4096
99     // Size of a pointer / table size
100     static constexpr size_type pointers_per_table = sizeof(segment_index_type) * 8; // one segment per bit
101 
102     using segment_ptr_type = bucket*;
103     using atomic_segment_type = std::atomic<segment_ptr_type>;
104     using segments_table_type = atomic_segment_type[pointers_per_table];
105 
106     hash_map_base( const allocator_type& alloc ) : my_allocator(alloc), my_mask(embedded_buckets - 1), my_size(0) {
107         for (size_type i = 0; i != embedded_buckets; ++i) {
108             my_embedded_segment[i].node_list.store(nullptr, std::memory_order_relaxed);
109         }
110 
111         for (size_type segment_index = 0; segment_index < pointers_per_table; ++segment_index) {
112             auto argument = segment_index < embedded_block ? my_embedded_segment + segment_base(segment_index) : nullptr;
113             my_table[segment_index].store(argument, std::memory_order_relaxed);
114         }
115 
116         __TBB_ASSERT( embedded_block <= first_block, "The first block number must include embedded blocks");
117     }
118 
119     // segment index of given index in the array
120     static segment_index_type segment_index_of( size_type index ) {
121         return segment_index_type(tbb::detail::log2( index|1 ));
122     }
123 
124     // the first array index of given segment
125     static segment_index_type segment_base( segment_index_type k ) {
126         return (segment_index_type(1) << k & ~segment_index_type(1));
127     }
128 
129     // segment size except for k == 0
130     static size_type segment_size( segment_index_type k ) {
131         return size_type(1) << k; // fake value for k==0
132     }
133 
134     // true if ptr is valid pointer
135     static bool is_valid( void* ptr ) {
136         return reinterpret_cast<uintptr_t>(ptr) > uintptr_t(63);
137     }
138 
139     template <typename... Args>
140     void init_buckets_impl( segment_ptr_type ptr, size_type sz, Args&&... args ) {
141         for (size_type i = 0; i < sz; ++i) {
142             bucket_allocator_traits::construct(my_allocator, ptr + i, std::forward<Args>(args)...);
143         }
144     }
145 
146     // Initialize buckets
147     void init_buckets( segment_ptr_type ptr, size_type sz, bool is_initial ) {
148         if (is_initial) {
149             init_buckets_impl(ptr, sz);
150         } else {
151             init_buckets_impl(ptr, sz, reinterpret_cast<node_base*>(rehash_req_flag));
152         }
153     }
154 
155     // Add node n to bucket b
156     static void add_to_bucket( bucket* b, node_base* n ) {
157         __TBB_ASSERT(!rehash_required(b->node_list.load(std::memory_order_relaxed)), nullptr);
158         n->next = b->node_list.load(std::memory_order_relaxed);
159         b->node_list.store(n, std::memory_order_relaxed); // its under lock and flag is set
160     }
161 
162     const bucket_allocator_type& get_allocator() const {
163         return my_allocator;
164     }
165 
166     bucket_allocator_type& get_allocator() {
167         return my_allocator;
168     }
169 
170     // Enable segment
171     void enable_segment( segment_index_type k, bool is_initial = false ) {
172         __TBB_ASSERT( k, "Zero segment must be embedded" );
173         size_type sz;
174         __TBB_ASSERT( !is_valid(my_table[k].load(std::memory_order_relaxed)), "Wrong concurrent assignment");
175         if (k >= first_block) {
176             sz = segment_size(k);
177             segment_ptr_type ptr = nullptr;
178             try_call( [&] {
179                 ptr = bucket_allocator_traits::allocate(my_allocator, sz);
180             } ).on_exception( [&] {
181                 my_table[k].store(nullptr, std::memory_order_relaxed);
182             });
183 
184             __TBB_ASSERT(ptr, nullptr);
185             init_buckets(ptr, sz, is_initial);
186             my_table[k].store(ptr, std::memory_order_release);
187             sz <<= 1;// double it to get entire capacity of the container
188         } else { // the first block
189             __TBB_ASSERT( k == embedded_block, "Wrong segment index" );
190             sz = segment_size(first_block);
191             segment_ptr_type ptr = nullptr;
192             try_call( [&] {
193                 ptr = bucket_allocator_traits::allocate(my_allocator, sz - embedded_buckets);
194             } ).on_exception( [&] {
195                 my_table[k].store(nullptr, std::memory_order_relaxed);
196             });
197 
198             __TBB_ASSERT(ptr, nullptr);
199             init_buckets(ptr, sz - embedded_buckets, is_initial);
200             ptr -= segment_base(embedded_block);
201             for(segment_index_type i = embedded_block; i < first_block; i++) // calc the offsets
202                 my_table[i].store(ptr + segment_base(i), std::memory_order_release);
203         }
204         my_mask.store(sz-1, std::memory_order_release);
205     }
206 
207     void delete_segment( segment_index_type s ) {
208         segment_ptr_type buckets_ptr = my_table[s].load(std::memory_order_relaxed);
209         size_type sz = segment_size( s ? s : 1 );
210 
211         size_type deallocate_size = 0;
212 
213         if (s >= first_block) { // the first segment or the next
214             deallocate_size = sz;
215         } else if (s == embedded_block && embedded_block != first_block) {
216             deallocate_size = segment_size(first_block) - embedded_buckets;
217         }
218 
219         for (size_type i = 0; i < deallocate_size; ++i) {
220             bucket_allocator_traits::destroy(my_allocator, buckets_ptr + i);
221         }
222         if (deallocate_size != 0) {
223             bucket_allocator_traits::deallocate(my_allocator, buckets_ptr, deallocate_size);
224         }
225 
226         if (s >= embedded_block) my_table[s].store(nullptr, std::memory_order_relaxed);
227     }
228 
229     // Get bucket by (masked) hashcode
230     bucket *get_bucket( hashcode_type h ) const noexcept {
231         segment_index_type s = segment_index_of( h );
232         h -= segment_base(s);
233         segment_ptr_type seg = my_table[s].load(std::memory_order_acquire);
234         __TBB_ASSERT( is_valid(seg), "hashcode must be cut by valid mask for allocated segments" );
235         return &seg[h];
236     }
237 
238     // detail serial rehashing helper
239     void mark_rehashed_levels( hashcode_type h ) noexcept {
240         segment_index_type s = segment_index_of( h );
241         while (segment_ptr_type seg = my_table[++s].load(std::memory_order_relaxed))
242             if (rehash_required(seg[h].node_list.load(std::memory_order_relaxed))) {
243                 seg[h].node_list.store(reinterpret_cast<node_base*>(empty_rehashed_flag), std::memory_order_relaxed);
244                 mark_rehashed_levels( h + ((hashcode_type)1<<s) ); // optimized segment_base(s)
245             }
246     }
247 
248     // Check for mask race
249     // Splitting into two functions should help inlining
250     inline bool check_mask_race( const hashcode_type h, hashcode_type &m ) const {
251         hashcode_type m_now, m_old = m;
252         m_now = my_mask.load(std::memory_order_acquire);
253         if (m_old != m_now) {
254             return check_rehashing_collision(h, m_old, m = m_now);
255         }
256         return false;
257     }
258 
259     // Process mask race, check for rehashing collision
260     bool check_rehashing_collision( const hashcode_type h, hashcode_type m_old, hashcode_type m ) const {
261         __TBB_ASSERT(m_old != m, nullptr); // TODO?: m arg could be optimized out by passing h = h&m
262         if( (h & m_old) != (h & m) ) { // mask changed for this hashcode, rare event
263             // condition above proves that 'h' has some other bits set beside 'm_old'
264             // find next applicable mask after m_old    //TODO: look at bsl instruction
265             for( ++m_old; !(h & m_old); m_old <<= 1 ) // at maximum few rounds depending on the first block size
266                 ;
267             m_old = (m_old<<1) - 1; // get full mask from a bit
268             __TBB_ASSERT((m_old&(m_old+1))==0 && m_old <= m, nullptr);
269             // check whether it is rehashing/ed
270             if (!rehash_required(get_bucket(h & m_old)->node_list.load(std::memory_order_acquire))) {
271                 return true;
272             }
273         }
274         return false;
275     }
276 
277     // Insert a node and check for load factor. @return segment index to enable.
278     segment_index_type insert_new_node( bucket *b, node_base *n, hashcode_type mask ) {
279         size_type sz = ++my_size; // prefix form is to enforce allocation after the first item inserted
280         add_to_bucket( b, n );
281         // check load factor
282         if( sz >= mask ) { // TODO: add custom load_factor
283             segment_index_type new_seg = tbb::detail::log2( mask+1 ); //optimized segment_index_of
284             __TBB_ASSERT( is_valid(my_table[new_seg-1].load(std::memory_order_relaxed)), "new allocations must not publish new mask until segment has allocated");
285             static const segment_ptr_type is_allocating = segment_ptr_type(2);;
286             segment_ptr_type disabled = nullptr;
287             if (!(my_table[new_seg].load(std::memory_order_acquire))
288                 && my_table[new_seg].compare_exchange_strong(disabled, is_allocating))
289                 return new_seg; // The value must be processed
290         }
291         return 0;
292     }
293 
294     // Prepare enough segments for number of buckets
295     void reserve(size_type buckets) {
296         if( !buckets-- ) return;
297         bool is_initial = !my_size.load(std::memory_order_relaxed);
298         for (size_type m = my_mask.load(std::memory_order_relaxed); buckets > m;
299             m = my_mask.load(std::memory_order_relaxed))
300         {
301             enable_segment( segment_index_of( m+1 ), is_initial );
302         }
303     }
304 
305     // Swap hash_map_bases
306     void internal_swap_content(hash_map_base &table) {
307         using std::swap;
308         swap_atomics_relaxed(my_mask, table.my_mask);
309         swap_atomics_relaxed(my_size, table.my_size);
310 
311         for(size_type i = 0; i < embedded_buckets; i++) {
312             auto temp = my_embedded_segment[i].node_list.load(std::memory_order_relaxed);
313             my_embedded_segment[i].node_list.store(table.my_embedded_segment[i].node_list.load(std::memory_order_relaxed),
314                 std::memory_order_relaxed);
315             table.my_embedded_segment[i].node_list.store(temp, std::memory_order_relaxed);
316         }
317         for(size_type i = embedded_block; i < pointers_per_table; i++) {
318             auto temp = my_table[i].load(std::memory_order_relaxed);
319             my_table[i].store(table.my_table[i].load(std::memory_order_relaxed),
320                 std::memory_order_relaxed);
321             table.my_table[i].store(temp, std::memory_order_relaxed);
322         }
323     }
324 
325     void internal_move(hash_map_base&& other) {
326         my_mask.store(other.my_mask.load(std::memory_order_relaxed), std::memory_order_relaxed);
327         other.my_mask.store(embedded_buckets - 1, std::memory_order_relaxed);
328 
329         my_size.store(other.my_size.load(std::memory_order_relaxed), std::memory_order_relaxed);
330         other.my_size.store(0, std::memory_order_relaxed);
331 
332         for (size_type i = 0; i < embedded_buckets; ++i) {
333             my_embedded_segment[i].node_list.store(other.my_embedded_segment[i].node_list, std::memory_order_relaxed);
334             other.my_embedded_segment[i].node_list.store(nullptr, std::memory_order_relaxed);
335         }
336 
337         for (size_type i = embedded_block; i < pointers_per_table; ++i) {
338             my_table[i].store(other.my_table[i].load(std::memory_order_relaxed),
339                 std::memory_order_relaxed);
340             other.my_table[i].store(nullptr, std::memory_order_relaxed);
341         }
342     }
343 
344 protected:
345     bucket_allocator_type my_allocator;
346     // Hash mask = sum of allocated segment sizes - 1
347     std::atomic<hashcode_type> my_mask;
348     // Size of container in stored items
349     std::atomic<size_type> my_size; // It must be in separate cache line from my_mask due to performance effects
350     // Zero segment
351     bucket my_embedded_segment[embedded_buckets];
352     // Segment pointers table. Also prevents false sharing between my_mask and my_size
353     segments_table_type my_table;
354 };
355 
356 template <typename Iterator>
357 class hash_map_range;
358 
359 // Meets requirements of a forward iterator for STL
360 // Value is either the T or const T type of the container.
361 template <typename Container, typename Value>
362 class hash_map_iterator {
363     using map_type = Container;
364     using node = typename Container::node;
365     using map_base = typename Container::base_type;
366     using node_base = typename map_base::node_base;
367     using bucket = typename map_base::bucket;
368 public:
369     using value_type = Value;
370     using size_type = typename Container::size_type;
371     using difference_type = typename Container::difference_type;
372     using pointer = value_type*;
373     using reference = value_type&;
374     using iterator_category = std::forward_iterator_tag;
375 
376     // Construct undefined iterator
377     hash_map_iterator(): my_map(), my_index(), my_bucket(), my_node() {}
378     hash_map_iterator( const hash_map_iterator<Container, typename Container::value_type>& other ) :
379         my_map(other.my_map),
380         my_index(other.my_index),
381         my_bucket(other.my_bucket),
382         my_node(other.my_node)
383     {}
384 
385     hash_map_iterator& operator=( const hash_map_iterator<Container, typename Container::value_type>& other ) {
386         my_map = other.my_map;
387         my_index = other.my_index;
388         my_bucket = other.my_bucket;
389         my_node = other.my_node;
390         return *this;
391     }
392 
393     Value& operator*() const {
394         __TBB_ASSERT( map_base::is_valid(my_node), "iterator uninitialized or at end of container?" );
395         return my_node->value();
396     }
397 
398     Value* operator->() const {return &operator*();}
399 
400     hash_map_iterator& operator++() {
401         my_node = static_cast<node*>( my_node->next );
402         if( !my_node ) advance_to_next_bucket();
403         return *this;
404     }
405 
406     // Post increment
407     hash_map_iterator operator++(int) {
408         hash_map_iterator old(*this);
409         operator++();
410         return old;
411     }
412 private:
413     template <typename C, typename T, typename U>
414     friend bool operator==( const hash_map_iterator<C,T>& i, const hash_map_iterator<C,U>& j );
415 
416     template <typename C, typename T, typename U>
417     friend bool operator!=( const hash_map_iterator<C,T>& i, const hash_map_iterator<C,U>& j );
418 
419     template <typename C, typename T, typename U>
420     friend ptrdiff_t operator-( const hash_map_iterator<C,T>& i, const hash_map_iterator<C,U>& j );
421 
422     template <typename C, typename U>
423     friend class hash_map_iterator;
424 
425     template <typename I>
426     friend class hash_map_range;
427 
428     void advance_to_next_bucket() { // TODO?: refactor to iterator_base class
429         size_t k = my_index+1;
430         __TBB_ASSERT( my_bucket, "advancing an invalid iterator?");
431         while (k <= my_map->my_mask.load(std::memory_order_relaxed)) {
432             // Following test uses 2's-complement wizardry
433             if( k&(k-2) ) // not the beginning of a segment
434                 ++my_bucket;
435             else my_bucket = my_map->get_bucket( k );
436             my_node = static_cast<node*>( my_bucket->node_list.load(std::memory_order_relaxed) );
437             if( map_base::is_valid(my_node) ) {
438                 my_index = k; return;
439             }
440             ++k;
441         }
442         my_bucket = 0; my_node = 0; my_index = k; // the end
443     }
444 
445     template <typename Key, typename T, typename HashCompare, typename A
446 #if __TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS
447             , typename M
448 #endif
449              >
450     friend class concurrent_hash_map;
451 
452     hash_map_iterator( const Container &map, std::size_t index, const bucket *b, node_base *n ) :
453         my_map(&map), my_index(index), my_bucket(b), my_node(static_cast<node*>(n))
454     {
455         if( b && !map_base::is_valid(n) )
456             advance_to_next_bucket();
457     }
458 
459     // concurrent_hash_map over which we are iterating.
460     const Container *my_map;
461     // Index in hash table for current item
462     size_t my_index;
463     // Pointer to bucket
464     const bucket* my_bucket;
465     // Pointer to node that has current item
466     node* my_node;
467 };
468 
469 template <typename Container, typename T, typename U>
470 bool operator==( const hash_map_iterator<Container,T>& i, const hash_map_iterator<Container,U>& j ) {
471     return i.my_node == j.my_node && i.my_map == j.my_map;
472 }
473 
474 template <typename Container, typename T, typename U>
475 bool operator!=( const hash_map_iterator<Container,T>& i, const hash_map_iterator<Container,U>& j ) {
476     return i.my_node != j.my_node || i.my_map != j.my_map;
477 }
478 
479 // Range class used with concurrent_hash_map
480 template <typename Iterator>
481 class hash_map_range {
482     using map_type = typename Iterator::map_type;
483 public:
484     // Type for size of a range
485     using size_type = std::size_t;
486     using value_type = typename Iterator::value_type;
487     using reference = typename Iterator::reference;
488     using difference_type = typename Iterator::difference_type;
489     using iterator = Iterator;
490 
491     // True if range is empty.
492     bool empty() const { return my_begin == my_end; }
493 
494     // True if range can be partitioned into two subranges.
495     bool is_divisible() const {
496         return my_midpoint != my_end;
497     }
498 
499     // Split range.
500     hash_map_range( hash_map_range& r, split ) :
501         my_end(r.my_end),
502         my_grainsize(r.my_grainsize)
503     {
504         r.my_end = my_begin = r.my_midpoint;
505         __TBB_ASSERT( !empty(), "Splitting despite the range is not divisible" );
506         __TBB_ASSERT( !r.empty(), "Splitting despite the range is not divisible" );
507         set_midpoint();
508         r.set_midpoint();
509     }
510 
511     // Init range with container and grainsize specified
512     hash_map_range( const map_type &map, size_type grainsize_ = 1 ) :
513         my_begin( Iterator( map, 0, map.my_embedded_segment, map.my_embedded_segment->node_list.load(std::memory_order_relaxed) ) ),
514         my_end( Iterator( map, map.my_mask.load(std::memory_order_relaxed) + 1, 0, 0 ) ),
515         my_grainsize( grainsize_ )
516     {
517         __TBB_ASSERT( grainsize_>0, "grainsize must be positive" );
518         set_midpoint();
519     }
520 
521     Iterator begin() const { return my_begin; }
522     Iterator end() const { return my_end; }
523     // The grain size for this range.
524     size_type grainsize() const { return my_grainsize; }
525 
526 private:
527     Iterator my_begin;
528     Iterator my_end;
529     mutable Iterator my_midpoint;
530     size_t my_grainsize;
531     // Set my_midpoint to point approximately half way between my_begin and my_end.
532     void set_midpoint() const;
533     template <typename U> friend class hash_map_range;
534 };
535 
536 template <typename Iterator>
537 void hash_map_range<Iterator>::set_midpoint() const {
538     // Split by groups of nodes
539     size_t m = my_end.my_index-my_begin.my_index;
540     if( m > my_grainsize ) {
541         m = my_begin.my_index + m/2u;
542         auto b = my_begin.my_map->get_bucket(m);
543         my_midpoint = Iterator(*my_begin.my_map,m,b,b->node_list.load(std::memory_order_relaxed));
544     } else {
545         my_midpoint = my_end;
546     }
547     __TBB_ASSERT( my_begin.my_index <= my_midpoint.my_index,
548         "my_begin is after my_midpoint" );
549     __TBB_ASSERT( my_midpoint.my_index <= my_end.my_index,
550         "my_midpoint is after my_end" );
551     __TBB_ASSERT( my_begin != my_midpoint || my_begin == my_end,
552         "[my_begin, my_midpoint) range should not be empty" );
553 }
554 
555 #if __TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS && __TBB_CPP20_CONCEPTS_PRESENT
556 template <typename Mutex>
557 concept ch_map_rw_scoped_lockable = rw_scoped_lockable<Mutex> &&
558                                     requires (const typename Mutex::scoped_lock& sl) {
559                                         { sl.is_writer() } -> std::convertible_to<bool>;
560                                     };
561 #endif
562 
563 template <typename Key, typename T,
564           typename HashCompare = d1::tbb_hash_compare<Key>,
565           typename Allocator = tbb_allocator<std::pair<const Key, T>>
566 #if __TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS
567         , typename MutexType = spin_rw_mutex
568          >
569     __TBB_requires(tbb::detail::hash_compare<HashCompare, Key> &&
570                    ch_map_rw_scoped_lockable<MutexType>)
571 #else
572          >
573     __TBB_requires(tbb::detail::hash_compare<HashCompare, Key>)
574 #endif
575 class concurrent_hash_map
576 #if __TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS
577     : protected hash_map_base<Allocator, MutexType>
578 #else
579     : protected hash_map_base<Allocator, spin_rw_mutex>
580 #endif
581 {
582     template <typename Container, typename Value>
583     friend class hash_map_iterator;
584 
585     template <typename I>
586     friend class hash_map_range;
587     using allocator_traits_type = tbb::detail::allocator_traits<Allocator>;
588 
589 #if __TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS
590     using base_type = hash_map_base<Allocator, MutexType>;
591 #else
592     using base_type = hash_map_base<Allocator, spin_rw_mutex>;
593 #endif
594 public:
595     using key_type = Key;
596     using mapped_type = T;
597     // type_identity is needed to disable implicit deduction guides for std::initializer_list constructors
598     // and copy/move constructor with explicit allocator argument
599     using allocator_type = tbb::detail::type_identity_t<Allocator>;
600     using hash_compare_type = tbb::detail::type_identity_t<HashCompare>;
601     using value_type = std::pair<const Key, T>;
602     using size_type = typename base_type::size_type;
603     using difference_type = std::ptrdiff_t;
604 #if __TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS
605     using mutex_type = MutexType;
606 #endif
607     using pointer = typename allocator_traits_type::pointer;
608     using const_pointer = typename allocator_traits_type::const_pointer;
609 
610     using reference = value_type&;
611     using const_reference = const value_type&;
612     using iterator = hash_map_iterator<concurrent_hash_map, value_type>;
613     using const_iterator = hash_map_iterator<concurrent_hash_map, const value_type>;
614     using range_type = hash_map_range<iterator>;
615     using const_range_type = hash_map_range<const_iterator>;
616 
617 protected:
618     static_assert(std::is_same<value_type, typename Allocator::value_type>::value,
619         "value_type of the container must be the same as its allocator's");
620 
621     friend class const_accessor;
622     class node;
623     using segment_index_type = typename base_type::segment_index_type;
624     using segment_ptr_type = typename base_type::segment_ptr_type;
625     using node_base = typename base_type::node_base;
626     using bucket = typename base_type::bucket;
627     using hashcode_type = typename base_type::hashcode_type;
628     using bucket_allocator_type = typename base_type::bucket_allocator_type;
629     using node_allocator_type = typename base_type::allocator_traits_type::template rebind_alloc<node>;
630     using node_allocator_traits = tbb::detail::allocator_traits<node_allocator_type>;
631     hash_compare_type my_hash_compare;
632 
633     class node : public node_base {
634     public:
635         node() {}
636         ~node() {}
637         pointer storage() { return &my_value; }
638         value_type& value() { return *storage(); }
639     private:
640         union {
641             value_type my_value;
642         };
643     };
644 
645     void delete_node( node_base *n ) {
646         node_allocator_type node_allocator(this->get_allocator());
647         node_allocator_traits::destroy(node_allocator, static_cast<node*>(n)->storage());
648         node_allocator_traits::destroy(node_allocator, static_cast<node*>(n));
649         node_allocator_traits::deallocate(node_allocator, static_cast<node*>(n), 1);
650     }
651 
652     template <typename... Args>
653     static node* create_node(bucket_allocator_type& allocator, Args&&... args) {
654         node_allocator_type node_allocator(allocator);
655         node* node_ptr = node_allocator_traits::allocate(node_allocator, 1);
656         auto guard = make_raii_guard([&] {
657             node_allocator_traits::destroy(node_allocator, node_ptr);
658             node_allocator_traits::deallocate(node_allocator, node_ptr, 1);
659         });
660 
661         node_allocator_traits::construct(node_allocator, node_ptr);
662         node_allocator_traits::construct(node_allocator, node_ptr->storage(), std::forward<Args>(args)...);
663         guard.dismiss();
664         return node_ptr;
665     }
666 
667     static node* allocate_node_copy_construct(bucket_allocator_type& allocator, const Key &key, const T * t){
668         return create_node(allocator, key, *t);
669     }
670 
671     static node* allocate_node_move_construct(bucket_allocator_type& allocator, const Key &key, const T * t){
672         return create_node(allocator, key, std::move(*const_cast<T*>(t)));
673     }
674 
675     template <typename K = Key>
676     static node* allocate_node_default_construct(bucket_allocator_type& allocator, const K &key, const T * ){
677         // Emplace construct an empty T object inside the pair
678         return create_node(allocator, std::piecewise_construct,
679                            std::forward_as_tuple(key), std::forward_as_tuple());
680     }
681 
682     static node* do_not_allocate_node(bucket_allocator_type& , const Key &, const T * ){
683         __TBB_ASSERT(false,"this dummy function should not be called");
684         return nullptr;
685     }
686 
687     template <typename K>
688     node *search_bucket( const K &key, bucket *b ) const {
689         node *n = static_cast<node*>( b->node_list.load(std::memory_order_relaxed) );
690         while (this->is_valid(n) && !my_hash_compare.equal(key, n->value().first))
691             n = static_cast<node*>( n->next );
692         __TBB_ASSERT(!rehash_required(n), "Search can be executed only for rehashed bucket");
693         return n;
694     }
695 
696     // bucket accessor is to find, rehash, acquire a lock, and access a bucket
697     class bucket_accessor : public bucket::scoped_type {
698         bucket *my_b;
699     public:
700         bucket_accessor( concurrent_hash_map *base, const hashcode_type h, bool writer = false ) { acquire( base, h, writer ); }
701         // find a bucket by masked hashcode, optionally rehash, and acquire the lock
702         inline void acquire( concurrent_hash_map *base, const hashcode_type h, bool writer = false ) {
703             my_b = base->get_bucket( h );
704             // TODO: actually, notification is unnecessary here, just hiding double-check
705             if (rehash_required(my_b->node_list.load(std::memory_order_acquire))
706                 && bucket::scoped_type::try_acquire( my_b->mutex, /*write=*/true ) )
707             {
708                 if (rehash_required(my_b->node_list.load(std::memory_order_relaxed))) base->rehash_bucket(my_b, h); // recursive rehashing
709             }
710             else bucket::scoped_type::acquire( my_b->mutex, writer );
711             __TBB_ASSERT(!rehash_required(my_b->node_list.load(std::memory_order_relaxed)), nullptr);
712         }
713 
714         // get bucket pointer
715         bucket *operator() () { return my_b; }
716     };
717 
718     // TODO refactor to hash_base
719     void rehash_bucket( bucket *b_new, const hashcode_type hash ) {
720         __TBB_ASSERT( *(intptr_t*)(&b_new->mutex), "b_new must be locked (for write)");
721         __TBB_ASSERT( hash > 1, "The lowermost buckets can't be rehashed" );
722         b_new->node_list.store(reinterpret_cast<node_base*>(empty_rehashed_flag), std::memory_order_release); // mark rehashed
723         hashcode_type mask = (1u << tbb::detail::log2(hash)) - 1; // get parent mask from the topmost bit
724         bucket_accessor b_old( this, hash & mask );
725 
726         mask = (mask<<1) | 1; // get full mask for new bucket
727         __TBB_ASSERT( (mask&(mask+1))==0 && (hash & mask) == hash, nullptr );
728     restart:
729         node_base* prev = nullptr;
730         node_base* curr = b_old()->node_list.load(std::memory_order_acquire);
731         while (this->is_valid(curr)) {
732             hashcode_type curr_node_hash = my_hash_compare.hash(static_cast<node*>(curr)->value().first);
733 
734             if ((curr_node_hash & mask) == hash) {
735                 if (!b_old.is_writer()) {
736                     if (!b_old.upgrade_to_writer()) {
737                         goto restart; // node ptr can be invalid due to concurrent erase
738                     }
739                 }
740                 node_base* next = curr->next;
741                 // exclude from b_old
742                 if (prev == nullptr) {
743                     b_old()->node_list.store(curr->next, std::memory_order_relaxed);
744                 } else {
745                     prev->next = curr->next;
746                 }
747                 this->add_to_bucket(b_new, curr);
748                 curr = next;
749             } else {
750                 prev = curr;
751                 curr = curr->next;
752             }
753         }
754     }
755 
756     template <typename U>
757     using hash_compare_is_transparent = dependent_bool<comp_is_transparent<hash_compare_type>, U>;
758 
759 public:
760 
761     class accessor;
762     // Combines data access, locking, and garbage collection.
763     class const_accessor : private node::scoped_type /*which derived from no_copy*/ {
764 #if __TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS
765         friend class concurrent_hash_map<Key,T,HashCompare,Allocator,MutexType>;
766 #else
767         friend class concurrent_hash_map<Key,T,HashCompare,Allocator>;
768 #endif
769         friend class accessor;
770     public:
771         // Type of value
772         using value_type = const typename concurrent_hash_map::value_type;
773 
774         // True if result is empty.
775         bool empty() const { return !my_node; }
776 
777         // Set to null
778         void release() {
779             if( my_node ) {
780                 node::scoped_type::release();
781                 my_node = 0;
782             }
783         }
784 
785         // Return reference to associated value in hash table.
786         const_reference operator*() const {
787             __TBB_ASSERT( my_node, "attempt to dereference empty accessor" );
788             return my_node->value();
789         }
790 
791         // Return pointer to associated value in hash table.
792         const_pointer operator->() const {
793             return &operator*();
794         }
795 
796         // Create empty result
797         const_accessor() : my_node(nullptr) {}
798 
799         // Destroy result after releasing the underlying reference.
800         ~const_accessor() {
801             my_node = nullptr; // scoped lock's release() is called in its destructor
802         }
803     protected:
804         bool is_writer() { return node::scoped_type::is_writer(); }
805         node *my_node;
806         hashcode_type my_hash;
807     };
808 
809     // Allows write access to elements and combines data access, locking, and garbage collection.
810     class accessor: public const_accessor {
811     public:
812         // Type of value
813         using value_type = typename concurrent_hash_map::value_type;
814 
815         // Return reference to associated value in hash table.
816         reference operator*() const {
817             __TBB_ASSERT( this->my_node, "attempt to dereference empty accessor" );
818             return this->my_node->value();
819         }
820 
821         // Return pointer to associated value in hash table.
822         pointer operator->() const {
823             return &operator*();
824         }
825     };
826 
827     explicit concurrent_hash_map( const hash_compare_type& compare, const allocator_type& a = allocator_type() )
828         : base_type(a)
829         , my_hash_compare(compare)
830     {}
831 
832     concurrent_hash_map() : concurrent_hash_map(hash_compare_type()) {}
833 
834     explicit concurrent_hash_map( const allocator_type& a )
835         : concurrent_hash_map(hash_compare_type(), a)
836     {}
837 
838     // Construct empty table with n preallocated buckets. This number serves also as initial concurrency level.
839     concurrent_hash_map( size_type n, const allocator_type &a = allocator_type() )
840         : concurrent_hash_map(a)
841     {
842         this->reserve(n);
843     }
844 
845     concurrent_hash_map( size_type n, const hash_compare_type& compare, const allocator_type& a = allocator_type() )
846         : concurrent_hash_map(compare, a)
847     {
848         this->reserve(n);
849     }
850 
851     // Copy constructor
852     concurrent_hash_map( const concurrent_hash_map &table )
853         : concurrent_hash_map(node_allocator_traits::select_on_container_copy_construction(table.get_allocator()))
854     {
855         try_call( [&] {
856             internal_copy(table);
857         }).on_exception( [&] {
858             this->clear();
859         });
860     }
861 
862     concurrent_hash_map( const concurrent_hash_map &table, const allocator_type &a)
863         : concurrent_hash_map(a)
864     {
865         try_call( [&] {
866             internal_copy(table);
867         }).on_exception( [&] {
868             this->clear();
869         });
870     }
871 
872     // Move constructor
873     concurrent_hash_map( concurrent_hash_map &&table )
874         : concurrent_hash_map(std::move(table.get_allocator()))
875     {
876         this->internal_move(std::move(table));
877     }
878 
879     // Move constructor
880     concurrent_hash_map( concurrent_hash_map &&table, const allocator_type &a )
881         : concurrent_hash_map(a)
882     {
883         using is_equal_type = typename node_allocator_traits::is_always_equal;
884         internal_move_construct_with_allocator(std::move(table), a, is_equal_type());
885     }
886 
887     // Construction with copying iteration range and given allocator instance
888     template <typename I>
889     concurrent_hash_map( I first, I last, const allocator_type &a = allocator_type() )
890         : concurrent_hash_map(a)
891     {
892         try_call( [&] {
893             internal_copy(first, last, std::distance(first, last));
894         }).on_exception( [&] {
895             this->clear();
896         });
897     }
898 
899     template <typename I>
900     concurrent_hash_map( I first, I last, const hash_compare_type& compare, const allocator_type& a = allocator_type() )
901         : concurrent_hash_map(compare, a)
902     {
903         try_call( [&] {
904             internal_copy(first, last, std::distance(first, last));
905         }).on_exception( [&] {
906             this->clear();
907         });
908     }
909 
910     concurrent_hash_map( std::initializer_list<value_type> il, const hash_compare_type& compare = hash_compare_type(), const allocator_type& a = allocator_type() )
911         : concurrent_hash_map(compare, a)
912     {
913         try_call( [&] {
914             internal_copy(il.begin(), il.end(), il.size());
915         }).on_exception( [&] {
916             this->clear();
917         });
918     }
919 
920     concurrent_hash_map( std::initializer_list<value_type> il, const allocator_type& a )
921         : concurrent_hash_map(il, hash_compare_type(), a) {}
922 
923     // Assignment
924     concurrent_hash_map& operator=( const concurrent_hash_map &table ) {
925         if( this != &table ) {
926             clear();
927             copy_assign_allocators(this->my_allocator, table.my_allocator);
928             internal_copy(table);
929         }
930         return *this;
931     }
932 
933     // Move Assignment
934     concurrent_hash_map& operator=( concurrent_hash_map &&table ) {
935         if( this != &table ) {
936             using pocma_type = typename node_allocator_traits::propagate_on_container_move_assignment;
937             using is_equal_type = typename node_allocator_traits::is_always_equal;
938             move_assign_allocators(this->my_allocator, table.my_allocator);
939             internal_move_assign(std::move(table), tbb::detail::disjunction<is_equal_type, pocma_type>());
940         }
941         return *this;
942     }
943 
944     // Assignment
945     concurrent_hash_map& operator=( std::initializer_list<value_type> il ) {
946         clear();
947         internal_copy(il.begin(), il.end(), il.size());
948         return *this;
949     }
950 
951     // Rehashes and optionally resizes the whole table.
952     /** Useful to optimize performance before or after concurrent operations.
953         Also enables using of find() and count() concurrent methods in serial context. */
954     void rehash(size_type sz = 0) {
955         this->reserve(sz); // TODO: add reduction of number of buckets as well
956         hashcode_type mask = this->my_mask.load(std::memory_order_relaxed);
957         hashcode_type b = (mask+1)>>1; // size or first index of the last segment
958         __TBB_ASSERT((b&(b-1))==0, nullptr); // zero or power of 2
959         bucket *bp = this->get_bucket( b ); // only the last segment should be scanned for rehashing
960         for(; b <= mask; b++, bp++ ) {
961             node_base *n = bp->node_list.load(std::memory_order_relaxed);
962             __TBB_ASSERT( this->is_valid(n) || empty_rehashed(n) || rehash_required(n), "Broken internal structure" );
963             __TBB_ASSERT( *reinterpret_cast<intptr_t*>(&bp->mutex) == 0, "concurrent or unexpectedly terminated operation during rehash() execution" );
964             if (rehash_required(n)) { // rehash bucket, conditional because rehashing of a previous bucket may affect this one
965                 hashcode_type h = b; bucket *b_old = bp;
966                 do {
967                     __TBB_ASSERT( h > 1, "The lowermost buckets can't be rehashed" );
968                     hashcode_type m = ( 1u<<tbb::detail::log2( h ) ) - 1; // get parent mask from the topmost bit
969                     b_old = this->get_bucket( h &= m );
970                 } while( rehash_required(b_old->node_list.load(std::memory_order_relaxed)) );
971                 // now h - is index of the root rehashed bucket b_old
972                 this->mark_rehashed_levels( h ); // mark all non-rehashed children recursively across all segments
973                 node_base* prev = nullptr;
974                 node_base* curr = b_old->node_list.load(std::memory_order_relaxed);
975                 while (this->is_valid(curr)) {
976                     hashcode_type curr_node_hash = my_hash_compare.hash(static_cast<node*>(curr)->value().first);
977 
978                     if ((curr_node_hash & mask) != h) { // should be rehashed
979                         node_base* next = curr->next;
980                         // exclude from b_old
981                         if (prev == nullptr) {
982                             b_old->node_list.store(curr->next, std::memory_order_relaxed);
983                         } else {
984                             prev->next = curr->next;
985                         }
986                         bucket *b_new = this->get_bucket(curr_node_hash & mask);
987                         __TBB_ASSERT(!rehash_required(b_new->node_list.load(std::memory_order_relaxed)), "hash() function changed for key in table or internal error");
988                         this->add_to_bucket(b_new, curr);
989                         curr = next;
990                     } else {
991                         prev = curr;
992                         curr = curr->next;
993                     }
994                 }
995             }
996         }
997     }
998 
999     // Clear table
1000     void clear() {
1001         hashcode_type m = this->my_mask.load(std::memory_order_relaxed);
1002         __TBB_ASSERT((m&(m+1))==0, "data structure is invalid");
1003         this->my_size.store(0, std::memory_order_relaxed);
1004         segment_index_type s = this->segment_index_of( m );
1005         __TBB_ASSERT( s+1 == this->pointers_per_table || !this->my_table[s+1].load(std::memory_order_relaxed), "wrong mask or concurrent grow" );
1006         do {
1007             __TBB_ASSERT(this->is_valid(this->my_table[s].load(std::memory_order_relaxed)), "wrong mask or concurrent grow" );
1008             segment_ptr_type buckets_ptr = this->my_table[s].load(std::memory_order_relaxed);
1009             size_type sz = this->segment_size( s ? s : 1 );
1010             for( segment_index_type i = 0; i < sz; i++ )
1011                 for( node_base *n = buckets_ptr[i].node_list.load(std::memory_order_relaxed);
1012                     this->is_valid(n); n = buckets_ptr[i].node_list.load(std::memory_order_relaxed) )
1013                 {
1014                     buckets_ptr[i].node_list.store(n->next, std::memory_order_relaxed);
1015                     delete_node( n );
1016                 }
1017             this->delete_segment(s);
1018         } while(s-- > 0);
1019         this->my_mask.store(this->embedded_buckets - 1, std::memory_order_relaxed);
1020     }
1021 
1022     // Clear table and destroy it.
1023     ~concurrent_hash_map() { clear(); }
1024 
1025     //------------------------------------------------------------------------
1026     // Parallel algorithm support
1027     //------------------------------------------------------------------------
1028     range_type range( size_type grainsize=1 ) {
1029         return range_type( *this, grainsize );
1030     }
1031     const_range_type range( size_type grainsize=1 ) const {
1032         return const_range_type( *this, grainsize );
1033     }
1034 
1035     //------------------------------------------------------------------------
1036     // STL support - not thread-safe methods
1037     //------------------------------------------------------------------------
1038     iterator begin() { return iterator( *this, 0, this->my_embedded_segment, this->my_embedded_segment->node_list.load(std::memory_order_relaxed) ); }
1039     const_iterator begin() const { return const_iterator( *this, 0, this->my_embedded_segment, this->my_embedded_segment->node_list.load(std::memory_order_relaxed) ); }
1040     const_iterator cbegin() const { return const_iterator( *this, 0, this->my_embedded_segment, this->my_embedded_segment->node_list.load(std::memory_order_relaxed) ); }
1041     iterator end() { return iterator( *this, 0, 0, 0 ); }
1042     const_iterator end() const { return const_iterator( *this, 0, 0, 0 ); }
1043     const_iterator cend() const { return const_iterator( *this, 0, 0, 0 ); }
1044     std::pair<iterator, iterator> equal_range( const Key& key ) { return internal_equal_range( key, end() ); }
1045     std::pair<const_iterator, const_iterator> equal_range( const Key& key ) const { return internal_equal_range( key, end() ); }
1046 
1047 #if __TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS
1048     template <typename K>
1049     typename std::enable_if<hash_compare_is_transparent<K>::value,
1050                             std::pair<iterator, iterator>>::type equal_range( const K& key ) {
1051         return internal_equal_range(key, end());
1052     }
1053 
1054     template <typename K>
1055     typename std::enable_if<hash_compare_is_transparent<K>::value,
1056                             std::pair<const_iterator, const_iterator>>::type equal_range( const K& key ) const {
1057         return internal_equal_range(key, end());
1058     }
1059 #endif // __TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS
1060 
1061     // Number of items in table.
1062     size_type size() const { return this->my_size.load(std::memory_order_acquire); }
1063 
1064     // True if size()==0.
1065     __TBB_nodiscard bool empty() const { return size() == 0; }
1066 
1067     // Upper bound on size.
1068     size_type max_size() const {
1069         return allocator_traits_type::max_size(base_type::get_allocator());
1070     }
1071 
1072     // Returns the current number of buckets
1073     size_type bucket_count() const { return this->my_mask.load(std::memory_order_relaxed) + 1; }
1074 
1075     // return allocator object
1076     allocator_type get_allocator() const { return base_type::get_allocator(); }
1077 
1078     // swap two instances. Iterators are invalidated
1079     void swap(concurrent_hash_map& table) {
1080         using pocs_type = typename node_allocator_traits::propagate_on_container_swap;
1081         using is_equal_type = typename node_allocator_traits::is_always_equal;
1082         swap_allocators(this->my_allocator, table.my_allocator);
1083         internal_swap(table, tbb::detail::disjunction<pocs_type, is_equal_type>());
1084     }
1085 
1086     //------------------------------------------------------------------------
1087     // concurrent map operations
1088     //------------------------------------------------------------------------
1089 
1090     // Return count of items (0 or 1)
1091     size_type count( const Key &key ) const {
1092         return const_cast<concurrent_hash_map*>(this)->lookup</*insert*/false>(key, nullptr, nullptr, /*write=*/false, &do_not_allocate_node);
1093     }
1094 
1095 #if __TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS
1096     template <typename K>
1097     typename std::enable_if<hash_compare_is_transparent<K>::value,
1098                             size_type>::type count( const K& key ) const {
1099         return const_cast<concurrent_hash_map*>(this)->lookup</*insert*/false>(key, nullptr, nullptr, /*write=*/false, &do_not_allocate_node);
1100     }
1101 #endif // __TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS
1102 
1103     // Find item and acquire a read lock on the item.
1104     /** Return true if item is found, false otherwise. */
1105     bool find( const_accessor &result, const Key &key ) const {
1106         result.release();
1107         return const_cast<concurrent_hash_map*>(this)->lookup</*insert*/false>(key, nullptr, &result, /*write=*/false, &do_not_allocate_node );
1108     }
1109 
1110     // Find item and acquire a write lock on the item.
1111     /** Return true if item is found, false otherwise. */
1112     bool find( accessor &result, const Key &key ) {
1113         result.release();
1114         return lookup</*insert*/false>(key, nullptr, &result, /*write=*/true, &do_not_allocate_node);
1115     }
1116 
1117 #if __TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS
1118     template <typename K>
1119     typename std::enable_if<hash_compare_is_transparent<K>::value,
1120                             bool>::type find( const_accessor& result, const K& key ) {
1121         result.release();
1122         return lookup</*insert*/false>(key, nullptr, &result, /*write=*/false, &do_not_allocate_node);
1123     }
1124 
1125     template <typename K>
1126     typename std::enable_if<hash_compare_is_transparent<K>::value,
1127                             bool>::type find( accessor& result, const K& key ) {
1128         result.release();
1129         return lookup</*insert*/false>(key, nullptr, &result, /*write=*/true, &do_not_allocate_node);
1130     }
1131 #endif // __TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS
1132 
1133     // Insert item (if not already present) and acquire a read lock on the item.
1134     /** Returns true if item is new. */
1135     bool insert( const_accessor &result, const Key &key ) {
1136         result.release();
1137         return lookup</*insert*/true>(key, nullptr, &result, /*write=*/false, &allocate_node_default_construct<>);
1138     }
1139 
1140     // Insert item (if not already present) and acquire a write lock on the item.
1141     /** Returns true if item is new. */
1142     bool insert( accessor &result, const Key &key ) {
1143         result.release();
1144         return lookup</*insert*/true>(key, nullptr, &result, /*write=*/true, &allocate_node_default_construct<>);
1145     }
1146 
1147 #if __TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS
1148     template <typename K>
1149     typename std::enable_if<hash_compare_is_transparent<K>::value &&
1150                             std::is_constructible<key_type, const K&>::value,
1151                             bool>::type insert( const_accessor& result, const K& key ) {
1152         result.release();
1153         return lookup</*insert*/true>(key, nullptr, &result, /*write=*/true, &allocate_node_default_construct<K>);
1154     }
1155 
1156     template <typename K>
1157     typename std::enable_if<hash_compare_is_transparent<K>::value &&
1158                             std::is_constructible<key_type, const K&>::value,
1159                             bool>::type insert( accessor& result, const K& key ) {
1160         result.release();
1161         return lookup</*insert*/true>(key, nullptr, &result, /*write=*/true, &allocate_node_default_construct<K>);
1162     }
1163 #endif // __TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS
1164 
1165     // Insert item by copying if there is no such key present already and acquire a read lock on the item.
1166     /** Returns true if item is new. */
1167     bool insert( const_accessor &result, const value_type &value ) {
1168         result.release();
1169         return lookup</*insert*/true>(value.first, &value.second, &result, /*write=*/false, &allocate_node_copy_construct);
1170     }
1171 
1172     // Insert item by copying if there is no such key present already and acquire a write lock on the item.
1173     /** Returns true if item is new. */
1174     bool insert( accessor &result, const value_type &value ) {
1175         result.release();
1176         return lookup</*insert*/true>(value.first, &value.second, &result, /*write=*/true, &allocate_node_copy_construct);
1177     }
1178 
1179     // Insert item by copying if there is no such key present already
1180     /** Returns true if item is inserted. */
1181     bool insert( const value_type &value ) {
1182         return lookup</*insert*/true>(value.first, &value.second, nullptr, /*write=*/false, &allocate_node_copy_construct);
1183     }
1184 
1185     // Insert item by copying if there is no such key present already and acquire a read lock on the item.
1186     /** Returns true if item is new. */
1187     bool insert( const_accessor &result, value_type && value ) {
1188         return generic_move_insert(result, std::move(value));
1189     }
1190 
1191     // Insert item by copying if there is no such key present already and acquire a write lock on the item.
1192     /** Returns true if item is new. */
1193     bool insert( accessor &result, value_type && value ) {
1194         return generic_move_insert(result, std::move(value));
1195     }
1196 
1197     // Insert item by copying if there is no such key present already
1198     /** Returns true if item is inserted. */
1199     bool insert( value_type && value ) {
1200         return generic_move_insert(accessor_not_used(), std::move(value));
1201     }
1202 
1203     // Insert item by copying if there is no such key present already and acquire a read lock on the item.
1204     /** Returns true if item is new. */
1205     template <typename... Args>
1206     bool emplace( const_accessor &result, Args&&... args ) {
1207         return generic_emplace(result, std::forward<Args>(args)...);
1208     }
1209 
1210     // Insert item by copying if there is no such key present already and acquire a write lock on the item.
1211     /** Returns true if item is new. */
1212     template <typename... Args>
1213     bool emplace( accessor &result, Args&&... args ) {
1214         return generic_emplace(result, std::forward<Args>(args)...);
1215     }
1216 
1217     // Insert item by copying if there is no such key present already
1218     /** Returns true if item is inserted. */
1219     template <typename... Args>
1220     bool emplace( Args&&... args ) {
1221         return generic_emplace(accessor_not_used(), std::forward<Args>(args)...);
1222     }
1223 
1224     // Insert range [first, last)
1225     template <typename I>
1226     void insert( I first, I last ) {
1227         for ( ; first != last; ++first )
1228             insert( *first );
1229     }
1230 
1231     // Insert initializer list
1232     void insert( std::initializer_list<value_type> il ) {
1233         insert( il.begin(), il.end() );
1234     }
1235 
1236     // Erase item.
1237     /** Return true if item was erased by particularly this call. */
1238     bool erase( const Key &key ) {
1239         return internal_erase(key);
1240     }
1241 
1242 #if __TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS
1243     template <typename K>
1244     typename std::enable_if<hash_compare_is_transparent<K>::value,
1245                             bool>::type erase( const K& key ) {
1246         return internal_erase(key);
1247     }
1248 #endif // __TBB_PREVIEW_CONCURRENT_HASH_MAP_EXTENSIONS
1249 
1250     // Erase item by const_accessor.
1251     /** Return true if item was erased by particularly this call. */
1252     bool erase( const_accessor& item_accessor ) {
1253         return exclude( item_accessor );
1254     }
1255 
1256     // Erase item by accessor.
1257     /** Return true if item was erased by particularly this call. */
1258     bool erase( accessor& item_accessor ) {
1259         return exclude( item_accessor );
1260     }
1261 
1262 protected:
1263     template <typename K, typename AllocateNodeType>
1264     node* allocate_node_helper( const K& key, const T* t, AllocateNodeType allocate_node, std::true_type ) {
1265         return allocate_node(base_type::get_allocator(), key, t);
1266     }
1267 
1268     template <typename K, typename AllocateNodeType>
1269     node* allocate_node_helper( const K&, const T*, AllocateNodeType, std::false_type ) {
1270         __TBB_ASSERT(false, "allocate_node_helper with std::false_type should never been called");
1271         return nullptr;
1272     }
1273 
1274     // Insert or find item and optionally acquire a lock on the item.
1275     template <bool OpInsert, typename K, typename AllocateNodeType>
1276     bool lookup( const K &key, const T *t, const_accessor *result, bool write, AllocateNodeType allocate_node, node *tmp_n  = 0)
1277     {
1278         __TBB_ASSERT( !result || !result->my_node, nullptr );
1279         bool return_value;
1280         hashcode_type const h = my_hash_compare.hash( key );
1281         hashcode_type m = this->my_mask.load(std::memory_order_acquire);
1282         segment_index_type grow_segment = 0;
1283         node *n;
1284         restart:
1285         {//lock scope
1286             __TBB_ASSERT((m&(m+1))==0, "data structure is invalid");
1287             return_value = false;
1288             // get bucket
1289             bucket_accessor b( this, h & m );
1290             // find a node
1291             n = search_bucket( key, b() );
1292             if( OpInsert ) {
1293                 // [opt] insert a key
1294                 if( !n ) {
1295                     if( !tmp_n ) {
1296                         tmp_n = allocate_node_helper(key, t, allocate_node, std::integral_constant<bool, OpInsert>{});
1297                     }
1298                     while ( !b.is_writer() && !b.upgrade_to_writer() ) { // TODO: improved insertion
1299                         // Rerun search list, in case another thread inserted the intem during the upgrade
1300                         n = search_bucket(key, b());
1301                         if (this->is_valid(n)) { // unfortunately, it did
1302                             if (!b.downgrade_to_reader()) {
1303                                 // If the lock was downgraded with reacquiring the mutex
1304                                 // Rerun search list in case another thread removed the item during the downgrade
1305                                 n = search_bucket(key, b());
1306                                 if (!this->is_valid(n)) {
1307                                     // Unfortunately, it did
1308                                     // We need to try upgrading to writer again
1309                                     continue;
1310                                 }
1311                             }
1312                             goto exists;
1313                         }
1314                     }
1315 
1316                     if( this->check_mask_race(h, m) )
1317                         goto restart; // b.release() is done in ~b().
1318                     // insert and set flag to grow the container
1319                     grow_segment = this->insert_new_node( b(), n = tmp_n, m );
1320                     tmp_n = 0;
1321                     return_value = true;
1322                 }
1323             } else { // find or count
1324                 if( !n ) {
1325                     if( this->check_mask_race( h, m ) )
1326                         goto restart; // b.release() is done in ~b(). TODO: replace by continue
1327                     return false;
1328                 }
1329                 return_value = true;
1330             }
1331         exists:
1332             if( !result ) goto check_growth;
1333             // TODO: the following seems as generic/regular operation
1334             // acquire the item
1335             if( !result->try_acquire( n->mutex, write ) ) {
1336                 for( tbb::detail::atomic_backoff backoff(true);; ) {
1337                     if( result->try_acquire( n->mutex, write ) ) break;
1338                     if( !backoff.bounded_pause() ) {
1339                         // the wait takes really long, restart the operation
1340                         b.release();
1341                         __TBB_ASSERT( !OpInsert || !return_value, "Can't acquire new item in locked bucket?" );
1342                         yield();
1343                         m = this->my_mask.load(std::memory_order_acquire);
1344                         goto restart;
1345                     }
1346                 }
1347             }
1348         }//lock scope
1349         result->my_node = n;
1350         result->my_hash = h;
1351     check_growth:
1352         // [opt] grow the container
1353         if( grow_segment ) {
1354             this->enable_segment( grow_segment );
1355         }
1356         if( tmp_n ) // if OpInsert only
1357             delete_node( tmp_n );
1358         return return_value;
1359     }
1360 
1361     struct accessor_not_used { void release(){}};
1362     friend const_accessor* accessor_location( accessor_not_used const& ){ return nullptr;}
1363     friend const_accessor* accessor_location( const_accessor & a )      { return &a;}
1364 
1365     friend bool is_write_access_needed( accessor const& )           { return true;}
1366     friend bool is_write_access_needed( const_accessor const& )     { return false;}
1367     friend bool is_write_access_needed( accessor_not_used const& )  { return false;}
1368 
1369     template <typename Accessor>
1370     bool generic_move_insert( Accessor && result, value_type && value ) {
1371         result.release();
1372         return lookup</*insert*/true>(value.first, &value.second, accessor_location(result), is_write_access_needed(result), &allocate_node_move_construct);
1373     }
1374 
1375     template <typename Accessor, typename... Args>
1376     bool generic_emplace( Accessor && result, Args &&... args ) {
1377         result.release();
1378         node * node_ptr = create_node(base_type::get_allocator(), std::forward<Args>(args)...);
1379         return lookup</*insert*/true>(node_ptr->value().first, nullptr, accessor_location(result), is_write_access_needed(result), &do_not_allocate_node, node_ptr);
1380     }
1381 
1382     // delete item by accessor
1383     bool exclude( const_accessor &item_accessor ) {
1384         __TBB_ASSERT( item_accessor.my_node, nullptr );
1385         node_base *const exclude_node = item_accessor.my_node;
1386         hashcode_type const hash = item_accessor.my_hash;
1387         hashcode_type mask = this->my_mask.load(std::memory_order_acquire);
1388         do {
1389             // get bucket
1390             bucket_accessor b( this, hash & mask, /*writer=*/true );
1391             node_base* prev = nullptr;
1392             node_base* curr = b()->node_list.load(std::memory_order_relaxed);
1393 
1394             while (curr && curr != exclude_node) {
1395                 prev = curr;
1396                 curr = curr->next;
1397             }
1398 
1399             if (curr == nullptr) { // someone else was first
1400                 if (this->check_mask_race(hash, mask))
1401                     continue;
1402                 item_accessor.release();
1403                 return false;
1404             }
1405             __TBB_ASSERT( curr == exclude_node, nullptr );
1406             // remove from container
1407             if (prev == nullptr) {
1408                 b()->node_list.store(curr->next, std::memory_order_relaxed);
1409             } else {
1410                 prev->next = curr->next;
1411             }
1412 
1413             this->my_size--;
1414             break;
1415         } while(true);
1416         if (!item_accessor.is_writer()) { // need to get exclusive lock
1417             item_accessor.upgrade_to_writer(); // return value means nothing here
1418         }
1419 
1420         item_accessor.release();
1421         delete_node(exclude_node); // Only one thread can delete it
1422         return true;
1423     }
1424 
1425     template <typename K>
1426     bool internal_erase( const K& key ) {
1427         node_base *erase_node;
1428         hashcode_type const hash = my_hash_compare.hash(key);
1429         hashcode_type mask = this->my_mask.load(std::memory_order_acquire);
1430     restart:
1431         {//lock scope
1432             // get bucket
1433             bucket_accessor b( this, hash & mask );
1434         search:
1435             node_base* prev = nullptr;
1436             erase_node = b()->node_list.load(std::memory_order_relaxed);
1437             while (this->is_valid(erase_node) && !my_hash_compare.equal(key, static_cast<node*>(erase_node)->value().first ) ) {
1438                 prev = erase_node;
1439                 erase_node = erase_node->next;
1440             }
1441 
1442             if (erase_node == nullptr) { // not found, but mask could be changed
1443                 if (this->check_mask_race(hash, mask))
1444                     goto restart;
1445                 return false;
1446             } else if (!b.is_writer() && !b.upgrade_to_writer()) {
1447                 if (this->check_mask_race(hash, mask)) // contended upgrade, check mask
1448                     goto restart;
1449                 goto search;
1450             }
1451 
1452             // remove from container
1453             if (prev == nullptr) {
1454                 b()->node_list.store(erase_node->next, std::memory_order_relaxed);
1455             } else {
1456                 prev->next = erase_node->next;
1457             }
1458             this->my_size--;
1459         }
1460         {
1461             typename node::scoped_type item_locker( erase_node->mutex, /*write=*/true );
1462         }
1463         // note: there should be no threads pretending to acquire this mutex again, do not try to upgrade const_accessor!
1464         delete_node(erase_node); // Only one thread can delete it due to write lock on the bucket
1465         return true;
1466     }
1467 
1468     // Returns an iterator for an item defined by the key, or for the next item after it (if upper==true)
1469     template <typename K, typename I>
1470     std::pair<I, I> internal_equal_range( const K& key, I end_ ) const {
1471         hashcode_type h = my_hash_compare.hash( key );
1472         hashcode_type m = this->my_mask.load(std::memory_order_relaxed);
1473         __TBB_ASSERT((m&(m+1))==0, "data structure is invalid");
1474         h &= m;
1475         bucket *b = this->get_bucket( h );
1476         while (rehash_required(b->node_list.load(std::memory_order_relaxed))) {
1477             m = ( 1u<<tbb::detail::log2( h ) ) - 1; // get parent mask from the topmost bit
1478             b = this->get_bucket( h &= m );
1479         }
1480         node *n = search_bucket( key, b );
1481         if( !n )
1482             return std::make_pair(end_, end_);
1483         iterator lower(*this, h, b, n), upper(lower);
1484         return std::make_pair(lower, ++upper);
1485     }
1486 
1487     // Copy "source" to *this, where *this must start out empty.
1488     void internal_copy( const concurrent_hash_map& source ) {
1489         hashcode_type mask = source.my_mask.load(std::memory_order_relaxed);
1490         if( this->my_mask.load(std::memory_order_relaxed) == mask ) { // optimized version
1491             this->reserve(source.my_size.load(std::memory_order_relaxed)); // TODO: load_factor?
1492             bucket *dst = 0, *src = 0;
1493             bool rehashing_required = false;
1494             for( hashcode_type k = 0; k <= mask; k++ ) {
1495                 if( k & (k-2) ) ++dst,src++; // not the beginning of a segment
1496                 else { dst = this->get_bucket( k ); src = source.get_bucket( k ); }
1497                 __TBB_ASSERT(!rehash_required(dst->node_list.load(std::memory_order_relaxed)), "Invalid bucket in destination table");
1498                 node *n = static_cast<node*>( src->node_list.load(std::memory_order_relaxed) );
1499                 if (rehash_required(n)) { // source is not rehashed, items are in previous buckets
1500                     rehashing_required = true;
1501                     dst->node_list.store(reinterpret_cast<node_base*>(rehash_req_flag), std::memory_order_relaxed);
1502                 } else for(; n; n = static_cast<node*>( n->next ) ) {
1503                     node* node_ptr = create_node(base_type::get_allocator(), n->value().first, n->value().second);
1504                     this->add_to_bucket( dst, node_ptr);
1505                     this->my_size.fetch_add(1, std::memory_order_relaxed);
1506                 }
1507             }
1508             if( rehashing_required ) rehash();
1509         } else internal_copy(source.begin(), source.end(), source.my_size.load(std::memory_order_relaxed));
1510     }
1511 
1512     template <typename I>
1513     void internal_copy( I first, I last, size_type reserve_size ) {
1514         this->reserve(reserve_size); // TODO: load_factor?
1515         hashcode_type m = this->my_mask.load(std::memory_order_relaxed);
1516         for(; first != last; ++first) {
1517             hashcode_type h = my_hash_compare.hash( (*first).first );
1518             bucket *b = this->get_bucket( h & m );
1519             __TBB_ASSERT(!rehash_required(b->node_list.load(std::memory_order_relaxed)), "Invalid bucket in destination table");
1520             node* node_ptr = create_node(base_type::get_allocator(), (*first).first, (*first).second);
1521             this->add_to_bucket( b, node_ptr );
1522             ++this->my_size; // TODO: replace by non-atomic op
1523         }
1524     }
1525 
1526     void internal_move_construct_with_allocator( concurrent_hash_map&& other, const allocator_type&,
1527                                                 /*is_always_equal=*/std::true_type )
1528     {
1529         this->internal_move(std::move(other));
1530     }
1531 
1532     void internal_move_construct_with_allocator( concurrent_hash_map&& other, const allocator_type& a,
1533                                                 /*is_always_equal=*/std::false_type )
1534     {
1535         if (a == other.get_allocator()){
1536             this->internal_move(std::move(other));
1537         } else {
1538             try_call( [&] {
1539                 internal_copy(std::make_move_iterator(other.begin()), std::make_move_iterator(other.end()),
1540                     other.size());
1541             }).on_exception( [&] {
1542                 this->clear();
1543             });
1544         }
1545     }
1546 
1547     void internal_move_assign( concurrent_hash_map&& other,
1548         /*is_always_equal || POCMA = */std::true_type)
1549     {
1550         this->internal_move(std::move(other));
1551     }
1552 
1553     void internal_move_assign(concurrent_hash_map&& other, /*is_always_equal=*/ std::false_type) {
1554         if (this->my_allocator == other.my_allocator) {
1555             this->internal_move(std::move(other));
1556         } else {
1557             //do per element move
1558             internal_copy(std::make_move_iterator(other.begin()), std::make_move_iterator(other.end()),
1559                 other.size());
1560         }
1561     }
1562 
1563     void internal_swap(concurrent_hash_map& other, /*is_always_equal || POCS = */ std::true_type) {
1564         this->internal_swap_content(other);
1565     }
1566 
1567     void internal_swap(concurrent_hash_map& other, /*is_always_equal || POCS = */ std::false_type) {
1568         __TBB_ASSERT(this->my_allocator == other.my_allocator, nullptr);
1569         this->internal_swap_content(other);
1570     }
1571 
1572     // Fast find when no concurrent erasure is used. For internal use inside TBB only!
1573     /** Return pointer to item with given key, or nullptr if no such item exists.
1574         Must not be called concurrently with erasure operations. */
1575     const_pointer internal_fast_find( const Key& key ) const {
1576         hashcode_type h = my_hash_compare.hash( key );
1577         hashcode_type m = this->my_mask.load(std::memory_order_acquire);
1578         node *n;
1579     restart:
1580         __TBB_ASSERT((m&(m+1))==0, "data structure is invalid");
1581         bucket *b = this->get_bucket( h & m );
1582         // TODO: actually, notification is unnecessary here, just hiding double-check
1583         if (rehash_required(b->node_list.load(std::memory_order_acquire)))
1584         {
1585             typename bucket::scoped_type lock;
1586             if( lock.try_acquire( b->mutex, /*write=*/true ) ) {
1587                 if (rehash_required(b->node_list.load(std::memory_order_relaxed)))
1588                     const_cast<concurrent_hash_map*>(this)->rehash_bucket( b, h & m ); //recursive rehashing
1589             }
1590             else lock.acquire( b->mutex, /*write=*/false );
1591             __TBB_ASSERT(!rehash_required(b->node_list.load(std::memory_order_relaxed)), nullptr);
1592         }
1593         n = search_bucket( key, b );
1594         if( n )
1595             return n->storage();
1596         else if( this->check_mask_race( h, m ) )
1597             goto restart;
1598         return 0;
1599     }
1600 };
1601 
1602 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
1603 template <typename It,
1604           typename HashCompare = d1::tbb_hash_compare<iterator_key_t<It>>,
1605           typename Alloc = tbb_allocator<iterator_alloc_pair_t<It>>,
1606           typename = std::enable_if_t<is_input_iterator_v<It>>,
1607           typename = std::enable_if_t<is_allocator_v<Alloc>>,
1608           typename = std::enable_if_t<!is_allocator_v<HashCompare>>>
1609 concurrent_hash_map( It, It, HashCompare = HashCompare(), Alloc = Alloc() )
1610 -> concurrent_hash_map<iterator_key_t<It>, iterator_mapped_t<It>, HashCompare, Alloc>;
1611 
1612 template <typename It, typename Alloc,
1613           typename = std::enable_if_t<is_input_iterator_v<It>>,
1614           typename = std::enable_if_t<is_allocator_v<Alloc>>>
1615 concurrent_hash_map( It, It, Alloc )
1616 -> concurrent_hash_map<iterator_key_t<It>, iterator_mapped_t<It>, d1::tbb_hash_compare<iterator_key_t<It>>, Alloc>;
1617 
1618 template <typename Key, typename T,
1619           typename HashCompare = d1::tbb_hash_compare<std::remove_const_t<Key>>,
1620           typename Alloc = tbb_allocator<std::pair<const Key, T>>,
1621           typename = std::enable_if_t<is_allocator_v<Alloc>>,
1622           typename = std::enable_if_t<!is_allocator_v<HashCompare>>>
1623 concurrent_hash_map( std::initializer_list<std::pair<Key, T>>, HashCompare = HashCompare(), Alloc = Alloc() )
1624 -> concurrent_hash_map<std::remove_const_t<Key>, T, HashCompare, Alloc>;
1625 
1626 template <typename Key, typename T, typename Alloc,
1627           typename = std::enable_if_t<is_allocator_v<Alloc>>>
1628 concurrent_hash_map( std::initializer_list<std::pair<Key, T>>, Alloc )
1629 -> concurrent_hash_map<std::remove_const_t<Key>, T, d1::tbb_hash_compare<std::remove_const_t<Key>>, Alloc>;
1630 
1631 #endif /* __TBB_CPP17_DEDUCTION_GUIDES_PRESENT */
1632 
1633 template <typename Key, typename T, typename HashCompare, typename A1, typename A2>
1634 inline bool operator==(const concurrent_hash_map<Key, T, HashCompare, A1> &a, const concurrent_hash_map<Key, T, HashCompare, A2> &b) {
1635     if(a.size() != b.size()) return false;
1636     typename concurrent_hash_map<Key, T, HashCompare, A1>::const_iterator i(a.begin()), i_end(a.end());
1637     typename concurrent_hash_map<Key, T, HashCompare, A2>::const_iterator j, j_end(b.end());
1638     for(; i != i_end; ++i) {
1639         j = b.equal_range(i->first).first;
1640         if( j == j_end || !(i->second == j->second) ) return false;
1641     }
1642     return true;
1643 }
1644 
1645 #if !__TBB_CPP20_COMPARISONS_PRESENT
1646 template <typename Key, typename T, typename HashCompare, typename A1, typename A2>
1647 inline bool operator!=(const concurrent_hash_map<Key, T, HashCompare, A1> &a, const concurrent_hash_map<Key, T, HashCompare, A2> &b)
1648 {    return !(a == b); }
1649 #endif // !__TBB_CPP20_COMPARISONS_PRESENT
1650 
1651 template <typename Key, typename T, typename HashCompare, typename A>
1652 inline void swap(concurrent_hash_map<Key, T, HashCompare, A> &a, concurrent_hash_map<Key, T, HashCompare, A> &b)
1653 {    a.swap( b ); }
1654 
1655 } // namespace d2
1656 } // namespace detail
1657 
1658 inline namespace v1 {
1659     using detail::split;
1660     using detail::d2::concurrent_hash_map;
1661     using detail::d1::tbb_hash_compare;
1662 } // namespace v1
1663 
1664 } // namespace tbb
1665 
1666 #endif /* __TBB_concurrent_hash_map_H */
1667