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