1 /* 2 Copyright (c) 2019-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_skip_list_H 18 #define __TBB_detail__concurrent_skip_list_H 19 20 #if !defined(__TBB_concurrent_map_H) && !defined(__TBB_concurrent_set_H) 21 #error Do not #include this internal file directly; use public TBB headers instead. 22 #endif 23 24 #include "_config.h" 25 #include "_range_common.h" 26 #include "_allocator_traits.h" 27 #include "_template_helpers.h" 28 #include "_node_handle.h" 29 #include "_containers_helpers.h" 30 #include "_assert.h" 31 #include "_exception.h" 32 #include "../enumerable_thread_specific.h" 33 #include <utility> 34 #include <initializer_list> 35 #include <atomic> 36 #include <array> 37 #include <type_traits> 38 #include <random> // Need std::geometric_distribution 39 #include <algorithm> // Need std::equal and std::lexicographical_compare 40 #include <cstdint> 41 #if __TBB_CPP20_COMPARISONS_PRESENT 42 #include <compare> 43 #endif 44 45 #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) 46 #pragma warning(push) 47 #pragma warning(disable: 4127) // warning C4127: conditional expression is constant 48 #endif 49 50 namespace tbb { 51 namespace detail { 52 namespace d2 { 53 54 template <typename Value, typename Allocator> 55 class skip_list_node { 56 using node_ptr = skip_list_node*; 57 public: 58 using value_type = Value; 59 using atomic_node_ptr = std::atomic<node_ptr>; 60 using size_type = std::size_t; 61 using container_allocator_type = Allocator; 62 63 using reference = value_type&; 64 using const_reference = const value_type&; 65 private: 66 using allocator_traits = tbb::detail::allocator_traits<container_allocator_type>; 67 68 // Allocator is the same as the container allocator=> allocates unitptr_t 69 // It is required to rebind it to value_type to get the correct pointer and const_pointer 70 using value_allocator_traits = typename allocator_traits::template rebind_traits<value_type>; 71 public: 72 using pointer = typename value_allocator_traits::pointer; 73 using const_pointer = typename value_allocator_traits::const_pointer; 74 75 //In perfect world these constructor and destructor would have been private, 76 //however this seems technically impractical due to use of allocator_traits. 77 78 //Should not be called directly, instead use create method 79 skip_list_node( size_type levels ) 80 : my_height(levels), my_index_number(0) 81 {} 82 83 //Should not be called directly, instead use destroy method 84 ~skip_list_node() {} 85 86 skip_list_node( const skip_list_node& ) = delete; 87 skip_list_node( skip_list_node&& ) = delete; 88 skip_list_node& operator=( const skip_list_node& ) = delete; 89 skip_list_node& operator=( skip_list_node&& ) = delete; 90 91 static skip_list_node* create( container_allocator_type& alloc, size_type height ) { 92 size_type sz = calc_node_size(height); 93 static_assert(std::is_same<typename allocator_traits::value_type, std::uint8_t>::value, "skip_list_node assumes that passed in allocator operates on bytes"); 94 auto* node = reinterpret_cast<skip_list_node*>(allocator_traits::allocate(alloc, sz)); 95 96 //Construct the node itself 97 allocator_traits::construct(alloc, node, height); 98 99 //Construct the level pointers 100 for (size_type l = 0; l < height; ++l) { 101 allocator_traits::construct(alloc, &node->get_atomic_next(l), nullptr); 102 } 103 104 return node; 105 } 106 107 static void destroy( container_allocator_type& alloc, skip_list_node* node ) { 108 //Destroy the level pointers 109 for (size_type l = 0; l < node->height(); ++l) { 110 allocator_traits::destroy(alloc, &node->atomic_next(l)); 111 } 112 size_type sz = calc_node_size(node->height()); 113 // Destroy the node itself 114 allocator_traits::destroy(alloc, node); 115 116 // Deallocate the node 117 allocator_traits::deallocate(alloc, reinterpret_cast<std::uint8_t*>(node), sz); 118 } 119 120 121 pointer storage() { 122 return &my_value; 123 } 124 125 reference value() { 126 return *storage(); 127 } 128 129 node_ptr next( size_type level ) const { 130 node_ptr res = get_atomic_next(level).load(std::memory_order_acquire); 131 __TBB_ASSERT(res == nullptr || res->height() > level, "Broken internal structure"); 132 return res; 133 } 134 135 atomic_node_ptr& atomic_next( size_type level ) { 136 atomic_node_ptr& res = get_atomic_next(level); 137 #if TBB_USE_DEBUG 138 node_ptr node = res.load(std::memory_order_acquire); 139 __TBB_ASSERT(node == nullptr || node->height() > level, "Broken internal structure"); 140 #endif 141 return res; 142 } 143 144 void set_next( size_type level, node_ptr n ) { 145 __TBB_ASSERT(n == nullptr || n->height() > level, "Broken internal structure"); 146 get_atomic_next(level).store(n, std::memory_order_relaxed); 147 } 148 149 size_type height() const { 150 return my_height; 151 } 152 153 void set_index_number( size_type index_num ) { 154 my_index_number = index_num; 155 } 156 157 size_type index_number() const { 158 return my_index_number; 159 } 160 161 private: 162 static size_type calc_node_size( size_type height ) { 163 static_assert(alignof(skip_list_node) >= alignof(atomic_node_ptr), "Incorrect alignment"); 164 return sizeof(skip_list_node) + height * sizeof(atomic_node_ptr); 165 } 166 167 atomic_node_ptr& get_atomic_next( size_type level ) { 168 atomic_node_ptr* arr = reinterpret_cast<atomic_node_ptr*>(this + 1); 169 return arr[level]; 170 } 171 172 const atomic_node_ptr& get_atomic_next( size_type level ) const { 173 const atomic_node_ptr* arr = reinterpret_cast<const atomic_node_ptr*>(this + 1); 174 return arr[level]; 175 } 176 177 union { 178 value_type my_value; 179 }; 180 size_type my_height; 181 size_type my_index_number; 182 }; // class skip_list_node 183 184 template <typename NodeType, typename ValueType> 185 class skip_list_iterator { 186 using node_type = NodeType; 187 using node_ptr = node_type*; 188 public: 189 using iterator_category = std::forward_iterator_tag; 190 using value_type = ValueType; 191 192 using difference_type = std::ptrdiff_t; 193 using pointer = value_type*; 194 using reference = value_type&; 195 196 skip_list_iterator() : skip_list_iterator(nullptr) {} 197 198 skip_list_iterator( const skip_list_iterator<node_type, typename node_type::value_type>& other ) 199 : my_node_ptr(other.my_node_ptr) {} 200 201 skip_list_iterator& operator=( const skip_list_iterator<node_type, typename node_type::value_type>& other ) { 202 my_node_ptr = other.my_node_ptr; 203 return *this; 204 } 205 206 reference operator*() const { return my_node_ptr->value(); } 207 pointer operator->() const { return my_node_ptr->storage(); } 208 209 skip_list_iterator& operator++() { 210 __TBB_ASSERT(my_node_ptr != nullptr, nullptr); 211 my_node_ptr = my_node_ptr->next(0); 212 return *this; 213 } 214 215 skip_list_iterator operator++(int) { 216 skip_list_iterator tmp = *this; 217 ++*this; 218 return tmp; 219 } 220 221 private: 222 skip_list_iterator(node_type* n) : my_node_ptr(n) {} 223 224 node_ptr my_node_ptr; 225 226 template <typename Traits> 227 friend class concurrent_skip_list; 228 229 template <typename N, typename V> 230 friend class skip_list_iterator; 231 232 friend class const_range; 233 friend class range; 234 235 friend bool operator==( const skip_list_iterator& lhs, const skip_list_iterator& rhs ) { 236 return lhs.my_node_ptr == rhs.my_node_ptr; 237 } 238 239 friend bool operator!=( const skip_list_iterator& lhs, const skip_list_iterator& rhs ) { 240 return lhs.my_node_ptr != rhs.my_node_ptr; 241 } 242 }; // class skip_list_iterator 243 244 template <typename Traits> 245 class concurrent_skip_list { 246 protected: 247 using container_traits = Traits; 248 using self_type = concurrent_skip_list<container_traits>; 249 using allocator_type = typename container_traits::allocator_type; 250 using allocator_traits_type = tbb::detail::allocator_traits<allocator_type>; 251 using key_compare = typename container_traits::compare_type; 252 using value_compare = typename container_traits::value_compare; 253 using key_type = typename container_traits::key_type; 254 using value_type = typename container_traits::value_type; 255 static_assert(std::is_same<value_type, typename allocator_type::value_type>::value, 256 "value_type of the container should be the same as its allocator"); 257 258 using size_type = std::size_t; 259 using difference_type = std::ptrdiff_t; 260 261 static constexpr size_type max_level = container_traits::max_level; 262 263 using node_allocator_type = typename allocator_traits_type::template rebind_alloc<std::uint8_t>; 264 using node_allocator_traits = tbb::detail::allocator_traits<node_allocator_type>; 265 266 using list_node_type = skip_list_node<value_type, node_allocator_type>; 267 using node_type = d1::node_handle<key_type, value_type, list_node_type, allocator_type>; 268 269 using iterator = skip_list_iterator<list_node_type, value_type>; 270 using const_iterator = skip_list_iterator<list_node_type, const value_type>; 271 272 using reference = value_type&; 273 using const_reference = const value_type&; 274 using pointer = typename allocator_traits_type::pointer; 275 using const_pointer = typename allocator_traits_type::const_pointer; 276 277 using random_level_generator_type = typename container_traits::random_level_generator_type; 278 279 using node_ptr = list_node_type*; 280 281 using array_type = std::array<node_ptr, max_level>; 282 private: 283 template <typename T> 284 using is_transparent = dependent_bool<comp_is_transparent<key_compare>, T>; 285 public: 286 static constexpr bool allow_multimapping = container_traits::allow_multimapping; 287 288 concurrent_skip_list() : my_head_ptr(nullptr), my_size(0), my_max_height(0) {} 289 290 explicit concurrent_skip_list( const key_compare& comp, const allocator_type& alloc = allocator_type() ) 291 : my_node_allocator(alloc), my_compare(comp), my_head_ptr(nullptr), my_size(0), my_max_height(0) {} 292 293 explicit concurrent_skip_list( const allocator_type& alloc ) 294 : concurrent_skip_list(key_compare(), alloc) {} 295 296 template<typename InputIterator> 297 concurrent_skip_list( InputIterator first, InputIterator last, const key_compare& comp = key_compare(), 298 const allocator_type& alloc = allocator_type() ) 299 : concurrent_skip_list(comp, alloc) 300 { 301 internal_copy(first, last); 302 } 303 304 template <typename InputIterator> 305 concurrent_skip_list( InputIterator first, InputIterator last, const allocator_type& alloc ) 306 : concurrent_skip_list(first, last, key_compare(), alloc) {} 307 308 concurrent_skip_list( std::initializer_list<value_type> init, const key_compare& comp = key_compare(), 309 const allocator_type& alloc = allocator_type() ) 310 : concurrent_skip_list(init.begin(), init.end(), comp, alloc) {} 311 312 concurrent_skip_list( std::initializer_list<value_type> init, const allocator_type& alloc ) 313 : concurrent_skip_list(init, key_compare(), alloc) {} 314 315 concurrent_skip_list( const concurrent_skip_list& other ) 316 : my_node_allocator(node_allocator_traits::select_on_container_copy_construction(other.get_allocator())), 317 my_compare(other.my_compare), my_rng(other.my_rng), my_head_ptr(nullptr), 318 my_size(0), my_max_height(0) 319 { 320 internal_copy(other); 321 __TBB_ASSERT(my_size == other.my_size, "Wrong size of copy-constructed container"); 322 } 323 324 concurrent_skip_list( const concurrent_skip_list& other, const allocator_type& alloc ) 325 : my_node_allocator(alloc), my_compare(other.my_compare), my_rng(other.my_rng), my_head_ptr(nullptr), 326 my_size(0), my_max_height(0) 327 { 328 internal_copy(other); 329 __TBB_ASSERT(my_size == other.my_size, "Wrong size of copy-constructed container"); 330 } 331 332 concurrent_skip_list( concurrent_skip_list&& other ) 333 : my_node_allocator(std::move(other.my_node_allocator)), my_compare(other.my_compare), 334 my_rng(std::move(other.my_rng)), my_head_ptr(nullptr) // my_head_ptr would be stored in internal_move 335 { 336 internal_move(std::move(other)); 337 } 338 339 concurrent_skip_list( concurrent_skip_list&& other, const allocator_type& alloc ) 340 : my_node_allocator(alloc), my_compare(other.my_compare), 341 my_rng(std::move(other.my_rng)), my_head_ptr(nullptr) 342 { 343 using is_always_equal = typename allocator_traits_type::is_always_equal; 344 internal_move_construct_with_allocator(std::move(other), is_always_equal()); 345 } 346 347 ~concurrent_skip_list() { 348 clear(); 349 delete_head(); 350 } 351 352 concurrent_skip_list& operator=( const concurrent_skip_list& other ) { 353 if (this != &other) { 354 clear(); 355 copy_assign_allocators(my_node_allocator, other.my_node_allocator); 356 my_compare = other.my_compare; 357 my_rng = other.my_rng; 358 internal_copy(other); 359 } 360 return *this; 361 } 362 363 concurrent_skip_list& operator=( concurrent_skip_list&& other ) { 364 if (this != &other) { 365 clear(); 366 delete_head(); 367 368 my_compare = std::move(other.my_compare); 369 my_rng = std::move(other.my_rng); 370 371 move_assign_allocators(my_node_allocator, other.my_node_allocator); 372 using pocma_type = typename node_allocator_traits::propagate_on_container_move_assignment; 373 using is_always_equal = typename node_allocator_traits::is_always_equal; 374 internal_move_assign(std::move(other), tbb::detail::disjunction<pocma_type, is_always_equal>()); 375 } 376 return *this; 377 } 378 379 concurrent_skip_list& operator=( std::initializer_list<value_type> il ) 380 { 381 clear(); 382 insert(il.begin(),il.end()); 383 return *this; 384 } 385 386 std::pair<iterator, bool> insert( const value_type& value ) { 387 return internal_insert(value); 388 } 389 390 std::pair<iterator, bool> insert( value_type&& value ) { 391 return internal_insert(std::move(value)); 392 } 393 394 iterator insert( const_iterator, const_reference value ) { 395 // Ignore hint 396 return insert(value).first; 397 } 398 399 iterator insert( const_iterator, value_type&& value ) { 400 // Ignore hint 401 return insert(std::move(value)).first; 402 } 403 404 template<typename InputIterator> 405 void insert( InputIterator first, InputIterator last ) { 406 while (first != last) { 407 insert(*first); 408 ++first; 409 } 410 } 411 412 void insert( std::initializer_list<value_type> init ) { 413 insert(init.begin(), init.end()); 414 } 415 416 std::pair<iterator, bool> insert( node_type&& nh ) { 417 if (!nh.empty()) { 418 auto insert_node = d1::node_handle_accessor::get_node_ptr(nh); 419 std::pair<iterator, bool> insert_result = internal_insert_node(insert_node); 420 if (insert_result.second) { 421 d1::node_handle_accessor::deactivate(nh); 422 } 423 return insert_result; 424 } 425 return std::pair<iterator, bool>(end(), false); 426 } 427 428 iterator insert( const_iterator, node_type&& nh ) { 429 // Ignore hint 430 return insert(std::move(nh)).first; 431 } 432 433 template<typename... Args> 434 std::pair<iterator, bool> emplace( Args&&... args ) { 435 return internal_insert(std::forward<Args>(args)...); 436 } 437 438 template<typename... Args> 439 iterator emplace_hint( const_iterator, Args&&... args ) { 440 // Ignore hint 441 return emplace(std::forward<Args>(args)...).first; 442 } 443 444 iterator unsafe_erase( iterator pos ) { 445 std::pair<node_ptr, node_ptr> extract_result = internal_extract(pos); 446 if (extract_result.first) { // node was extracted 447 delete_value_node(extract_result.first); 448 return extract_result.second; 449 } 450 return end(); 451 } 452 453 iterator unsafe_erase( const_iterator pos ) { 454 return unsafe_erase(get_iterator(pos)); 455 } 456 457 iterator unsafe_erase( const_iterator first, const_iterator last ) { 458 while (first != last) { 459 // Unsafe erase returns the iterator which follows the erased one 460 first = unsafe_erase(first); 461 } 462 return get_iterator(first); 463 } 464 465 size_type unsafe_erase( const key_type& key ) { 466 return internal_erase(key); 467 } 468 469 template <typename K> 470 typename std::enable_if<is_transparent<K>::value 471 && !std::is_convertible<K, const_iterator>::value 472 && !std::is_convertible<K, iterator>::value, 473 size_type>::type unsafe_erase( const K& key ) 474 { 475 return internal_erase(key); 476 } 477 478 node_type unsafe_extract( const_iterator pos ) { 479 std::pair<node_ptr, node_ptr> extract_result = internal_extract(pos); 480 return extract_result.first ? d1::node_handle_accessor::construct<node_type>(extract_result.first) : node_type(); 481 } 482 483 node_type unsafe_extract( iterator pos ) { 484 return unsafe_extract(const_iterator(pos)); 485 } 486 487 node_type unsafe_extract( const key_type& key ) { 488 return unsafe_extract(find(key)); 489 } 490 491 template <typename K> 492 typename std::enable_if<is_transparent<K>::value 493 && !std::is_convertible<K, const_iterator>::value 494 && !std::is_convertible<K, iterator>::value, 495 node_type>::type unsafe_extract( const K& key ) 496 { 497 return unsafe_extract(find(key)); 498 } 499 500 iterator lower_bound( const key_type& key ) { 501 return iterator(internal_get_bound(key, my_compare)); 502 } 503 504 const_iterator lower_bound( const key_type& key ) const { 505 return const_iterator(internal_get_bound(key, my_compare)); 506 } 507 508 template <typename K> 509 typename std::enable_if<is_transparent<K>::value, iterator>::type lower_bound( const K& key ) { 510 return iterator(internal_get_bound(key, my_compare)); 511 } 512 513 template <typename K> 514 typename std::enable_if<is_transparent<K>::value, const_iterator>::type lower_bound( const K& key ) const { 515 return const_iterator(internal_get_bound(key, my_compare)); 516 } 517 518 iterator upper_bound( const key_type& key ) { 519 return iterator(internal_get_bound(key, not_greater_compare(my_compare))); 520 } 521 522 const_iterator upper_bound( const key_type& key ) const { 523 return const_iterator(internal_get_bound(key, not_greater_compare(my_compare))); 524 } 525 526 template <typename K> 527 typename std::enable_if<is_transparent<K>::value, iterator>::type upper_bound( const K& key ) { 528 return iterator(internal_get_bound(key, not_greater_compare(my_compare))); 529 } 530 531 template <typename K> 532 typename std::enable_if<is_transparent<K>::value, const_iterator>::type upper_bound( const K& key ) const { 533 return const_iterator(internal_get_bound(key, not_greater_compare(my_compare))); 534 } 535 536 iterator find( const key_type& key ) { 537 return iterator(internal_find(key)); 538 } 539 540 const_iterator find( const key_type& key ) const { 541 return const_iterator(internal_find(key)); 542 } 543 544 template <typename K> 545 typename std::enable_if<is_transparent<K>::value, iterator>::type find( const K& key ) { 546 return iterator(internal_find(key)); 547 } 548 549 template <typename K> 550 typename std::enable_if<is_transparent<K>::value, const_iterator>::type find( const K& key ) const { 551 return const_iterator(internal_find(key)); 552 } 553 554 size_type count( const key_type& key ) const { 555 return internal_count(key); 556 } 557 558 template <typename K> 559 typename std::enable_if<is_transparent<K>::value, size_type>::type count( const K& key ) const { 560 return internal_count(key); 561 } 562 563 bool contains( const key_type& key ) const { 564 return find(key) != end(); 565 } 566 567 template <typename K> 568 typename std::enable_if<is_transparent<K>::value, bool>::type contains( const K& key ) const { 569 return find(key) != end(); 570 } 571 572 void clear() noexcept { 573 // clear is not thread safe - load can be relaxed 574 node_ptr head = my_head_ptr.load(std::memory_order_relaxed); 575 576 if (head == nullptr) return; // Head is not allocated => container is empty 577 578 node_ptr current = head->next(0); 579 580 // Delete all value nodes in the container 581 while (current) { 582 node_ptr next = current->next(0); 583 delete_value_node(current); 584 current = next; 585 } 586 587 for (size_type level = 0; level < head->height(); ++level) { 588 head->set_next(level, nullptr); 589 } 590 591 my_size.store(0, std::memory_order_relaxed); 592 my_max_height.store(0, std::memory_order_relaxed); 593 } 594 595 iterator begin() { 596 return iterator(internal_begin()); 597 } 598 599 const_iterator begin() const { 600 return const_iterator(internal_begin()); 601 } 602 603 const_iterator cbegin() const { 604 return const_iterator(internal_begin()); 605 } 606 607 iterator end() { 608 return iterator(nullptr); 609 } 610 611 const_iterator end() const { 612 return const_iterator(nullptr); 613 } 614 615 const_iterator cend() const { 616 return const_iterator(nullptr); 617 } 618 619 size_type size() const { 620 return my_size.load(std::memory_order_relaxed); 621 } 622 623 size_type max_size() const { 624 return node_allocator_traits::max_size(my_node_allocator); 625 } 626 627 __TBB_nodiscard bool empty() const { 628 return 0 == size(); 629 } 630 631 allocator_type get_allocator() const { 632 return my_node_allocator; 633 } 634 635 void swap(concurrent_skip_list& other) { 636 if (this != &other) { 637 using pocs_type = typename node_allocator_traits::propagate_on_container_swap; 638 using is_always_equal = typename node_allocator_traits::is_always_equal; 639 internal_swap(other, tbb::detail::disjunction<pocs_type, is_always_equal>()); 640 } 641 } 642 643 std::pair<iterator, iterator> equal_range(const key_type& key) { 644 return internal_equal_range(key); 645 } 646 647 std::pair<const_iterator, const_iterator> equal_range(const key_type& key) const { 648 return internal_equal_range(key); 649 } 650 651 template <typename K> 652 typename std::enable_if<is_transparent<K>::value, std::pair<iterator, iterator>>::type equal_range( const K& key ) { 653 return internal_equal_range(key); 654 } 655 656 template <typename K> 657 typename std::enable_if<is_transparent<K>::value, std::pair<const_iterator, const_iterator>>::type equal_range( const K& key ) const { 658 return internal_equal_range(key); 659 } 660 661 key_compare key_comp() const { return my_compare; } 662 663 value_compare value_comp() const { return container_traits::value_comp(my_compare); } 664 665 class const_range_type { 666 public: 667 using size_type = typename concurrent_skip_list::size_type; 668 using difference_type = typename concurrent_skip_list::difference_type; 669 using iterator = typename concurrent_skip_list::const_iterator; 670 using value_type = typename iterator::value_type; 671 using reference = typename iterator::reference; 672 673 bool empty() const { 674 return my_begin.my_node_ptr->next(0) == my_end.my_node_ptr; 675 } 676 677 bool is_divisible() const { 678 return my_level != 0 ? my_begin.my_node_ptr->next(my_level - 1) != my_end.my_node_ptr : false; 679 } 680 681 size_type size() const { return std::distance(my_begin, my_end); } 682 683 const_range_type( const_range_type& r, split) 684 : my_end(r.my_end) { 685 my_begin = iterator(r.my_begin.my_node_ptr->next(r.my_level - 1)); 686 my_level = my_begin.my_node_ptr->height(); 687 r.my_end = my_begin; 688 } 689 690 const_range_type( const concurrent_skip_list& l) 691 : my_end(l.end()), my_begin(l.begin()), my_level(my_begin.my_node_ptr->height() ) {} 692 693 iterator begin() const { return my_begin; } 694 iterator end() const { return my_end; } 695 size_type grainsize() const { return 1; } 696 697 private: 698 const_iterator my_end; 699 const_iterator my_begin; 700 size_type my_level; 701 }; // class const_range_type 702 703 class range_type : public const_range_type { 704 public: 705 using iterator = typename concurrent_skip_list::iterator; 706 using value_type = typename iterator::value_type; 707 using reference = typename iterator::reference; 708 709 range_type(range_type& r, split) : const_range_type(r, split()) {} 710 range_type(const concurrent_skip_list& l) : const_range_type(l) {} 711 712 iterator begin() const { 713 node_ptr node = const_range_type::begin().my_node_ptr; 714 return iterator(node); 715 } 716 717 iterator end() const { 718 node_ptr node = const_range_type::end().my_node_ptr; 719 return iterator(node); 720 } 721 }; // class range_type 722 723 range_type range() { return range_type(*this); } 724 const_range_type range() const { return const_range_type(*this); } 725 726 private: 727 node_ptr internal_begin() const { 728 node_ptr head = get_head(); 729 return head == nullptr ? head : head->next(0); 730 } 731 732 void internal_move(concurrent_skip_list&& other) { 733 my_head_ptr.store(other.my_head_ptr.load(std::memory_order_relaxed), std::memory_order_relaxed); 734 other.my_head_ptr.store(nullptr, std::memory_order_relaxed); 735 736 my_size.store(other.my_size.load(std::memory_order_relaxed), std::memory_order_relaxed); 737 other.my_size.store(0, std::memory_order_relaxed); 738 739 my_max_height.store(other.my_max_height.load(std::memory_order_relaxed), std::memory_order_relaxed); 740 other.my_max_height.store(0, std::memory_order_relaxed); 741 } 742 743 void internal_move_construct_with_allocator(concurrent_skip_list&& other, 744 /*is_always_equal = */std::true_type) { 745 internal_move(std::move(other)); 746 } 747 748 void internal_move_construct_with_allocator(concurrent_skip_list&& other, 749 /*is_always_equal = */std::false_type) { 750 if (my_node_allocator == other.get_allocator()) { 751 internal_move(std::move(other)); 752 } else { 753 my_size.store(0, std::memory_order_relaxed); 754 my_max_height.store(other.my_max_height.load(std::memory_order_relaxed), std::memory_order_relaxed); 755 internal_copy(std::make_move_iterator(other.begin()), std::make_move_iterator(other.end())); 756 } 757 } 758 759 static const key_type& get_key( node_ptr n ) { 760 __TBB_ASSERT(n, nullptr); 761 return container_traits::get_key(static_cast<node_ptr>(n)->value()); 762 } 763 764 template <typename K> 765 bool found( node_ptr node, const K& key ) const { 766 return node != nullptr && !my_compare(key, get_key(node)); 767 } 768 769 template <typename K> 770 node_ptr internal_find(const K& key) const { 771 return allow_multimapping ? internal_find_multi(key) : internal_find_unique(key); 772 } 773 774 template <typename K> 775 node_ptr internal_find_multi( const K& key ) const { 776 node_ptr prev = get_head(); 777 if (prev == nullptr) return nullptr; // If the head node is not allocated - exit 778 779 node_ptr curr = nullptr; 780 node_ptr old_curr = curr; 781 782 for (size_type h = my_max_height.load(std::memory_order_acquire); h > 0; --h) { 783 curr = internal_find_position(h - 1, prev, key, my_compare); 784 785 if (curr != old_curr && found(curr, key)) { 786 return curr; 787 } 788 old_curr = curr; 789 } 790 return nullptr; 791 } 792 793 template <typename K> 794 node_ptr internal_find_unique( const K& key ) const { 795 const_iterator it = lower_bound(key); 796 return (it == end() || my_compare(key, container_traits::get_key(*it))) ? nullptr : it.my_node_ptr; 797 } 798 799 template <typename K> 800 size_type internal_count( const K& key ) const { 801 if (allow_multimapping) { 802 // TODO: reimplement without double traversal 803 std::pair<const_iterator, const_iterator> r = equal_range(key); 804 return std::distance(r.first, r.second); 805 } 806 return size_type(contains(key) ? 1 : 0); 807 } 808 809 template <typename K> 810 std::pair<iterator, iterator> internal_equal_range(const K& key) const { 811 iterator lb = get_iterator(lower_bound(key)); 812 auto result = std::make_pair(lb, lb); 813 814 // If the lower bound points to the node with the requested key 815 if (found(lb.my_node_ptr, key)) { 816 817 if (!allow_multimapping) { 818 // For unique containers - move the second iterator forward and exit 819 ++result.second; 820 } else { 821 // For multi containers - find the upper bound starting from the lower bound 822 node_ptr prev = lb.my_node_ptr; 823 node_ptr curr = nullptr; 824 not_greater_compare cmp(my_compare); 825 826 // Start from the lower bound of the range 827 for (size_type h = prev->height(); h > 0; --h) { 828 curr = prev->next(h - 1); 829 while (curr && cmp(get_key(curr), key)) { 830 prev = curr; 831 // If the height of the next node is greater than the current one - jump to its height 832 if (h < curr->height()) { 833 h = curr->height(); 834 } 835 curr = prev->next(h - 1); 836 } 837 } 838 result.second = iterator(curr); 839 } 840 } 841 842 return result; 843 } 844 845 // Finds position on the level using comparator cmp starting from the node prev 846 template <typename K, typename Comparator> 847 node_ptr internal_find_position( size_type level, node_ptr& prev, const K& key, 848 const Comparator& cmp ) const { 849 __TBB_ASSERT(level < prev->height(), "Wrong level to find position"); 850 node_ptr curr = prev->next(level); 851 852 while (curr && cmp(get_key(curr), key)) { 853 prev = curr; 854 __TBB_ASSERT(level < prev->height(), nullptr); 855 curr = prev->next(level); 856 } 857 858 return curr; 859 } 860 861 // The same as previous overload, but allows index_number comparison 862 template <typename Comparator> 863 node_ptr internal_find_position( size_type level, node_ptr& prev, node_ptr node, 864 const Comparator& cmp ) const { 865 __TBB_ASSERT(level < prev->height(), "Wrong level to find position"); 866 node_ptr curr = prev->next(level); 867 868 while (curr && cmp(get_key(curr), get_key(node))) { 869 if (allow_multimapping && cmp(get_key(node), get_key(curr)) && curr->index_number() > node->index_number()) { 870 break; 871 } 872 873 prev = curr; 874 __TBB_ASSERT(level < prev->height(), nullptr); 875 curr = prev->next(level); 876 } 877 return curr; 878 } 879 880 template <typename Comparator> 881 void fill_prev_curr_arrays(array_type& prev_nodes, array_type& curr_nodes, node_ptr node, const key_type& key, 882 const Comparator& cmp, node_ptr head ) { 883 884 size_type curr_max_height = my_max_height.load(std::memory_order_acquire); 885 size_type node_height = node->height(); 886 if (curr_max_height < node_height) { 887 std::fill(prev_nodes.begin() + curr_max_height, prev_nodes.begin() + node_height, head); 888 std::fill(curr_nodes.begin() + curr_max_height, curr_nodes.begin() + node_height, nullptr); 889 } 890 891 node_ptr prev = head; 892 for (size_type level = curr_max_height; level > 0; --level) { 893 node_ptr curr = internal_find_position(level - 1, prev, key, cmp); 894 prev_nodes[level - 1] = prev; 895 curr_nodes[level - 1] = curr; 896 } 897 } 898 899 void fill_prev_array_for_existing_node( array_type& prev_nodes, node_ptr node ) { 900 node_ptr head = create_head_if_necessary(); 901 prev_nodes.fill(head); 902 903 node_ptr prev = head; 904 for (size_type level = node->height(); level > 0; --level) { 905 while (prev->next(level - 1) != node) { 906 prev = prev->next(level - 1); 907 } 908 prev_nodes[level - 1] = prev; 909 } 910 } 911 912 struct not_greater_compare { 913 const key_compare& my_less_compare; 914 915 not_greater_compare( const key_compare& less_compare ) : my_less_compare(less_compare) {} 916 917 template <typename K1, typename K2> 918 bool operator()( const K1& first, const K2& second ) const { 919 return !my_less_compare(second, first); 920 } 921 }; 922 923 not_greater_compare select_comparator( /*allow_multimapping = */ std::true_type ) { 924 return not_greater_compare(my_compare); 925 } 926 927 key_compare select_comparator( /*allow_multimapping = */ std::false_type ) { 928 return my_compare; 929 } 930 931 template<typename... Args> 932 std::pair<iterator, bool> internal_insert( Args&&... args ) { 933 node_ptr new_node = create_value_node(std::forward<Args>(args)...); 934 std::pair<iterator, bool> insert_result = internal_insert_node(new_node); 935 if (!insert_result.second) { 936 delete_value_node(new_node); 937 } 938 return insert_result; 939 } 940 941 std::pair<iterator, bool> internal_insert_node( node_ptr new_node ) { 942 array_type prev_nodes; 943 array_type curr_nodes; 944 size_type new_height = new_node->height(); 945 auto compare = select_comparator(std::integral_constant<bool, allow_multimapping>{}); 946 947 node_ptr head_node = create_head_if_necessary(); 948 949 for (;;) { 950 fill_prev_curr_arrays(prev_nodes, curr_nodes, new_node, get_key(new_node), compare, head_node); 951 952 node_ptr prev = prev_nodes[0]; 953 node_ptr next = curr_nodes[0]; 954 955 if (allow_multimapping) { 956 new_node->set_index_number(prev->index_number() + 1); 957 } else { 958 if (found(next, get_key(new_node))) { 959 return std::pair<iterator, bool>(iterator(next), false); 960 } 961 } 962 963 new_node->set_next(0, next); 964 if (!prev->atomic_next(0).compare_exchange_strong(next, new_node)) { 965 continue; 966 } 967 968 // If the node was successfully linked on the first level - it will be linked on other levels 969 // Insertion cannot fail starting from this point 970 971 // If the height of inserted node is greater than maximum - increase maximum 972 size_type max_height = my_max_height.load(std::memory_order_acquire); 973 for (;;) { 974 if (new_height <= max_height || my_max_height.compare_exchange_strong(max_height, new_height)) { 975 // If the maximum was successfully updated by current thread 976 // or by an other thread for the value, greater or equal to new_height 977 break; 978 } 979 } 980 981 for (std::size_t level = 1; level < new_height; ++level) { 982 // Link the node on upper levels 983 for (;;) { 984 prev = prev_nodes[level]; 985 next = static_cast<node_ptr>(curr_nodes[level]); 986 987 new_node->set_next(level, next); 988 __TBB_ASSERT(new_node->height() > level, "Internal structure break"); 989 if (prev->atomic_next(level).compare_exchange_strong(next, new_node)) { 990 break; 991 } 992 993 for (size_type lev = level; lev != new_height; ++lev ) { 994 curr_nodes[lev] = internal_find_position(lev, prev_nodes[lev], new_node, compare); 995 } 996 } 997 } 998 ++my_size; 999 return std::pair<iterator, bool>(iterator(new_node), true); 1000 } 1001 } 1002 1003 template <typename K, typename Comparator> 1004 node_ptr internal_get_bound( const K& key, const Comparator& cmp ) const { 1005 node_ptr prev = get_head(); 1006 if (prev == nullptr) return nullptr; // If the head node is not allocated - exit 1007 1008 node_ptr curr = nullptr; 1009 1010 for (size_type h = my_max_height.load(std::memory_order_acquire); h > 0; --h) { 1011 curr = internal_find_position(h - 1, prev, key, cmp); 1012 } 1013 1014 return curr; 1015 } 1016 1017 template <typename K> 1018 size_type internal_erase( const K& key ) { 1019 auto eq = equal_range(key); 1020 size_type old_size = size(); 1021 unsafe_erase(eq.first, eq.second); 1022 return old_size - size(); 1023 } 1024 1025 // Returns node_ptr to the extracted node and node_ptr to the next node after the extracted 1026 std::pair<node_ptr, node_ptr> internal_extract( const_iterator it ) { 1027 std::pair<node_ptr, node_ptr> result(nullptr, nullptr); 1028 if ( it != end() ) { 1029 array_type prev_nodes; 1030 1031 node_ptr erase_node = it.my_node_ptr; 1032 node_ptr next_node = erase_node->next(0); 1033 fill_prev_array_for_existing_node(prev_nodes, erase_node); 1034 1035 for (size_type level = 0; level < erase_node->height(); ++level) { 1036 prev_nodes[level]->set_next(level, erase_node->next(level)); 1037 erase_node->set_next(level, nullptr); 1038 } 1039 my_size.fetch_sub(1, std::memory_order_relaxed); 1040 1041 result.first = erase_node; 1042 result.second = next_node; 1043 } 1044 return result; 1045 } 1046 1047 protected: 1048 template<typename SourceType> 1049 void internal_merge( SourceType&& source ) { 1050 using source_type = typename std::decay<SourceType>::type; 1051 using source_iterator = typename source_type::iterator; 1052 static_assert((std::is_same<node_type, typename source_type::node_type>::value), "Incompatible containers cannot be merged"); 1053 1054 for (source_iterator it = source.begin(); it != source.end();) { 1055 source_iterator where = it++; 1056 if (allow_multimapping || !contains(container_traits::get_key(*where))) { 1057 node_type handle = source.unsafe_extract(where); 1058 __TBB_ASSERT(!handle.empty(), "Extracted handle in merge is empty"); 1059 1060 if (!insert(std::move(handle)).second) { 1061 //If the insertion fails - return the node into source 1062 source.insert(std::move(handle)); 1063 } 1064 __TBB_ASSERT(handle.empty(), "Node handle should be empty after the insertion"); 1065 } 1066 } 1067 } 1068 1069 private: 1070 void internal_copy( const concurrent_skip_list& other ) { 1071 internal_copy(other.begin(), other.end()); 1072 } 1073 1074 template<typename Iterator> 1075 void internal_copy( Iterator first, Iterator last ) { 1076 try_call([&] { 1077 for (auto it = first; it != last; ++it) { 1078 insert(*it); 1079 } 1080 }).on_exception([&] { 1081 clear(); 1082 delete_head(); 1083 }); 1084 } 1085 1086 node_ptr create_node( size_type height ) { 1087 return list_node_type::create(my_node_allocator, height); 1088 } 1089 1090 template <typename... Args> 1091 node_ptr create_value_node( Args&&... args ) { 1092 node_ptr node = create_node(my_rng()); 1093 1094 // try_call API is not convenient here due to broken 1095 // variadic capture on GCC 4.8.5 1096 auto value_guard = make_raii_guard([&] { 1097 delete_node(node); 1098 }); 1099 1100 // Construct the value inside the node 1101 node_allocator_traits::construct(my_node_allocator, node->storage(), std::forward<Args>(args)...); 1102 value_guard.dismiss(); 1103 return node; 1104 } 1105 1106 node_ptr create_head_node() { 1107 return create_node(max_level); 1108 } 1109 1110 void delete_head() { 1111 node_ptr head = my_head_ptr.load(std::memory_order_relaxed); 1112 if (head != nullptr) { 1113 delete_node(head); 1114 my_head_ptr.store(nullptr, std::memory_order_relaxed); 1115 } 1116 } 1117 1118 void delete_node( node_ptr node ) { 1119 list_node_type::destroy(my_node_allocator, node); 1120 } 1121 1122 void delete_value_node( node_ptr node ) { 1123 // Destroy the value inside the node 1124 node_allocator_traits::destroy(my_node_allocator, node->storage()); 1125 delete_node(node); 1126 } 1127 1128 node_ptr get_head() const { 1129 return my_head_ptr.load(std::memory_order_acquire); 1130 } 1131 1132 node_ptr create_head_if_necessary() { 1133 node_ptr current_head = get_head(); 1134 if (current_head == nullptr) { 1135 // Head node was not created - create it 1136 node_ptr new_head = create_head_node(); 1137 if (my_head_ptr.compare_exchange_strong(current_head, new_head)) { 1138 current_head = new_head; 1139 } else { 1140 // If an other thread has already created the head node - destroy new_head 1141 // current_head now points to the actual head node 1142 delete_node(new_head); 1143 } 1144 } 1145 __TBB_ASSERT(my_head_ptr.load(std::memory_order_relaxed) != nullptr, nullptr); 1146 __TBB_ASSERT(current_head != nullptr, nullptr); 1147 return current_head; 1148 } 1149 1150 static iterator get_iterator( const_iterator it ) { 1151 return iterator(it.my_node_ptr); 1152 } 1153 1154 void internal_move_assign( concurrent_skip_list&& other, /*POCMA || is_always_equal =*/std::true_type ) { 1155 internal_move(std::move(other)); 1156 } 1157 1158 void internal_move_assign( concurrent_skip_list&& other, /*POCMA || is_always_equal =*/std::false_type ) { 1159 if (my_node_allocator == other.my_node_allocator) { 1160 internal_move(std::move(other)); 1161 } else { 1162 internal_copy(std::make_move_iterator(other.begin()), std::make_move_iterator(other.end())); 1163 } 1164 } 1165 1166 void internal_swap_fields( concurrent_skip_list& other ) { 1167 using std::swap; 1168 swap_allocators(my_node_allocator, other.my_node_allocator); 1169 swap(my_compare, other.my_compare); 1170 swap(my_rng, other.my_rng); 1171 1172 swap_atomics_relaxed(my_head_ptr, other.my_head_ptr); 1173 swap_atomics_relaxed(my_size, other.my_size); 1174 swap_atomics_relaxed(my_max_height, other.my_max_height); 1175 } 1176 1177 void internal_swap( concurrent_skip_list& other, /*POCMA || is_always_equal =*/std::true_type ) { 1178 internal_swap_fields(other); 1179 } 1180 1181 void internal_swap( concurrent_skip_list& other, /*POCMA || is_always_equal =*/std::false_type ) { 1182 __TBB_ASSERT(my_node_allocator == other.my_node_allocator, "Swapping with unequal allocators is not allowed"); 1183 internal_swap_fields(other); 1184 } 1185 1186 node_allocator_type my_node_allocator; 1187 key_compare my_compare; 1188 random_level_generator_type my_rng; 1189 std::atomic<list_node_type*> my_head_ptr; 1190 std::atomic<size_type> my_size; 1191 std::atomic<size_type> my_max_height; 1192 1193 template<typename OtherTraits> 1194 friend class concurrent_skip_list; 1195 }; // class concurrent_skip_list 1196 1197 template <typename Traits> 1198 bool operator==( const concurrent_skip_list<Traits>& lhs, const concurrent_skip_list<Traits>& rhs ) { 1199 if (lhs.size() != rhs.size()) return false; 1200 #if _MSC_VER 1201 // Passing "unchecked" iterators to std::equal with 3 parameters 1202 // causes compiler warnings. 1203 // The workaround is to use overload with 4 parameters, which is 1204 // available since C++14 - minimally supported version on MSVC 1205 return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); 1206 #else 1207 return std::equal(lhs.begin(), lhs.end(), rhs.begin()); 1208 #endif 1209 } 1210 1211 #if !__TBB_CPP20_COMPARISONS_PRESENT 1212 template <typename Traits> 1213 bool operator!=( const concurrent_skip_list<Traits>& lhs, const concurrent_skip_list<Traits>& rhs ) { 1214 return !(lhs == rhs); 1215 } 1216 #endif 1217 1218 #if __TBB_CPP20_COMPARISONS_PRESENT && __TBB_CPP20_CONCEPTS_PRESENT 1219 template <typename Traits> 1220 tbb::detail::synthesized_three_way_result<typename Traits::value_type> 1221 operator<=>( const concurrent_skip_list<Traits>& lhs, const concurrent_skip_list<Traits>& rhs ) { 1222 return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), 1223 rhs.begin(), rhs.end(), 1224 tbb::detail::synthesized_three_way_comparator{}); 1225 } 1226 #else 1227 template <typename Traits> 1228 bool operator<( const concurrent_skip_list<Traits>& lhs, const concurrent_skip_list<Traits>& rhs ) { 1229 return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); 1230 } 1231 1232 template <typename Traits> 1233 bool operator>( const concurrent_skip_list<Traits>& lhs, const concurrent_skip_list<Traits>& rhs ) { 1234 return rhs < lhs; 1235 } 1236 1237 template <typename Traits> 1238 bool operator<=( const concurrent_skip_list<Traits>& lhs, const concurrent_skip_list<Traits>& rhs ) { 1239 return !(rhs < lhs); 1240 } 1241 1242 template <typename Traits> 1243 bool operator>=( const concurrent_skip_list<Traits>& lhs, const concurrent_skip_list<Traits>& rhs ) { 1244 return !(lhs < rhs); 1245 } 1246 #endif // __TBB_CPP20_COMPARISONS_PRESENT && __TBB_CPP20_CONCEPTS_PRESENT 1247 1248 // Generates a number from the interval [0, MaxLevel). 1249 template <std::size_t MaxLevel> 1250 class concurrent_geometric_level_generator { 1251 public: 1252 static constexpr std::size_t max_level = MaxLevel; 1253 // TODO: modify the algorithm to accept other values of max_level 1254 static_assert(max_level == 32, "Incompatible max_level for rng"); 1255 1256 concurrent_geometric_level_generator() : engines(std::minstd_rand::result_type(time(nullptr))) {} 1257 1258 std::size_t operator()() { 1259 // +1 is required to pass at least 1 into log2 (log2(0) is undefined) 1260 // -1 is required to have an ability to return 0 from the generator (max_level - log2(2^31) - 1) 1261 std::size_t result = max_level - std::size_t(tbb::detail::log2(engines.local()() + 1)) - 1; 1262 __TBB_ASSERT(result <= max_level, nullptr); 1263 return result; 1264 } 1265 1266 private: 1267 tbb::enumerable_thread_specific<std::minstd_rand> engines; 1268 }; 1269 1270 } // namespace d2 1271 1272 } // namespace detail 1273 } // namespace tbb 1274 1275 #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) 1276 #pragma warning(pop) // warning 4127 is back 1277 #endif 1278 1279 #endif // __TBB_detail__concurrent_skip_list_H 1280