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_detail__concurrent_unordered_base_H
18 #define __TBB_detail__concurrent_unordered_base_H
19 
20 #if !defined(__TBB_concurrent_unordered_map_H) && !defined(__TBB_concurrent_unordered_set_H)
21 #error Do not #include this internal file directly; use public TBB headers instead.
22 #endif
23 
24 #include "_range_common.h"
25 #include "_containers_helpers.h"
26 #include "_segment_table.h"
27 #include "_hash_compare.h"
28 #include "_allocator_traits.h"
29 #include "_node_handle.h"
30 #include "_assert.h"
31 #include "_utils.h"
32 #include "_exception.h"
33 #include <iterator>
34 #include <utility>
35 #include <functional>
36 #include <initializer_list>
37 #include <atomic>
38 #include <type_traits>
39 #include <memory>
40 #include <algorithm>
41 
42 #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
43 #pragma warning(push)
44 #pragma warning(disable: 4127) // warning C4127: conditional expression is constant
45 #endif
46 
47 namespace tbb {
48 namespace detail {
49 namespace d1 {
50 
51 template <typename Traits>
52 class concurrent_unordered_base;
53 
54 template<typename Container, typename Value>
55 class solist_iterator {
56 private:
57     using node_ptr = typename Container::value_node_ptr;
58     template <typename T, typename Allocator>
59     friend class split_ordered_list;
60     template<typename M, typename V>
61     friend class solist_iterator;
62     template <typename Traits>
63     friend class concurrent_unordered_base;
64     template<typename M, typename T, typename U>
65     friend bool operator==( const solist_iterator<M,T>& i, const solist_iterator<M,U>& j );
66     template<typename M, typename T, typename U>
67     friend bool operator!=( const solist_iterator<M,T>& i, const solist_iterator<M,U>& j );
68 public:
69     using value_type = Value;
70     using difference_type = typename Container::difference_type;
71     using pointer = value_type*;
72     using reference = value_type&;
73     using iterator_category = std::forward_iterator_tag;
74 
75     solist_iterator() : my_node_ptr(nullptr) {}
76     solist_iterator( const solist_iterator<Container, typename Container::value_type>& other )
77         : my_node_ptr(other.my_node_ptr) {}
78 
79     solist_iterator& operator=( const solist_iterator<Container, typename Container::value_type>& other ) {
80         my_node_ptr = other.my_node_ptr;
81         return *this;
82     }
83 
84     reference operator*() const {
85         return my_node_ptr->value();
86     }
87 
88     pointer operator->() const {
89         return my_node_ptr->storage();
90     }
91 
92     solist_iterator& operator++() {
93         auto next_node = my_node_ptr->next();
94         while(next_node && next_node->is_dummy()) {
95             next_node = next_node->next();
96         }
97         my_node_ptr = static_cast<node_ptr>(next_node);
98         return *this;
99     }
100 
101     solist_iterator operator++(int) {
102         solist_iterator tmp = *this;
103         ++*this;
104         return tmp;
105     }
106 
107 private:
108     solist_iterator( node_ptr pnode ) : my_node_ptr(pnode) {}
109 
110     node_ptr get_node_ptr() const { return my_node_ptr; }
111 
112     node_ptr my_node_ptr;
113 };
114 
115 template<typename Solist, typename T, typename U>
116 bool operator==( const solist_iterator<Solist, T>& i, const solist_iterator<Solist, U>& j ) {
117     return i.my_node_ptr == j.my_node_ptr;
118 }
119 
120 template<typename Solist, typename T, typename U>
121 bool operator!=( const solist_iterator<Solist, T>& i, const solist_iterator<Solist, U>& j ) {
122     return i.my_node_ptr != j.my_node_ptr;
123 }
124 
125 template <typename SokeyType>
126 class list_node {
127 public:
128     using node_ptr = list_node*;
129     using sokey_type = SokeyType;
130 
131     list_node(sokey_type key) : my_next(nullptr), my_order_key(key) {}
132 
133     void init( sokey_type key ) {
134         my_order_key = key;
135     }
136 
137     sokey_type order_key() const {
138         return my_order_key;
139     }
140 
141     bool is_dummy() {
142         // The last bit of order key is unset for dummy nodes
143         return (my_order_key & 0x1) == 0;
144     }
145 
146     node_ptr next() const {
147         return my_next.load(std::memory_order_acquire);
148     }
149 
150     void set_next( node_ptr next_node ) {
151         my_next.store(next_node, std::memory_order_release);
152     }
153 
154     bool try_set_next( node_ptr expected_next, node_ptr new_next ) {
155         return my_next.compare_exchange_strong(expected_next, new_next);
156     }
157 
158 private:
159     std::atomic<node_ptr> my_next;
160     sokey_type my_order_key;
161 }; // class list_node
162 
163 template <typename ValueType, typename SokeyType>
164 class value_node : public list_node<SokeyType>
165 {
166 public:
167     using base_type = list_node<SokeyType>;
168     using sokey_type = typename base_type::sokey_type;
169     using value_type = ValueType;
170 
171     value_node( sokey_type ord_key ) : base_type(ord_key) {}
172     ~value_node() {}
173     value_type* storage() {
174         return reinterpret_cast<value_type*>(&my_value);
175     }
176 
177     value_type& value() {
178         return *storage();
179     }
180 
181 private:
182     using aligned_storage_type = typename std::aligned_storage<sizeof(value_type)>::type;
183     aligned_storage_type my_value;
184 }; // class value_node
185 
186 template <typename Traits>
187 class concurrent_unordered_base {
188     using self_type = concurrent_unordered_base<Traits>;
189     using traits_type = Traits;
190     using hash_compare_type = typename traits_type::hash_compare_type;
191     class unordered_segment_table;
192 public:
193     using value_type = typename traits_type::value_type;
194     using key_type = typename traits_type::key_type;
195     using allocator_type = typename traits_type::allocator_type;
196 
197 private:
198     using allocator_traits_type = tbb::detail::allocator_traits<allocator_type>;
199     // TODO: check assert conditions for different C++ standards
200     static_assert(std::is_same<typename allocator_traits_type::value_type, value_type>::value,
201                   "value_type of the container must be the same as its allocator");
202     using sokey_type = std::size_t;
203 
204 public:
205     using size_type = std::size_t;
206     using difference_type = std::ptrdiff_t;
207 
208     using iterator = solist_iterator<self_type, value_type>;
209     using const_iterator = solist_iterator<self_type, const value_type>;
210     using local_iterator = iterator;
211     using const_local_iterator = const_iterator;
212 
213     using reference = value_type&;
214     using const_reference = const value_type&;
215     using pointer = typename allocator_traits_type::pointer;
216     using const_pointer = typename allocator_traits_type::const_pointer;
217 
218     using hasher = typename hash_compare_type::hasher;
219     using key_equal = typename hash_compare_type::key_equal;
220 
221 private:
222     using list_node_type = list_node<sokey_type>;
223     using value_node_type = value_node<value_type, sokey_type>;
224     using node_ptr = list_node_type*;
225     using value_node_ptr = value_node_type*;
226 
227     using value_node_allocator_type = typename allocator_traits_type::template rebind_alloc<value_node_type>;
228     using node_allocator_type = typename allocator_traits_type::template rebind_alloc<list_node_type>;
229 
230     using node_allocator_traits = tbb::detail::allocator_traits<node_allocator_type>;
231     using value_node_allocator_traits = tbb::detail::allocator_traits<value_node_allocator_type>;
232 
233     static constexpr size_type round_up_to_power_of_two( size_type bucket_count ) {
234         return size_type(1) << size_type(tbb::detail::log2(uintptr_t(bucket_count == 0 ? 1 : bucket_count) * 2 - 1));
235     }
236 
237     template <typename T>
238     using is_transparent = dependent_bool<has_transparent_key_equal<key_type, hasher, key_equal>, T>;
239 public:
240     using node_type = node_handle<key_type, value_type, value_node_type, allocator_type>;
241 
242     explicit concurrent_unordered_base( size_type bucket_count, const hasher& hash = hasher(),
243                                         const key_equal& equal = key_equal(), const allocator_type& alloc = allocator_type() )
244         : my_size(0),
245           my_bucket_count(round_up_to_power_of_two(bucket_count)),
246           my_max_load_factor(float(initial_max_load_factor)),
247           my_hash_compare(hash, equal),
248           my_head(sokey_type(0)),
249           my_segments(alloc) {}
250 
251     concurrent_unordered_base() : concurrent_unordered_base(initial_bucket_count) {}
252 
253     concurrent_unordered_base( size_type bucket_count, const allocator_type& alloc )
254         : concurrent_unordered_base(bucket_count, hasher(), key_equal(), alloc) {}
255 
256     concurrent_unordered_base( size_type bucket_count, const hasher& hash, const allocator_type& alloc )
257         : concurrent_unordered_base(bucket_count, hash, key_equal(), alloc) {}
258 
259     explicit concurrent_unordered_base( const allocator_type& alloc )
260         : concurrent_unordered_base(initial_bucket_count, hasher(), key_equal(), alloc) {}
261 
262     template <typename InputIterator>
263     concurrent_unordered_base( InputIterator first, InputIterator last,
264                                size_type bucket_count = initial_bucket_count, const hasher& hash = hasher(),
265                                const key_equal& equal = key_equal(), const allocator_type& alloc = allocator_type() )
266         : concurrent_unordered_base(bucket_count, hash, equal, alloc)
267     {
268         insert(first, last);
269     }
270 
271     template <typename InputIterator>
272     concurrent_unordered_base( InputIterator first, InputIterator last,
273                                size_type bucket_count, const allocator_type& alloc )
274         : concurrent_unordered_base(first, last, bucket_count, hasher(), key_equal(), alloc) {}
275 
276     template <typename InputIterator>
277     concurrent_unordered_base( InputIterator first, InputIterator last,
278                                size_type bucket_count, const hasher& hash, const allocator_type& alloc )
279         : concurrent_unordered_base(first, last, bucket_count, hash, key_equal(), alloc) {}
280 
281     concurrent_unordered_base( const concurrent_unordered_base& other )
282         : my_size(other.my_size.load(std::memory_order_relaxed)),
283           my_bucket_count(other.my_bucket_count.load(std::memory_order_relaxed)),
284           my_max_load_factor(other.my_max_load_factor),
285           my_hash_compare(other.my_hash_compare),
286           my_head(other.my_head.order_key()),
287           my_segments(other.my_segments)
288     {
289         try_call( [&] {
290             internal_copy(other);
291         } ).on_exception( [&] {
292             clear();
293         });
294     }
295 
296     concurrent_unordered_base( const concurrent_unordered_base& other, const allocator_type& alloc )
297         : my_size(other.my_size.load(std::memory_order_relaxed)),
298           my_bucket_count(other.my_bucket_count.load(std::memory_order_relaxed)),
299           my_max_load_factor(other.my_max_load_factor),
300           my_hash_compare(other.my_hash_compare),
301           my_head(other.my_head.order_key()),
302           my_segments(other.my_segments, alloc)
303     {
304         try_call( [&] {
305             internal_copy(other);
306         } ).on_exception( [&] {
307             clear();
308         });
309     }
310 
311     concurrent_unordered_base( concurrent_unordered_base&& other )
312         : my_size(other.my_size.load(std::memory_order_relaxed)),
313           my_bucket_count(other.my_bucket_count.load(std::memory_order_relaxed)),
314           my_max_load_factor(std::move(other.my_max_load_factor)),
315           my_hash_compare(std::move(other.my_hash_compare)),
316           my_head(other.my_head.order_key()),
317           my_segments(std::move(other.my_segments))
318     {
319         move_content(std::move(other));
320     }
321 
322     concurrent_unordered_base( concurrent_unordered_base&& other, const allocator_type& alloc )
323         : my_size(other.my_size.load(std::memory_order_relaxed)),
324           my_bucket_count(other.my_bucket_count.load(std::memory_order_relaxed)),
325           my_max_load_factor(std::move(other.my_max_load_factor)),
326           my_hash_compare(std::move(other.my_hash_compare)),
327           my_head(other.my_head.order_key()),
328           my_segments(std::move(other.my_segments), alloc)
329     {
330         using is_always_equal = typename allocator_traits_type::is_always_equal;
331         internal_move_construct_with_allocator(std::move(other), alloc, is_always_equal());
332     }
333 
334     concurrent_unordered_base( std::initializer_list<value_type> init,
335                                size_type bucket_count = initial_bucket_count,
336                                const hasher& hash = hasher(), const key_equal& equal = key_equal(),
337                                const allocator_type& alloc = allocator_type() )
338         : concurrent_unordered_base(init.begin(), init.end(), bucket_count, hash, equal, alloc) {}
339 
340     concurrent_unordered_base( std::initializer_list<value_type> init,
341                                size_type bucket_count, const allocator_type& alloc )
342         : concurrent_unordered_base(init, bucket_count, hasher(), key_equal(), alloc) {}
343 
344     concurrent_unordered_base( std::initializer_list<value_type> init,
345                                size_type bucket_count, const hasher& hash, const allocator_type& alloc )
346         : concurrent_unordered_base(init, bucket_count, hash, key_equal(), alloc) {}
347 
348     ~concurrent_unordered_base() {
349         internal_clear();
350     }
351 
352     concurrent_unordered_base& operator=( const concurrent_unordered_base& other ) {
353         if (this != &other) {
354             clear();
355             my_size.store(other.my_size.load(std::memory_order_relaxed), std::memory_order_relaxed);
356             my_bucket_count.store(other.my_bucket_count.load(std::memory_order_relaxed), std::memory_order_relaxed);
357             my_max_load_factor = other.my_max_load_factor;
358             my_hash_compare = other.my_hash_compare;
359             my_segments = other.my_segments;
360             internal_copy(other); // TODO: guards for exceptions?
361         }
362         return *this;
363     }
364 
365     concurrent_unordered_base& operator=( concurrent_unordered_base&& other ) noexcept(unordered_segment_table::is_noexcept_assignment) {
366         if (this != &other) {
367             clear();
368             my_size.store(other.my_size.load(std::memory_order_relaxed), std::memory_order_relaxed);
369             my_bucket_count.store(other.my_bucket_count.load(std::memory_order_relaxed), std::memory_order_relaxed);
370             my_max_load_factor = std::move(other.my_max_load_factor);
371             my_hash_compare = std::move(other.my_hash_compare);
372             my_segments = std::move(other.my_segments);
373 
374             using pocma_type = typename allocator_traits_type::propagate_on_container_move_assignment;
375             using is_always_equal = typename allocator_traits_type::is_always_equal;
376             internal_move_assign(std::move(other), tbb::detail::disjunction<pocma_type, is_always_equal>());
377         }
378         return *this;
379     }
380 
381     concurrent_unordered_base& operator=( std::initializer_list<value_type> init ) {
382         clear();
383         insert(init);
384         return *this;
385     }
386 
387     void swap( concurrent_unordered_base& other ) noexcept(unordered_segment_table::is_noexcept_swap) {
388         if (this != &other) {
389             using pocs_type = typename allocator_traits_type::propagate_on_container_swap;
390             using is_always_equal = typename allocator_traits_type::is_always_equal;
391             internal_swap(other, tbb::detail::disjunction<pocs_type, is_always_equal>());
392         }
393     }
394 
395     allocator_type get_allocator() const noexcept { return my_segments.get_allocator(); }
396 
397     iterator begin() noexcept { return iterator(first_value_node(&my_head)); }
398     const_iterator begin() const noexcept { return const_iterator(first_value_node(const_cast<node_ptr>(&my_head))); }
399     const_iterator cbegin() const noexcept { return const_iterator(first_value_node(const_cast<node_ptr>(&my_head))); }
400 
401     iterator end() noexcept { return iterator(nullptr); }
402     const_iterator end() const noexcept { return const_iterator(nullptr); }
403     const_iterator cend() const noexcept { return const_iterator(nullptr); }
404 
405     __TBB_nodiscard bool empty() const noexcept { return size() == 0; }
406     size_type size() const noexcept { return my_size.load(std::memory_order_relaxed); }
407     size_type max_size() const noexcept { return allocator_traits_type::max_size(get_allocator()); }
408 
409     void clear() noexcept {
410         internal_clear();
411     }
412 
413     std::pair<iterator, bool> insert( const value_type& value ) {
414         return internal_insert_value(value);
415     }
416 
417     std::pair<iterator, bool> insert( value_type&& value ) {
418         return internal_insert_value(std::move(value));
419     }
420 
421     iterator insert( const_iterator, const value_type& value ) {
422         // Ignore hint
423         return insert(value).first;
424     }
425 
426     iterator insert( const_iterator, value_type&& value ) {
427         // Ignore hint
428         return insert(std::move(value)).first;
429     }
430 
431     template <typename InputIterator>
432     void insert( InputIterator first, InputIterator last ) {
433         for (; first != last; ++first) {
434             insert(*first);
435         }
436     }
437 
438     void insert( std::initializer_list<value_type> init ) {
439         insert(init.begin(), init.end());
440     }
441 
442     std::pair<iterator, bool> insert( node_type&& nh ) {
443         if (!nh.empty()) {
444             value_node_ptr insert_node = node_handle_accessor::get_node_ptr(nh);
445             auto init_node = [&insert_node]( sokey_type order_key )->value_node_ptr {
446                 insert_node->init(order_key);
447                 return insert_node;
448             };
449             auto insert_result = internal_insert(insert_node->value(), init_node);
450             if (insert_result.inserted) {
451                 // If the insertion succeeded - set node handle to the empty state
452                 __TBB_ASSERT(insert_result.remaining_node == nullptr,
453                             "internal_insert_node should not return the remaining node if the insertion succeeded");
454                 node_handle_accessor::deactivate(nh);
455             }
456             return { iterator(insert_result.node_with_equal_key), insert_result.inserted };
457         }
458         return {end(), false};
459     }
460 
461     iterator insert( const_iterator, node_type&& nh ) {
462         // Ignore hint
463         return insert(std::move(nh)).first;
464     }
465 
466     template <typename... Args>
467     std::pair<iterator, bool> emplace( Args&&... args ) {
468         // Create a node with temporary order_key 0, which will be reinitialize
469         // in internal_insert after the hash calculation
470         value_node_ptr insert_node = create_node(0, std::forward<Args>(args)...);
471 
472         auto init_node = [&insert_node]( sokey_type order_key )->value_node_ptr {
473             insert_node->init(order_key);
474             return insert_node;
475         };
476 
477         auto insert_result = internal_insert(insert_node->value(), init_node);
478 
479         if (!insert_result.inserted) {
480             // If the insertion failed - destroy the node which was created
481             insert_node->init(split_order_key_regular(1));
482             destroy_node(insert_node);
483         }
484 
485         return { iterator(insert_result.node_with_equal_key), insert_result.inserted };
486     }
487 
488     template <typename... Args>
489     iterator emplace_hint( const_iterator, Args&&... args ) {
490         // Ignore hint
491         return emplace(std::forward<Args>(args)...).first;
492     }
493 
494     iterator unsafe_erase( const_iterator pos ) {
495         return iterator(first_value_node(internal_erase(pos.get_node_ptr())));
496     }
497 
498     iterator unsafe_erase( iterator pos ) {
499         return iterator(first_value_node(internal_erase(pos.get_node_ptr())));
500     }
501 
502     iterator unsafe_erase( const_iterator first, const_iterator last ) {
503         while(first != last) {
504             first = unsafe_erase(first);
505         }
506         return iterator(first.get_node_ptr());
507     }
508 
509     size_type unsafe_erase( const key_type& key ) {
510         return internal_erase_by_key(key);
511     }
512 
513     template <typename K>
514     typename std::enable_if<is_transparent<K>::value
515                             && !std::is_convertible<K, const_iterator>::value
516                             && !std::is_convertible<K, iterator>::value,
517                             size_type>::type unsafe_erase( const K& key )
518     {
519         return internal_erase_by_key(key);
520     }
521 
522     node_type unsafe_extract( const_iterator pos ) {
523         internal_extract(pos.get_node_ptr());
524         return node_handle_accessor::construct<node_type>(pos.get_node_ptr());
525     }
526 
527     node_type unsafe_extract( iterator pos ) {
528         internal_extract(pos.get_node_ptr());
529         return node_handle_accessor::construct<node_type>(pos.get_node_ptr());
530     }
531 
532     node_type unsafe_extract( const key_type& key ) {
533         iterator item = find(key);
534         return item == end() ? node_type() : unsafe_extract(item);
535     }
536 
537     template <typename K>
538     typename std::enable_if<is_transparent<K>::value
539                             && !std::is_convertible<K, const_iterator>::value
540                             && !std::is_convertible<K, iterator>::value,
541                             node_type>::type unsafe_extract( const K& key )
542     {
543         iterator item = find(key);
544         return item == end() ? node_type() : unsafe_extract(item);
545     }
546 
547     // Lookup functions
548     iterator find( const key_type& key ) {
549         value_node_ptr result = internal_find(key);
550         return result == nullptr ? end() : iterator(result);
551     }
552 
553     const_iterator find( const key_type& key ) const {
554         value_node_ptr result = const_cast<self_type*>(this)->internal_find(key);
555         return result == nullptr ? end() : const_iterator(result);
556     }
557 
558     template <typename K>
559     typename std::enable_if<is_transparent<K>::value, iterator>::type find( const K& key ) {
560         value_node_ptr result = internal_find(key);
561         return result == nullptr ? end() : iterator(result);
562     }
563 
564     template <typename K>
565     typename std::enable_if<is_transparent<K>::value, const_iterator>::type find( const K& key ) const {
566         value_node_ptr result = const_cast<self_type*>(this)->internal_find(key);
567         return result == nullptr ? end() : const_iterator(result);
568     }
569 
570     std::pair<iterator, iterator> equal_range( const key_type& key ) {
571         auto result = internal_equal_range(key);
572         return std::make_pair(iterator(result.first), iterator(result.second));
573     }
574 
575     std::pair<const_iterator, const_iterator> equal_range( const key_type& key ) const {
576         auto result = const_cast<self_type*>(this)->internal_equal_range(key);
577         return std::make_pair(const_iterator(result.first), const_iterator(result.second));
578     }
579 
580     template <typename K>
581     typename std::enable_if<is_transparent<K>::value, std::pair<iterator, iterator>>::type equal_range( const K& key ) {
582         auto result = internal_equal_range(key);
583         return std::make_pair(iterator(result.first), iterator(result.second));
584     }
585 
586     template <typename K>
587     typename std::enable_if<is_transparent<K>::value, std::pair<const_iterator, const_iterator>>::type equal_range( const K& key ) const {
588         auto result = const_cast<self_type*>(this)->internal_equal_range(key);
589         return std::make_pair(iterator(result.first), iterator(result.second));
590     }
591 
592     size_type count( const key_type& key ) const {
593         return internal_count(key);
594     }
595 
596     template <typename K>
597     typename std::enable_if<is_transparent<K>::value, size_type>::type count( const K& key ) const {
598         return internal_count(key);
599     }
600 
601     bool contains( const key_type& key ) const {
602         return find(key) != end();
603     }
604 
605     template <typename K>
606     typename std::enable_if<is_transparent<K>::value, bool>::type contains( const K& key ) const {
607         return find(key) != end();
608     }
609 
610     // Bucket interface
611     local_iterator unsafe_begin( size_type n ) {
612         return local_iterator(first_value_node(get_bucket(n)));
613     }
614 
615     const_local_iterator unsafe_begin( size_type n ) const {
616         auto bucket_begin = first_value_node(const_cast<self_type*>(this)->get_bucket(n));
617         return const_local_iterator(bucket_begin);
618     }
619 
620     const_local_iterator unsafe_cbegin( size_type n ) const {
621         auto bucket_begin = first_value_node(const_cast<self_type*>(this)->get_bucket(n));
622         return const_local_iterator(bucket_begin);
623     }
624 
625     local_iterator unsafe_end( size_type n ) {
626         size_type bucket_count = my_bucket_count.load(std::memory_order_relaxed);
627         return n != bucket_count - 1 ? unsafe_begin(get_next_bucket_index(n)) : local_iterator(nullptr);
628     }
629 
630     const_local_iterator unsafe_end( size_type n ) const {
631         size_type bucket_count = my_bucket_count.load(std::memory_order_relaxed);
632         return n != bucket_count - 1 ? unsafe_begin(get_next_bucket_index(n)) : const_local_iterator(nullptr);
633     }
634 
635     const_local_iterator unsafe_cend( size_type n ) const {
636         size_type bucket_count = my_bucket_count.load(std::memory_order_relaxed);
637         return n != bucket_count - 1 ? unsafe_begin(get_next_bucket_index(n)) : const_local_iterator(nullptr);
638     }
639 
640     size_type unsafe_bucket_count() const { return my_bucket_count.load(std::memory_order_relaxed); }
641 
642     size_type unsafe_max_bucket_count() const {
643         return max_size();
644     }
645 
646     size_type unsafe_bucket_size( size_type n ) const {
647         return size_type(std::distance(unsafe_begin(n), unsafe_end(n)));
648     }
649 
650     size_type unsafe_bucket( const key_type& key ) const {
651         return my_hash_compare(key) % my_bucket_count.load(std::memory_order_relaxed);
652     }
653 
654     // Hash policy
655     float load_factor() const {
656         return float(size() / float(my_bucket_count.load(std::memory_order_acquire)));
657     }
658 
659     float max_load_factor() const { return my_max_load_factor; }
660 
661     void max_load_factor( float mlf ) {
662         if (mlf != mlf || mlf < 0) {
663             tbb::detail::throw_exception(exception_id::invalid_load_factor);
664         }
665         my_max_load_factor = mlf;
666     } // TODO: unsafe?
667 
668     void rehash( size_type bucket_count ) {
669         size_type current_bucket_count = my_bucket_count.load(std::memory_order_acquire);
670         if (current_bucket_count < bucket_count) {
671             // TODO: do we need do-while here?
672             my_bucket_count.compare_exchange_strong(current_bucket_count, round_up_to_power_of_two(bucket_count));
673         }
674     }
675 
676     void reserve( size_type elements_count ) {
677         size_type current_bucket_count = my_bucket_count.load(std::memory_order_acquire);
678         size_type necessary_bucket_count = current_bucket_count;
679 
680         do {
681             // TODO: Log2 seems useful here
682             while (necessary_bucket_count * max_load_factor() < elements_count) {
683                 necessary_bucket_count <<= 1;
684             }
685         } while (current_bucket_count >= necessary_bucket_count ||
686                  !my_bucket_count.compare_exchange_strong(current_bucket_count, necessary_bucket_count));
687     }
688 
689     // Observers
690     hasher hash_function() const { return my_hash_compare.hash_function(); }
691     key_equal key_eq() const { return my_hash_compare.key_eq(); }
692 
693     class const_range_type {
694     private:
695         const concurrent_unordered_base& my_instance;
696         node_ptr my_begin_node; // may be node* const
697         node_ptr my_end_node;
698         mutable node_ptr my_midpoint_node;
699     public:
700         using size_type = typename concurrent_unordered_base::size_type;
701         using value_type = typename concurrent_unordered_base::value_type;
702         using reference = typename concurrent_unordered_base::reference;
703         using difference_type = typename concurrent_unordered_base::difference_type;
704         using iterator = typename concurrent_unordered_base::const_iterator;
705 
706         bool empty() const { return my_begin_node == my_end_node; }
707 
708         bool is_divisible() const {
709             return my_midpoint_node != my_end_node;
710         }
711 
712         size_type grainsize() const { return 1; }
713 
714         const_range_type( const_range_type& range, split )
715             : my_instance(range.my_instance),
716               my_begin_node(range.my_midpoint_node),
717               my_end_node(range.my_end_node)
718         {
719             range.my_end_node = my_begin_node;
720             __TBB_ASSERT(!empty(), "Splitting despite the range is not divisible");
721             __TBB_ASSERT(!range.empty(), "Splitting despite the range is not divisible");
722             set_midpoint();
723             range.set_midpoint();
724         }
725 
726         iterator begin() const { return iterator(my_instance.first_value_node(my_begin_node)); }
727         iterator end() const { return iterator(my_instance.first_value_node(my_end_node)); }
728 
729         const_range_type( const concurrent_unordered_base& table )
730             : my_instance(table), my_begin_node(const_cast<node_ptr>(&table.my_head)), my_end_node(nullptr)
731         {
732             set_midpoint();
733         }
734     private:
735         void set_midpoint() const {
736             if (my_begin_node == my_end_node) {
737                 my_midpoint_node = my_end_node;
738             } else {
739                 sokey_type invalid_key = ~sokey_type(0);
740                 sokey_type begin_key = my_begin_node != nullptr ? my_begin_node->order_key() : invalid_key;
741                 sokey_type end_key = my_end_node != nullptr ? my_end_node->order_key() : invalid_key;
742 
743                 size_type mid_bucket = reverse_bits(begin_key + (end_key - begin_key) / 2) %
744                     my_instance.my_bucket_count.load(std::memory_order_relaxed);
745                 while( my_instance.my_segments[mid_bucket].load(std::memory_order_relaxed) == nullptr) {
746                     mid_bucket = my_instance.get_parent(mid_bucket);
747                 }
748                 if (reverse_bits(mid_bucket) > begin_key) {
749                     // Found a dummy node between begin and end
750                     my_midpoint_node = my_instance.first_value_node(
751                         my_instance.my_segments[mid_bucket].load(std::memory_order_relaxed));
752                 } else {
753                     // Didn't find a dummy node between begin and end
754                     my_midpoint_node = my_end_node;
755                 }
756             }
757         }
758     }; // class const_range_type
759 
760     class range_type : public const_range_type {
761     public:
762         using iterator = typename concurrent_unordered_base::iterator;
763         using const_range_type::const_range_type;
764 
765         iterator begin() const { return iterator(const_range_type::begin().get_node_ptr()); }
766         iterator end() const { return iterator(const_range_type::end().get_node_ptr()); }
767     }; // class range_type
768 
769     // Parallel iteration
770     range_type range() {
771         return range_type(*this);
772     }
773 
774     const_range_type range() const {
775         return const_range_type(*this);
776     }
777 protected:
778     static constexpr bool allow_multimapping = traits_type::allow_multimapping;
779 
780 private:
781     static constexpr size_type initial_bucket_count = 8;
782     static constexpr float initial_max_load_factor = 4; // TODO: consider 1?
783     static constexpr size_type pointers_per_embedded_table = sizeof(size_type) * 8 - 1;
784 
785     class unordered_segment_table
786         : public segment_table<std::atomic<node_ptr>, allocator_type, unordered_segment_table, pointers_per_embedded_table>
787     {
788         using self_type = unordered_segment_table;
789         using atomic_node_ptr = std::atomic<node_ptr>;
790         using base_type = segment_table<std::atomic<node_ptr>, allocator_type, unordered_segment_table, pointers_per_embedded_table>;
791         using segment_type = typename base_type::segment_type;
792         using base_allocator_type = typename base_type::allocator_type;
793 
794         using segment_allocator_type = typename allocator_traits_type::template rebind_alloc<atomic_node_ptr>;
795         using segment_allocator_traits = tbb::detail::allocator_traits<segment_allocator_type>;
796     public:
797         // Segment table for unordered containers should not be extended in the wait- free implementation
798         static constexpr bool allow_table_extending = false;
799         static constexpr bool is_noexcept_assignment = std::is_nothrow_move_assignable<hasher>::value &&
800                                                        std::is_nothrow_move_assignable<key_equal>::value &&
801                                                        segment_allocator_traits::is_always_equal::value;
802         static constexpr bool is_noexcept_swap = tbb::detail::is_nothrow_swappable<hasher>::value &&
803                                                  tbb::detail::is_nothrow_swappable<key_equal>::value &&
804                                                  segment_allocator_traits::is_always_equal::value;
805 
806         // TODO: using base_type::base_type is not compiling on Windows and Intel Compiler - investigate
807         unordered_segment_table( const base_allocator_type& alloc = base_allocator_type() )
808             : base_type(alloc) {}
809 
810         unordered_segment_table( const unordered_segment_table& ) = default;
811 
812         unordered_segment_table( const unordered_segment_table& other, const base_allocator_type& alloc )
813             : base_type(other, alloc) {}
814 
815         unordered_segment_table( unordered_segment_table&& ) = default;
816 
817         unordered_segment_table( unordered_segment_table&& other, const base_allocator_type& alloc )
818             : base_type(std::move(other), alloc) {}
819 
820         unordered_segment_table& operator=( const unordered_segment_table& ) = default;
821 
822         unordered_segment_table& operator=( unordered_segment_table&& ) = default;
823 
824         segment_type create_segment( typename base_type::segment_table_type, typename base_type::segment_index_type segment_index, size_type ) {
825             segment_allocator_type alloc(this->get_allocator());
826             size_type seg_size = this->segment_size(segment_index);
827             segment_type new_segment = segment_allocator_traits::allocate(alloc, seg_size);
828             for (size_type i = 0; i != seg_size; ++i) {
829                 segment_allocator_traits::construct(alloc, new_segment + i, nullptr);
830             }
831             return new_segment;
832         }
833 
834         // deallocate_segment is required by the segment_table base class, but
835         // in unordered, it is also necessary to call the destructor during deallocation
836         void deallocate_segment( segment_type address, size_type index ) {
837             destroy_segment(address, index);
838         }
839 
840         void destroy_segment( segment_type address, size_type index ) {
841             segment_allocator_type alloc(this->get_allocator());
842             for (size_type i = 0; i != this->segment_size(index); ++i) {
843                 segment_allocator_traits::destroy(alloc, address + i);
844             }
845             segment_allocator_traits::deallocate(alloc, address, this->segment_size(index));
846         }
847 
848 
849         void copy_segment( size_type index, segment_type, segment_type to ) {
850             if (index == 0) {
851                 // The first element in the first segment is embedded into the table (my_head)
852                 // so the first pointer should not be stored here
853                 // It would be stored during move ctor/assignment operation
854                 to[1].store(nullptr, std::memory_order_relaxed);
855             } else {
856                 for (size_type i = 0; i != this->segment_size(index); ++i) {
857                     to[i].store(nullptr, std::memory_order_relaxed);
858                 }
859             }
860         }
861 
862         void move_segment( size_type index, segment_type from, segment_type to ) {
863             if (index == 0) {
864                 // The first element in the first segment is embedded into the table (my_head)
865                 // so the first pointer should not be stored here
866                 // It would be stored during move ctor/assignment operation
867                 to[1].store(from[1].load(std::memory_order_relaxed), std::memory_order_relaxed);
868             } else {
869                 for (size_type i = 0; i != this->segment_size(index); ++i) {
870                     to[i].store(from[i].load(std::memory_order_relaxed), std::memory_order_relaxed);
871                     from[i].store(nullptr, std::memory_order_relaxed);
872                 }
873             }
874         }
875 
876         // allocate_long_table is required by the segment_table base class, but unused for unordered containers
877         typename base_type::segment_table_type allocate_long_table( const typename base_type::atomic_segment*, size_type ) {
878             __TBB_ASSERT(false, "This method should never been called");
879             // TableType is a pointer
880             return nullptr;
881         }
882 
883         // destroy_elements is required by the segment_table base class, but unused for unordered containers
884         // this function call but do nothing
885         void destroy_elements() {}
886     }; // struct unordered_segment_table
887 
888     void internal_clear() {
889         // TODO: consider usefulness of two versions of clear() - with dummy nodes deallocation and without it
890         node_ptr next = my_head.next();
891         node_ptr curr = next;
892 
893         my_head.set_next(nullptr);
894 
895         while (curr != nullptr) {
896             next = curr->next();
897             destroy_node(curr);
898             curr = next;
899         }
900 
901         my_size.store(0, std::memory_order_relaxed);
902         my_segments.clear();
903     }
904 
905     void destroy_node( node_ptr node ) {
906         if (node->is_dummy()) {
907             node_allocator_type dummy_node_allocator(my_segments.get_allocator());
908             // Destroy the node
909             node_allocator_traits::destroy(dummy_node_allocator, node);
910             // Deallocate the memory
911             node_allocator_traits::deallocate(dummy_node_allocator, node, 1);
912         } else {
913             // GCC 11.1 issues a warning here that incorrect destructor might be called for dummy_nodes
914             #if (__TBB_GCC_VERSION >= 110100 && __TBB_GCC_VERSION < 120000 ) && !__clang__ && !__INTEL_COMPILER
915             volatile
916             #endif
917             value_node_ptr val_node = static_cast<value_node_ptr>(node);
918             value_node_allocator_type value_node_allocator(my_segments.get_allocator());
919             // Destroy the value
920             value_node_allocator_traits::destroy(value_node_allocator, val_node->storage());
921             // Destroy the node
922             value_node_allocator_traits::destroy(value_node_allocator, val_node);
923             // Deallocate the memory
924             value_node_allocator_traits::deallocate(value_node_allocator, val_node, 1);
925         }
926     }
927 
928     struct internal_insert_return_type {
929         // If the insertion failed - the remaining_node points to the node, which was failed to insert
930         // This node can be allocated in process of insertion
931         value_node_ptr remaining_node;
932         // If the insertion failed - node_with_equal_key points to the node in the list with the
933         // key, equivalent to the inserted, otherwise it points to the node, which was inserted.
934         value_node_ptr node_with_equal_key;
935         // Insertion status
936         // NOTE: if it is true - remaining_node should be nullptr
937         bool inserted;
938     }; // struct internal_insert_return_type
939 
940     // Inserts the value into the split ordered list
941     template <typename ValueType>
942     std::pair<iterator, bool> internal_insert_value( ValueType&& value ) {
943 
944         auto create_value_node = [&value, this]( sokey_type order_key )->value_node_ptr {
945             return create_node(order_key, std::forward<ValueType>(value));
946         };
947 
948         auto insert_result = internal_insert(value, create_value_node);
949 
950         if (insert_result.remaining_node != nullptr) {
951             // If the insertion fails - destroy the node which was failed to insert if it exist
952             __TBB_ASSERT(!insert_result.inserted,
953                          "remaining_node should be nullptr if the node was successfully inserted");
954             destroy_node(insert_result.remaining_node);
955         }
956 
957         return { iterator(insert_result.node_with_equal_key), insert_result.inserted };
958     }
959 
960     // Inserts the node into the split ordered list
961     // Creates a node using the specified callback after the place for insertion was found
962     // Returns internal_insert_return_type object, where:
963     //     - If the insertion succeeded:
964     //         - remaining_node is nullptr
965     //         - node_with_equal_key point to the inserted node
966     //         - inserted is true
967     //     - If the insertion failed:
968     //         - remaining_node points to the node, that was failed to insert if it was created.
969     //           nullptr if the node was not created, because the requested key was already
970     //           presented in the list
971     //         - node_with_equal_key point to the element in the list with the key, equivalent to
972     //           to the requested key
973     //         - inserted is false
974     template <typename ValueType, typename CreateInsertNode>
975     internal_insert_return_type internal_insert( ValueType&& value, CreateInsertNode create_insert_node ) {
976         static_assert(std::is_same<typename std::decay<ValueType>::type, value_type>::value,
977                       "Incorrect type in internal_insert");
978         const key_type& key = traits_type::get_key(value);
979         sokey_type hash_key = sokey_type(my_hash_compare(key));
980 
981         sokey_type order_key = split_order_key_regular(hash_key);
982         node_ptr prev = prepare_bucket(hash_key);
983         __TBB_ASSERT(prev != nullptr, "Invalid head node");
984 
985         auto search_result = search_after(prev, order_key, key);
986 
987         if (search_result.second) {
988             return internal_insert_return_type{ nullptr, search_result.first, false };
989         }
990 
991         value_node_ptr new_node = create_insert_node(order_key);
992         node_ptr curr = search_result.first;
993 
994         while (!try_insert(prev, new_node, curr)) {
995             search_result = search_after(prev, order_key, key);
996             if (search_result.second) {
997                 return internal_insert_return_type{ new_node, search_result.first, false };
998             }
999             curr = search_result.first;
1000         }
1001 
1002         auto sz = my_size.fetch_add(1);
1003         adjust_table_size(sz + 1, my_bucket_count.load(std::memory_order_acquire));
1004         return internal_insert_return_type{ nullptr, static_cast<value_node_ptr>(new_node), true };
1005     }
1006 
1007     // Searches the node with the key, equivalent to key with requested order key after the node prev
1008     // Returns the existing node and true if the node is already in the list
1009     // Returns the first node with the order key, greater than requested and false if the node is not presented in the list
1010     std::pair<value_node_ptr, bool> search_after( node_ptr& prev, sokey_type order_key, const key_type& key ) {
1011         // NOTE: static_cast<value_node_ptr>(curr) should be done only after we would ensure
1012         // that the node is not a dummy node
1013 
1014         node_ptr curr = prev->next();
1015 
1016         while (curr != nullptr && (curr->order_key() < order_key ||
1017                (curr->order_key() == order_key && !my_hash_compare(traits_type::get_key(static_cast<value_node_ptr>(curr)->value()), key))))
1018         {
1019             prev = curr;
1020             curr = curr->next();
1021         }
1022 
1023         if (curr != nullptr && curr->order_key() == order_key && !allow_multimapping) {
1024             return { static_cast<value_node_ptr>(curr), true };
1025         }
1026         return { static_cast<value_node_ptr>(curr), false };
1027     }
1028 
1029     void adjust_table_size( size_type total_elements, size_type current_size ) {
1030         // Grow the table by a factor of 2 if possible and needed
1031         if ( (float(total_elements) / float(current_size)) > my_max_load_factor ) {
1032             // Double the size of the hash only if size hash not changed in between loads
1033             my_bucket_count.compare_exchange_strong(current_size, 2u * current_size);
1034         }
1035     }
1036 
1037     node_ptr insert_dummy_node( node_ptr parent_dummy_node, sokey_type order_key ) {
1038         node_ptr prev_node = parent_dummy_node;
1039 
1040         node_ptr dummy_node = create_dummy_node(order_key);
1041         node_ptr next_node;
1042 
1043         do {
1044             next_node = prev_node->next();
1045             // Move forward through the list while the order key is less than requested
1046             while (next_node != nullptr && next_node->order_key() < order_key) {
1047                 prev_node = next_node;
1048                 next_node = next_node->next();
1049             }
1050 
1051             if (next_node != nullptr && next_node->order_key() == order_key) {
1052                 // Another dummy node with the same order key was inserted by another thread
1053                 // Destroy the node and exit
1054                 destroy_node(dummy_node);
1055                 return next_node;
1056             }
1057         } while (!try_insert(prev_node, dummy_node, next_node));
1058 
1059         return dummy_node;
1060     }
1061 
1062     // Try to insert a node between prev_node and expected next
1063     // If the next is not equal to expected next - return false
1064     static bool try_insert( node_ptr prev_node, node_ptr new_node, node_ptr current_next_node ) {
1065         new_node->set_next(current_next_node);
1066         return prev_node->try_set_next(current_next_node, new_node);
1067     }
1068 
1069     // Returns the bucket, associated with the hash_key
1070     node_ptr prepare_bucket( sokey_type hash_key ) {
1071         size_type bucket = hash_key % my_bucket_count.load(std::memory_order_acquire);
1072         return get_bucket(bucket);
1073     }
1074 
1075     // Initialize the corresponding bucket if it is not initialized
1076     node_ptr get_bucket( size_type bucket_index ) {
1077         if (my_segments[bucket_index].load(std::memory_order_acquire) == nullptr) {
1078             init_bucket(bucket_index);
1079         }
1080         return my_segments[bucket_index].load(std::memory_order_acquire);
1081     }
1082 
1083     void init_bucket( size_type bucket ) {
1084         if (bucket == 0) {
1085             // Atomicaly store the first bucket into my_head
1086             node_ptr disabled = nullptr;
1087             my_segments[0].compare_exchange_strong(disabled, &my_head);
1088             return;
1089         }
1090 
1091         size_type parent_bucket = get_parent(bucket);
1092 
1093         while (my_segments[parent_bucket].load(std::memory_order_acquire) == nullptr) {
1094             // Initialize all of the parent buckets
1095             init_bucket(parent_bucket);
1096         }
1097 
1098         __TBB_ASSERT(my_segments[parent_bucket].load(std::memory_order_acquire) != nullptr, "Parent bucket should be initialized");
1099         node_ptr parent = my_segments[parent_bucket].load(std::memory_order_acquire);
1100 
1101         // Insert dummy node into the list
1102         node_ptr dummy_node = insert_dummy_node(parent, split_order_key_dummy(bucket));
1103         // TODO: consider returning pair<node_ptr, bool> to avoid store operation if the bucket was stored by an other thread
1104         // or move store to insert_dummy_node
1105         // Add dummy_node into the segment table
1106         my_segments[bucket].store(dummy_node, std::memory_order_release);
1107     }
1108 
1109     node_ptr create_dummy_node( sokey_type order_key ) {
1110         node_allocator_type dummy_node_allocator(my_segments.get_allocator());
1111         node_ptr dummy_node = node_allocator_traits::allocate(dummy_node_allocator, 1);
1112         node_allocator_traits::construct(dummy_node_allocator, dummy_node, order_key);
1113         return dummy_node;
1114     }
1115 
1116     template <typename... Args>
1117     value_node_ptr create_node( sokey_type order_key, Args&&... args ) {
1118         value_node_allocator_type value_node_allocator(my_segments.get_allocator());
1119         // Allocate memory for the value_node
1120         value_node_ptr new_node = value_node_allocator_traits::allocate(value_node_allocator, 1);
1121         // Construct the node
1122         value_node_allocator_traits::construct(value_node_allocator, new_node, order_key);
1123 
1124         // try_call API is not convenient here due to broken
1125         // variadic capture on GCC 4.8.5
1126         auto value_guard = make_raii_guard([&] {
1127             value_node_allocator_traits::destroy(value_node_allocator, new_node);
1128             value_node_allocator_traits::deallocate(value_node_allocator, new_node, 1);
1129         });
1130 
1131         // Construct the value in the node
1132         value_node_allocator_traits::construct(value_node_allocator, new_node->storage(), std::forward<Args>(args)...);
1133         value_guard.dismiss();
1134         return new_node;
1135     }
1136 
1137     value_node_ptr first_value_node( node_ptr first_node ) const {
1138         while (first_node != nullptr && first_node->is_dummy()) {
1139             first_node = first_node->next();
1140         }
1141         return static_cast<value_node_ptr>(first_node);
1142     }
1143 
1144     // Unsafe method, which removes the node from the list and returns the next node
1145     node_ptr internal_erase( value_node_ptr node_to_erase ) {
1146         __TBB_ASSERT(node_to_erase != nullptr, "Invalid iterator for erase");
1147         node_ptr next_node = node_to_erase->next();
1148         internal_extract(node_to_erase);
1149         destroy_node(node_to_erase);
1150         return next_node;
1151     }
1152 
1153     template <typename K>
1154     size_type internal_erase_by_key( const K& key ) {
1155         // TODO: consider reimplementation without equal_range - it is not effective to perform lookup over a bucket
1156         // for each unsafe_erase call
1157         auto eq_range = equal_range(key);
1158         size_type erased_count = 0;
1159 
1160         for (auto it = eq_range.first; it != eq_range.second;) {
1161             it = unsafe_erase(it);
1162             ++erased_count;
1163         }
1164         return erased_count;
1165     }
1166 
1167     // Unsafe method, which extracts the node from the list
1168     void internal_extract( value_node_ptr node_to_extract ) {
1169         const key_type& key = traits_type::get_key(node_to_extract->value());
1170         sokey_type hash_key = sokey_type(my_hash_compare(key));
1171 
1172         node_ptr prev_node = prepare_bucket(hash_key);
1173 
1174         for (node_ptr node = prev_node->next(); node != nullptr; prev_node = node, node = node->next()) {
1175             if (node == node_to_extract) {
1176                 unlink_node(prev_node, node, node_to_extract->next());
1177                 my_size.store(my_size.load(std::memory_order_relaxed) - 1, std::memory_order_relaxed);
1178                 return;
1179             }
1180             __TBB_ASSERT(node->order_key() <= node_to_extract->order_key(),
1181                          "node, which is going to be extracted should be presented in the list");
1182         }
1183     }
1184 
1185 protected:
1186     template <typename SourceType>
1187     void internal_merge( SourceType&& source ) {
1188         static_assert(std::is_same<node_type, typename std::decay<SourceType>::type::node_type>::value,
1189                       "Incompatible containers cannot be merged");
1190 
1191         for (node_ptr source_prev = &source.my_head; source_prev->next() != nullptr;) {
1192             if (!source_prev->next()->is_dummy()) {
1193                 value_node_ptr curr = static_cast<value_node_ptr>(source_prev->next());
1194                 // If the multimapping is allowed, or the key is not presented
1195                 // in the *this container - extract the node from the list
1196                 if (allow_multimapping || !contains(traits_type::get_key(curr->value()))) {
1197                     node_ptr next_node = curr->next();
1198                     source.unlink_node(source_prev, curr, next_node);
1199 
1200                     // Remember the old order key
1201                     sokey_type old_order_key = curr->order_key();
1202 
1203                     // Node handle with curr cannot be used directly in insert call, because
1204                     // the destructor of node_type will destroy curr
1205                     node_type curr_node = node_handle_accessor::construct<node_type>(curr);
1206 
1207                     // If the insertion fails - return ownership of the node to the source
1208                     if (!insert(std::move(curr_node)).second) {
1209                         __TBB_ASSERT(!allow_multimapping, "Insertion should succeed for multicontainer");
1210                         __TBB_ASSERT(source_prev->next() == next_node,
1211                                      "Concurrent operations with the source container in merge are prohibited");
1212 
1213                         // Initialize the node with the old order key, because the order key
1214                         // can change during the insertion
1215                         curr->init(old_order_key);
1216                         __TBB_ASSERT(old_order_key >= source_prev->order_key() &&
1217                                      (next_node == nullptr || old_order_key <= next_node->order_key()),
1218                                      "Wrong nodes order in the source container");
1219                         // Merge is unsafe for source container, so the insertion back can be done without compare_exchange
1220                         curr->set_next(next_node);
1221                         source_prev->set_next(curr);
1222                         source_prev = curr;
1223                         node_handle_accessor::deactivate(curr_node);
1224                     } else {
1225                         source.my_size.fetch_sub(1, std::memory_order_relaxed);
1226                     }
1227                 } else {
1228                     source_prev = curr;
1229                 }
1230             } else {
1231                 source_prev = source_prev->next();
1232             }
1233         }
1234     }
1235 
1236 private:
1237     // Unsafe method, which unlinks the node between prev and next
1238     void unlink_node( node_ptr prev_node, node_ptr node_to_unlink, node_ptr next_node ) {
1239         __TBB_ASSERT(prev_node->next() == node_to_unlink &&
1240                      node_to_unlink->next() == next_node,
1241                      "erasing and extracting nodes from the containers are unsafe in concurrent mode");
1242         prev_node->set_next(next_node);
1243         node_to_unlink->set_next(nullptr);
1244     }
1245 
1246     template <typename K>
1247     value_node_ptr internal_find( const K& key ) {
1248         sokey_type hash_key = sokey_type(my_hash_compare(key));
1249         sokey_type order_key = split_order_key_regular(hash_key);
1250 
1251         node_ptr curr = prepare_bucket(hash_key);
1252 
1253         while (curr != nullptr) {
1254             if (curr->order_key() > order_key) {
1255                 // If the order key is greater than the requested order key,
1256                 // the element is not in the hash table
1257                 return nullptr;
1258             } else if (curr->order_key() == order_key &&
1259                        my_hash_compare(traits_type::get_key(static_cast<value_node_ptr>(curr)->value()), key)) {
1260                 // The fact that order keys match does not mean that the element is found.
1261                 // Key function comparison has to be performed to check whether this is the
1262                 // right element. If not, keep searching while order key is the same.
1263                 return static_cast<value_node_ptr>(curr);
1264             }
1265             curr = curr->next();
1266         }
1267 
1268         return nullptr;
1269     }
1270 
1271     template <typename K>
1272     std::pair<value_node_ptr, value_node_ptr> internal_equal_range( const K& key ) {
1273         sokey_type hash_key = sokey_type(my_hash_compare(key));
1274         sokey_type order_key = split_order_key_regular(hash_key);
1275 
1276         node_ptr curr = prepare_bucket(hash_key);
1277 
1278         while (curr != nullptr) {
1279             if (curr->order_key() > order_key) {
1280                 // If the order key is greater than the requested order key,
1281                 // the element is not in the hash table
1282                 return std::make_pair(nullptr, nullptr);
1283             } else if (curr->order_key() == order_key &&
1284                        my_hash_compare(traits_type::get_key(static_cast<value_node_ptr>(curr)->value()), key)) {
1285                 value_node_ptr first = static_cast<value_node_ptr>(curr);
1286                 node_ptr last = first;
1287                 do {
1288                     last = last->next();
1289                 } while (allow_multimapping && last != nullptr && !last->is_dummy() &&
1290                         my_hash_compare(traits_type::get_key(static_cast<value_node_ptr>(last)->value()), key));
1291                 return std::make_pair(first, first_value_node(last));
1292             }
1293             curr = curr->next();
1294         }
1295         return {nullptr, nullptr};
1296     }
1297 
1298     template <typename K>
1299     size_type internal_count( const K& key ) const {
1300         if (allow_multimapping) {
1301             // TODO: consider reimplementing the internal_equal_range with elements counting to avoid std::distance
1302             auto eq_range = equal_range(key);
1303             return std::distance(eq_range.first, eq_range.second);
1304         } else {
1305             return contains(key) ? 1 : 0;
1306         }
1307     }
1308 
1309     void internal_copy( const concurrent_unordered_base& other ) {
1310         node_ptr last_node = &my_head;
1311         my_segments[0].store(&my_head, std::memory_order_relaxed);
1312 
1313         for (node_ptr node = other.my_head.next(); node != nullptr; node = node->next()) {
1314             node_ptr new_node;
1315             if (!node->is_dummy()) {
1316                 // The node in the right table contains a value
1317                 new_node = create_node(node->order_key(), static_cast<value_node_ptr>(node)->value());
1318             } else {
1319                 // The node in the right table is a dummy node
1320                 new_node = create_dummy_node(node->order_key());
1321                 my_segments[reverse_bits(node->order_key())].store(new_node, std::memory_order_relaxed);
1322             }
1323 
1324             last_node->set_next(new_node);
1325             last_node = new_node;
1326         }
1327     }
1328 
1329     void internal_move( concurrent_unordered_base&& other ) {
1330         node_ptr last_node = &my_head;
1331         my_segments[0].store(&my_head, std::memory_order_relaxed);
1332 
1333         for (node_ptr node = other.my_head.next(); node != nullptr; node = node->next()) {
1334             node_ptr new_node;
1335             if (!node->is_dummy()) {
1336                 // The node in the right table contains a value
1337                 new_node = create_node(node->order_key(), std::move(static_cast<value_node_ptr>(node)->value()));
1338             } else {
1339                 // TODO: do we need to destroy a dummy node in the right container?
1340                 // The node in the right table is a dummy_node
1341                 new_node = create_dummy_node(node->order_key());
1342                 my_segments[reverse_bits(node->order_key())].store(new_node, std::memory_order_relaxed);
1343             }
1344 
1345             last_node->set_next(new_node);
1346             last_node = new_node;
1347         }
1348     }
1349 
1350     void move_content( concurrent_unordered_base&& other ) {
1351         // NOTE: allocators should be equal
1352         my_head.set_next(other.my_head.next());
1353         other.my_head.set_next(nullptr);
1354         my_segments[0].store(&my_head, std::memory_order_relaxed);
1355 
1356         other.my_bucket_count.store(initial_bucket_count, std::memory_order_relaxed);
1357         other.my_max_load_factor = initial_max_load_factor;
1358         other.my_size.store(0, std::memory_order_relaxed);
1359     }
1360 
1361     void internal_move_construct_with_allocator( concurrent_unordered_base&& other, const allocator_type&,
1362                                                  /*is_always_equal = */std::true_type ) {
1363         // Allocators are always equal - no need to compare for equality
1364         move_content(std::move(other));
1365     }
1366 
1367     void internal_move_construct_with_allocator( concurrent_unordered_base&& other, const allocator_type& alloc,
1368                                                  /*is_always_equal = */std::false_type ) {
1369         // Allocators are not always equal
1370         if (alloc == other.my_segments.get_allocator()) {
1371             move_content(std::move(other));
1372         } else {
1373             try_call( [&] {
1374                 internal_move(std::move(other));
1375             } ).on_exception( [&] {
1376                 clear();
1377             });
1378         }
1379     }
1380 
1381     // Move assigns the hash table to other is any instances of allocator_type are always equal
1382     // or propagate_on_container_move_assignment is true
1383     void internal_move_assign( concurrent_unordered_base&& other, /*is_always_equal || POCMA = */std::true_type ) {
1384         move_content(std::move(other));
1385     }
1386 
1387     // Move assigns the hash table to other is any instances of allocator_type are not always equal
1388     // and propagate_on_container_move_assignment is false
1389     void internal_move_assign( concurrent_unordered_base&& other, /*is_always_equal || POCMA = */std::false_type ) {
1390         if (my_segments.get_allocator() == other.my_segments.get_allocator()) {
1391             move_content(std::move(other));
1392         } else {
1393             // TODO: guards for exceptions
1394             internal_move(std::move(other));
1395         }
1396     }
1397 
1398     void internal_swap( concurrent_unordered_base& other, /*is_always_equal || POCS = */std::true_type ) {
1399         internal_swap_fields(other);
1400     }
1401 
1402     void internal_swap( concurrent_unordered_base& other, /*is_always_equal || POCS = */std::false_type ) {
1403         __TBB_ASSERT(my_segments.get_allocator() == other.my_segments.get_allocator(),
1404                      "Swapping with unequal allocators is not allowed");
1405         internal_swap_fields(other);
1406     }
1407 
1408     void internal_swap_fields( concurrent_unordered_base& other ) {
1409         node_ptr first_node = my_head.next();
1410         my_head.set_next(other.my_head.next());
1411         other.my_head.set_next(first_node);
1412 
1413         size_type current_size = my_size.load(std::memory_order_relaxed);
1414         my_size.store(other.my_size.load(std::memory_order_relaxed), std::memory_order_relaxed);
1415         other.my_size.store(current_size, std::memory_order_relaxed);
1416 
1417         size_type bucket_count = my_bucket_count.load(std::memory_order_relaxed);
1418         my_bucket_count.store(other.my_bucket_count.load(std::memory_order_relaxed), std::memory_order_relaxed);
1419         other.my_bucket_count.store(bucket_count, std::memory_order_relaxed);
1420 
1421         using std::swap;
1422         swap(my_max_load_factor, other.my_max_load_factor);
1423         swap(my_hash_compare, other.my_hash_compare);
1424         my_segments.swap(other.my_segments);
1425 
1426         // swap() method from segment table swaps all of the segments including the first segment
1427         // We should restore it to my_head. Without it the first segment of the container will point
1428         // to other.my_head.
1429         my_segments[0].store(&my_head, std::memory_order_relaxed);
1430         other.my_segments[0].store(&other.my_head, std::memory_order_relaxed);
1431     }
1432 
1433     // A regular order key has its original hash value reversed and the last bit set
1434     static constexpr sokey_type split_order_key_regular( sokey_type hash ) {
1435         return reverse_bits(hash) | 0x1;
1436     }
1437 
1438     // A dummy order key has its original hash value reversed and the last bit unset
1439     static constexpr sokey_type split_order_key_dummy( sokey_type hash ) {
1440         return reverse_bits(hash) & ~sokey_type(0x1);
1441     }
1442 
1443     size_type get_parent( size_type bucket ) const {
1444         // Unset bucket's most significant turned-on bit
1445         __TBB_ASSERT(bucket != 0, "Unable to get_parent of the bucket 0");
1446         size_type msb = tbb::detail::log2(bucket);
1447         return bucket & ~(size_type(1) << msb);
1448     }
1449 
1450     size_type get_next_bucket_index( size_type bucket ) const {
1451         size_type bits = tbb::detail::log2(my_bucket_count.load(std::memory_order_relaxed));
1452         size_type reversed_next = reverse_n_bits(bucket, bits) + 1;
1453         return reverse_n_bits(reversed_next, bits);
1454     }
1455 
1456     std::atomic<size_type> my_size;
1457     std::atomic<size_type> my_bucket_count;
1458     float my_max_load_factor;
1459     hash_compare_type my_hash_compare;
1460 
1461     list_node_type my_head; // Head node for split ordered list
1462     unordered_segment_table my_segments; // Segment table of pointers to nodes
1463 
1464     template <typename Container, typename Value>
1465     friend class solist_iterator;
1466 
1467     template <typename OtherTraits>
1468     friend class concurrent_unordered_base;
1469 }; // class concurrent_unordered_base
1470 
1471 template <typename Traits>
1472 bool operator==( const concurrent_unordered_base<Traits>& lhs,
1473                  const concurrent_unordered_base<Traits>& rhs ) {
1474     if (&lhs == &rhs) { return true; }
1475     if (lhs.size() != rhs.size()) { return false; }
1476 
1477 #if _MSC_VER
1478     // Passing "unchecked" iterators to std::permutation with 3 parameters
1479     // causes compiler warnings.
1480     // The workaround is to use overload with 4 parameters, which is
1481     // available since C++14 - minimally supported version on MSVC
1482     return std::is_permutation(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
1483 #else
1484     return std::is_permutation(lhs.begin(), lhs.end(), rhs.begin());
1485 #endif
1486 }
1487 
1488 #if !__TBB_CPP20_COMPARISONS_PRESENT
1489 template <typename Traits>
1490 bool operator!=( const concurrent_unordered_base<Traits>& lhs,
1491                  const concurrent_unordered_base<Traits>& rhs ) {
1492     return !(lhs == rhs);
1493 }
1494 #endif
1495 
1496 #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
1497 #pragma warning(pop) // warning 4127 is back
1498 #endif
1499 
1500 } // namespace d1
1501 } // namespace detail
1502 } // namespace tbb
1503 
1504 #endif // __TBB_detail__concurrent_unordered_base_H
1505