Back to home page

EIC code displayed by LXR

 
 

    


Warning, file /include/oneapi/tbb/concurrent_vector.h was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     Copyright (c) 2005-2022 Intel Corporation
0003 
0004     Licensed under the Apache License, Version 2.0 (the "License");
0005     you may not use this file except in compliance with the License.
0006     You may obtain a copy of the License at
0007 
0008         http://www.apache.org/licenses/LICENSE-2.0
0009 
0010     Unless required by applicable law or agreed to in writing, software
0011     distributed under the License is distributed on an "AS IS" BASIS,
0012     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013     See the License for the specific language governing permissions and
0014     limitations under the License.
0015 */
0016 
0017 #ifndef __TBB_concurrent_vector_H
0018 #define __TBB_concurrent_vector_H
0019 
0020 #include "detail/_namespace_injection.h"
0021 #include "detail/_utils.h"
0022 #include "detail/_assert.h"
0023 #include "detail/_allocator_traits.h"
0024 #include "detail/_segment_table.h"
0025 #include "detail/_containers_helpers.h"
0026 #include "blocked_range.h"
0027 #include "cache_aligned_allocator.h"
0028 
0029 #include <algorithm>
0030 #include <utility> // std::move_if_noexcept
0031 #include <algorithm>
0032 #if __TBB_CPP20_COMPARISONS_PRESENT
0033 #include <compare>
0034 #endif
0035 
0036 namespace tbb {
0037 namespace detail {
0038 namespace d1 {
0039 
0040 template <typename Vector, typename Value>
0041 class vector_iterator {
0042     using vector_type = Vector;
0043 
0044 public:
0045     using value_type = Value;
0046     using size_type = typename vector_type::size_type;
0047     using difference_type = typename vector_type::difference_type;
0048     using pointer = value_type*;
0049     using reference = value_type&;
0050     using iterator_category = std::random_access_iterator_tag;
0051 
0052     template <typename Vec, typename Val>
0053     friend vector_iterator<Vec, Val> operator+( typename vector_iterator<Vec, Val>::difference_type, const vector_iterator<Vec, Val>& );
0054 
0055     template <typename Vec, typename Val1, typename Val2>
0056     friend typename vector_iterator<Vec, Val1>::difference_type operator-( const vector_iterator<Vec, Val1>&, const vector_iterator<Vec, Val2>& );
0057 
0058     template <typename Vec, typename Val1, typename Val2>
0059     friend bool operator==( const vector_iterator<Vec, Val1>&, const vector_iterator<Vec, Val2>& );
0060 
0061     template <typename Vec, typename Val1, typename Val2>
0062     friend bool operator<( const vector_iterator<Vec, Val1>&, const vector_iterator<Vec, Val2>& );
0063 
0064     template <typename Vec, typename Val>
0065     friend class vector_iterator;
0066 
0067     template <typename T, typename Allocator>
0068     friend class concurrent_vector;
0069 
0070 private:
0071     vector_iterator( const vector_type& vector, size_type index, value_type* item = nullptr )
0072         : my_vector(const_cast<vector_type*>(&vector)), my_index(index), my_item(item)
0073     {}
0074 
0075 public:
0076     vector_iterator() : my_vector(nullptr), my_index(~size_type(0)), my_item(nullptr)
0077     {}
0078 
0079     vector_iterator( const vector_iterator<vector_type, typename vector_type::value_type>& other )
0080         : my_vector(other.my_vector), my_index(other.my_index), my_item(other.my_item)
0081     {}
0082 
0083     vector_iterator& operator=( const vector_iterator<vector_type, typename vector_type::value_type>& other ) {
0084         my_vector = other.my_vector;
0085         my_index = other.my_index;
0086         my_item = other.my_item;
0087         return *this;
0088     }
0089 
0090     vector_iterator operator+( difference_type offset ) const {
0091         return vector_iterator(*my_vector, my_index + offset);
0092     }
0093 
0094     vector_iterator& operator+=( difference_type offset ) {
0095         my_index += offset;
0096         my_item = nullptr;
0097         return *this;
0098     }
0099 
0100     vector_iterator operator-( difference_type offset ) const {
0101         return vector_iterator(*my_vector, my_index - offset);
0102     }
0103 
0104     vector_iterator& operator-=( difference_type offset ) {
0105         my_index -= offset;
0106         my_item = nullptr;
0107         return *this;
0108     }
0109 
0110     reference operator*() const {
0111         value_type *item = my_item;
0112         if (item == nullptr) {
0113             item = &my_vector->internal_subscript(my_index);
0114         } else {
0115             __TBB_ASSERT(item == &my_vector->internal_subscript(my_index), "corrupt cache");
0116         }
0117         return *item;
0118     }
0119 
0120     pointer operator->() const { return &(operator*()); }
0121 
0122     reference operator[]( difference_type k ) const {
0123         return my_vector->internal_subscript(my_index + k);
0124     }
0125 
0126     vector_iterator& operator++() {
0127         ++my_index;
0128         if (my_item != nullptr) {
0129             if (vector_type::is_first_element_in_segment(my_index)) {
0130                 // If the iterator crosses a segment boundary, the pointer become invalid
0131                 // as possibly next segment is in another memory location
0132                 my_item = nullptr;
0133             } else {
0134                 ++my_item;
0135             }
0136         }
0137         return *this;
0138     }
0139 
0140     vector_iterator operator++(int) {
0141         vector_iterator result = *this;
0142         ++(*this);
0143         return result;
0144     }
0145 
0146     vector_iterator& operator--() {
0147         __TBB_ASSERT(my_index > 0, "operator--() applied to iterator already at beginning of concurrent_vector");
0148         --my_index;
0149         if (my_item != nullptr) {
0150             if (vector_type::is_first_element_in_segment(my_index)) {
0151                 // If the iterator crosses a segment boundary, the pointer become invalid
0152                 // as possibly next segment is in another memory location
0153                 my_item = nullptr;
0154             } else {
0155                 --my_item;
0156             }
0157         }
0158         return *this;
0159     }
0160 
0161     vector_iterator operator--(int) {
0162         vector_iterator result = *this;
0163         --(*this);
0164         return result;
0165     }
0166 
0167 private:
0168     // concurrent_vector over which we are iterating.
0169     vector_type* my_vector;
0170 
0171     // Index into the vector
0172     size_type my_index;
0173 
0174     // Caches my_vector *it;
0175     // If my_item == nullptr cached value is not available use internal_subscript(my_index)
0176     mutable value_type* my_item;
0177 }; // class vector_iterator
0178 
0179 template <typename Vector, typename T>
0180 vector_iterator<Vector, T> operator+( typename vector_iterator<Vector, T>::difference_type offset,
0181                                       const vector_iterator<Vector, T>& v )
0182 {
0183     return vector_iterator<Vector, T>(*v.my_vector, v.my_index + offset);
0184 }
0185 
0186 template <typename Vector, typename T, typename U>
0187 typename vector_iterator<Vector, T>::difference_type operator-( const vector_iterator<Vector, T>& i,
0188                                                                 const vector_iterator<Vector, U>& j )
0189 {
0190     using difference_type = typename vector_iterator<Vector, T>::difference_type;
0191     return static_cast<difference_type>(i.my_index) - static_cast<difference_type>(j.my_index);
0192 }
0193 
0194 template <typename Vector, typename T, typename U>
0195 bool operator==( const vector_iterator<Vector, T>& i, const vector_iterator<Vector, U>& j ) {
0196     return i.my_vector == j.my_vector && i.my_index == j.my_index;
0197 }
0198 
0199 template <typename Vector, typename T, typename U>
0200 bool operator!=( const vector_iterator<Vector, T>& i, const vector_iterator<Vector, U>& j ) {
0201     return !(i == j);
0202 }
0203 
0204 template <typename Vector, typename T, typename U>
0205 bool operator<( const vector_iterator<Vector, T>& i, const vector_iterator<Vector, U>& j ) {
0206     return i.my_index < j.my_index;
0207 }
0208 
0209 template <typename Vector, typename T, typename U>
0210 bool operator>( const vector_iterator<Vector, T>& i, const vector_iterator<Vector, U>& j ) {
0211     return j < i;
0212 }
0213 
0214 template <typename Vector, typename T, typename U>
0215 bool operator>=( const vector_iterator<Vector, T>& i, const vector_iterator<Vector, U>& j ) {
0216     return !(i < j);
0217 }
0218 
0219 template <typename Vector, typename T, typename U>
0220 bool operator<=( const vector_iterator<Vector, T>& i, const vector_iterator<Vector, U>& j ) {
0221     return !(j < i);
0222 }
0223 
0224 static constexpr std::size_t embedded_table_num_segments = 3;
0225 
0226 template <typename T, typename Allocator = tbb::cache_aligned_allocator<T>>
0227 class concurrent_vector
0228     : private segment_table<T, Allocator, concurrent_vector<T, Allocator>, embedded_table_num_segments>
0229 {
0230     using self_type = concurrent_vector<T, Allocator>;
0231     using base_type = segment_table<T, Allocator, self_type, embedded_table_num_segments>;
0232 
0233     friend class segment_table<T, Allocator, self_type, embedded_table_num_segments>;
0234 
0235     template <typename Iterator>
0236     class generic_range_type : public tbb::blocked_range<Iterator> {
0237         using base_type = tbb::blocked_range<Iterator>;
0238     public:
0239         using value_type = T;
0240         using reference = T&;
0241         using const_reference = const T&;
0242         using iterator = Iterator;
0243         using difference_type = std::ptrdiff_t;
0244 
0245         using base_type::base_type;
0246 
0247         template<typename U>
0248         generic_range_type( const generic_range_type<U>& r) : blocked_range<Iterator>(r.begin(), r.end(), r.grainsize()) {}
0249         generic_range_type( generic_range_type& r, split ) : blocked_range<Iterator>(r, split()) {}
0250     }; // class generic_range_type
0251 
0252     static_assert(std::is_same<T, typename Allocator::value_type>::value,
0253                   "value_type of the container must be the same as its allocator's");
0254     using allocator_traits_type = tbb::detail::allocator_traits<Allocator>;
0255     // Segment table for concurrent_vector can be extended
0256     static constexpr bool allow_table_extending = true;
0257     static constexpr bool is_noexcept_assignment = allocator_traits_type::propagate_on_container_move_assignment::value ||
0258                                                    allocator_traits_type::is_always_equal::value;
0259     static constexpr bool is_noexcept_swap = allocator_traits_type::propagate_on_container_swap::value ||
0260                                              allocator_traits_type::is_always_equal::value;
0261 
0262 public:
0263     using value_type = T;
0264     using allocator_type = Allocator;
0265     using size_type = std::size_t;
0266     using difference_type = std::ptrdiff_t;
0267     using reference = value_type&;
0268     using const_reference = const value_type&;
0269 
0270     using pointer = typename allocator_traits_type::pointer;
0271     using const_pointer = typename allocator_traits_type::const_pointer;
0272 
0273     using iterator = vector_iterator<concurrent_vector, value_type>;
0274     using const_iterator = vector_iterator<concurrent_vector, const value_type>;
0275     using reverse_iterator = std::reverse_iterator<iterator>;
0276     using const_reverse_iterator = std::reverse_iterator<const_iterator>;
0277 
0278     using range_type = generic_range_type<iterator>;
0279     using const_range_type = generic_range_type<const_iterator>;
0280 
0281     concurrent_vector() : concurrent_vector(allocator_type()) {}
0282 
0283     explicit concurrent_vector( const allocator_type& alloc ) noexcept
0284         : base_type(alloc)
0285     {}
0286 
0287     explicit concurrent_vector( size_type count, const value_type& value,
0288                                 const allocator_type& alloc = allocator_type() )
0289         : concurrent_vector(alloc)
0290     {
0291         try_call( [&] {
0292             grow_by(count, value);
0293         } ).on_exception( [&] {
0294             base_type::clear();
0295         });
0296     }
0297 
0298     explicit concurrent_vector( size_type count, const allocator_type& alloc = allocator_type() )
0299         : concurrent_vector(alloc)
0300     {
0301         try_call( [&] {
0302             grow_by(count);
0303         } ).on_exception( [&] {
0304             base_type::clear();
0305         });
0306     }
0307 
0308     template <typename InputIterator>
0309     concurrent_vector( InputIterator first, InputIterator last, const allocator_type& alloc = allocator_type() )
0310         : concurrent_vector(alloc)
0311     {
0312         try_call( [&] {
0313             grow_by(first, last);
0314         } ).on_exception( [&] {
0315             base_type::clear();
0316         });
0317     }
0318 
0319     concurrent_vector( const concurrent_vector& other )
0320         : base_type(segment_table_allocator_traits::select_on_container_copy_construction(other.get_allocator()))
0321     {
0322         try_call( [&] {
0323             grow_by(other.begin(), other.end());
0324         } ).on_exception( [&] {
0325             base_type::clear();
0326         });
0327     }
0328 
0329     concurrent_vector( const concurrent_vector& other, const allocator_type& alloc )
0330         : base_type(other, alloc) {}
0331 
0332     concurrent_vector(concurrent_vector&& other) noexcept
0333         : base_type(std::move(other))
0334     {}
0335 
0336     concurrent_vector( concurrent_vector&& other, const allocator_type& alloc )
0337         : base_type(std::move(other), alloc)
0338     {}
0339 
0340     concurrent_vector( std::initializer_list<value_type> init,
0341                        const allocator_type& alloc = allocator_type() )
0342         : concurrent_vector(init.begin(), init.end(), alloc)
0343     {}
0344 
0345     ~concurrent_vector() {}
0346 
0347     // Assignment
0348     concurrent_vector& operator=( const concurrent_vector& other ) {
0349         base_type::operator=(other);
0350         return *this;
0351     }
0352 
0353     concurrent_vector& operator=( concurrent_vector&& other ) noexcept(is_noexcept_assignment) {
0354         base_type::operator=(std::move(other));
0355         return *this;
0356     }
0357 
0358     concurrent_vector& operator=( std::initializer_list<value_type> init ) {
0359         assign(init);
0360         return *this;
0361     }
0362 
0363     void assign( size_type count, const value_type& value ) {
0364         destroy_elements();
0365         grow_by(count, value);
0366     }
0367 
0368     template <typename InputIterator>
0369     typename std::enable_if<is_input_iterator<InputIterator>::value, void>::type
0370     assign( InputIterator first, InputIterator last ) {
0371         destroy_elements();
0372         grow_by(first, last);
0373     }
0374 
0375     void assign( std::initializer_list<value_type> init ) {
0376         destroy_elements();
0377         assign(init.begin(), init.end());
0378     }
0379 
0380     // Concurrent growth
0381     iterator grow_by( size_type delta ) {
0382         return internal_grow_by_delta(delta);
0383     }
0384 
0385     iterator grow_by( size_type delta, const value_type& value ) {
0386         return internal_grow_by_delta(delta, value);
0387     }
0388 
0389     template <typename ForwardIterator>
0390     typename std::enable_if<is_input_iterator<ForwardIterator>::value, iterator>::type
0391     grow_by( ForwardIterator first, ForwardIterator last ) {
0392         auto delta = std::distance(first, last);
0393         return internal_grow_by_delta(delta, first, last);
0394     }
0395 
0396     iterator grow_by( std::initializer_list<value_type> init ) {
0397         return grow_by(init.begin(), init.end());
0398     }
0399 
0400     iterator grow_to_at_least( size_type n ) {
0401         return internal_grow_to_at_least(n);
0402     }
0403     iterator grow_to_at_least( size_type n, const value_type& value ) {
0404         return internal_grow_to_at_least(n, value);
0405     }
0406 
0407     iterator push_back( const value_type& item ) {
0408         return internal_emplace_back(item);
0409     }
0410 
0411     iterator push_back( value_type&& item ) {
0412         return internal_emplace_back(std::move(item));
0413     }
0414 
0415     template <typename... Args>
0416     iterator emplace_back( Args&&... args ) {
0417         return internal_emplace_back(std::forward<Args>(args)...);
0418     }
0419 
0420     // Items access
0421     reference operator[]( size_type index ) {
0422         return internal_subscript(index);
0423     }
0424     const_reference operator[]( size_type index ) const {
0425         return internal_subscript(index);
0426     }
0427 
0428     reference at( size_type index ) {
0429         return internal_subscript_with_exceptions(index);
0430     }
0431     const_reference at( size_type index ) const {
0432         return internal_subscript_with_exceptions(index);
0433     }
0434 
0435     // Get range for iterating with parallel algorithms
0436     range_type range( size_t grainsize = 1 ) {
0437         return range_type(begin(), end(), grainsize);
0438     }
0439 
0440     // Get const range for iterating with parallel algorithms
0441     const_range_type range( size_t grainsize = 1 ) const {
0442         return const_range_type(begin(), end(), grainsize);
0443     }
0444 
0445     reference front() {
0446         return internal_subscript(0);
0447     }
0448 
0449     const_reference front() const {
0450         return internal_subscript(0);
0451     }
0452 
0453     reference back() {
0454         return internal_subscript(size() - 1);
0455     }
0456 
0457     const_reference back() const {
0458         return internal_subscript(size() - 1);
0459     }
0460 
0461     // Iterators
0462     iterator begin() { return iterator(*this, 0); }
0463     const_iterator begin() const { return const_iterator(*this, 0); }
0464     const_iterator cbegin() const { return const_iterator(*this, 0); }
0465 
0466     iterator end() { return iterator(*this, size()); }
0467     const_iterator end() const { return const_iterator(*this, size()); }
0468     const_iterator cend() const { return const_iterator(*this, size()); }
0469 
0470     reverse_iterator rbegin() { return reverse_iterator(end()); }
0471     const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
0472     const_reverse_iterator crbegin() const { return const_reverse_iterator(cend()); }
0473 
0474     reverse_iterator rend() { return reverse_iterator(begin()); }
0475     const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }
0476     const_reverse_iterator crend() const { return const_reverse_iterator(cbegin()); }
0477 
0478     allocator_type get_allocator() const {
0479         return base_type::get_allocator();
0480     }
0481 
0482     // Storage
0483     bool empty() const noexcept {
0484         return 0 == size();
0485     }
0486 
0487     size_type size() const noexcept {
0488         return std::min(this->my_size.load(std::memory_order_acquire), capacity());
0489     }
0490 
0491     size_type max_size() const noexcept {
0492         return allocator_traits_type::max_size(base_type::get_allocator());
0493     }
0494 
0495     size_type capacity() const noexcept {
0496         return base_type::capacity();
0497     }
0498 
0499     void reserve( size_type n ) {
0500         if (n == 0) return;
0501 
0502         if (n > max_size()) {
0503             tbb::detail::throw_exception(exception_id::reservation_length_error);
0504         }
0505 
0506         this->assign_first_block_if_necessary(this->segment_index_of(n - 1) + 1);
0507         base_type::reserve(n);
0508     }
0509 
0510     void resize( size_type n ) {
0511         internal_resize(n);
0512     }
0513 
0514     void resize( size_type n, const value_type& val ) {
0515         internal_resize(n, val);
0516     }
0517 
0518     void shrink_to_fit() {
0519         internal_compact();
0520     }
0521 
0522     void swap(concurrent_vector& other) noexcept(is_noexcept_swap) {
0523         base_type::swap(other);
0524     }
0525 
0526     void clear() {
0527         destroy_elements();
0528     }
0529 
0530 private:
0531     using segment_type = typename base_type::segment_type;
0532     using segment_table_type = typename base_type::segment_table_type;
0533     using segment_table_allocator_traits = typename base_type::segment_table_allocator_traits;
0534     using segment_index_type = typename base_type::segment_index_type;
0535 
0536     using segment_element_type = typename base_type::value_type;
0537     using segment_element_allocator_type = typename allocator_traits_type::template rebind_alloc<segment_element_type>;
0538     using segment_element_allocator_traits = tbb::detail::allocator_traits<segment_element_allocator_type>;
0539 
0540     segment_table_type allocate_long_table( const typename base_type::atomic_segment* embedded_table, size_type start_index ) {
0541         __TBB_ASSERT(start_index <= this->embedded_table_size, "Start index out of embedded table");
0542 
0543         // If other threads are trying to set pointers in the short segment, wait for them to finish their
0544         // assignments before we copy the short segment to the long segment. Note: grow_to_at_least depends on it
0545         for (segment_index_type i = 0; this->segment_base(i) < start_index; ++i) {
0546             spin_wait_while_eq(embedded_table[i], segment_type(nullptr));
0547         }
0548 
0549         // It is possible that the table was extend by a thread allocating first_block, need to check this.
0550         if (this->get_table() != embedded_table) {
0551             return nullptr;
0552         }
0553 
0554         // Allocate long segment table and fill with null pointers
0555         segment_table_type new_segment_table = segment_table_allocator_traits::allocate(base_type::get_allocator(), this->pointers_per_long_table);
0556         // Copy segment pointers from the embedded table
0557         for (size_type segment_index = 0; segment_index < this->pointers_per_embedded_table; ++segment_index) {
0558             segment_table_allocator_traits::construct(base_type::get_allocator(), &new_segment_table[segment_index],
0559                 embedded_table[segment_index].load(std::memory_order_relaxed));
0560         }
0561         for (size_type segment_index = this->pointers_per_embedded_table; segment_index < this->pointers_per_long_table; ++segment_index) {
0562             segment_table_allocator_traits::construct(base_type::get_allocator(), &new_segment_table[segment_index], nullptr);
0563         }
0564 
0565         return new_segment_table;
0566     }
0567 
0568     // create_segment function is required by the segment_table base class
0569     segment_type create_segment( segment_table_type table, segment_index_type seg_index, size_type index ) {
0570         size_type first_block = this->my_first_block.load(std::memory_order_relaxed);
0571         // First block allocation
0572         if (seg_index < first_block) {
0573             // If 0 segment is already allocated, then it remains to wait until the segments are filled to requested
0574             if (table[0].load(std::memory_order_acquire) != nullptr) {
0575                 spin_wait_while_eq(table[seg_index], segment_type(nullptr));
0576                 return nullptr;
0577             }
0578 
0579             segment_element_allocator_type segment_allocator(base_type::get_allocator());
0580             segment_type new_segment = nullptr;
0581             size_type first_block_size = this->segment_size(first_block);
0582             try_call( [&] {
0583                 new_segment = segment_element_allocator_traits::allocate(segment_allocator, first_block_size);
0584             } ).on_exception( [&] {
0585                 segment_type disabled_segment = nullptr;
0586                 if (table[0].compare_exchange_strong(disabled_segment, this->segment_allocation_failure_tag)) {
0587                     size_type end_segment = table == this->my_embedded_table ? this->pointers_per_embedded_table : first_block;
0588                     for (size_type i = 1; i < end_segment; ++i) {
0589                         table[i].store(this->segment_allocation_failure_tag, std::memory_order_release);
0590                     }
0591                 }
0592             });
0593 
0594             segment_type disabled_segment = nullptr;
0595             if (table[0].compare_exchange_strong(disabled_segment, new_segment)) {
0596                 this->extend_table_if_necessary(table, 0, first_block_size);
0597                 for (size_type i = 1; i < first_block; ++i) {
0598                     table[i].store(new_segment, std::memory_order_release);
0599                 }
0600 
0601                 // Other threads can wait on a snapshot of an embedded table, need to fill it.
0602                 for (size_type i = 1; i < first_block && i < this->pointers_per_embedded_table; ++i) {
0603                     this->my_embedded_table[i].store(new_segment, std::memory_order_release);
0604                 }
0605             } else if (new_segment != this->segment_allocation_failure_tag) {
0606                 // Deallocate the memory
0607                 segment_element_allocator_traits::deallocate(segment_allocator, new_segment, first_block_size);
0608                 // 0 segment is already allocated, then it remains to wait until the segments are filled to requested
0609                 spin_wait_while_eq(table[seg_index], segment_type(nullptr));
0610             }
0611         } else {
0612             size_type offset = this->segment_base(seg_index);
0613             if (index == offset) {
0614                 __TBB_ASSERT(table[seg_index].load(std::memory_order_relaxed) == nullptr, "Only this thread can enable this segment");
0615                 segment_element_allocator_type segment_allocator(base_type::get_allocator());
0616                 segment_type new_segment = this->segment_allocation_failure_tag;
0617                 try_call( [&] {
0618                     new_segment = segment_element_allocator_traits::allocate(segment_allocator,this->segment_size(seg_index));
0619                     // Shift base address to simplify access by index
0620                     new_segment -= this->segment_base(seg_index);
0621                 } ).on_completion( [&] {
0622                     table[seg_index].store(new_segment, std::memory_order_release);
0623                 });
0624             } else {
0625                 spin_wait_while_eq(table[seg_index], segment_type(nullptr));
0626             }
0627         }
0628         return nullptr;
0629     }
0630 
0631     // Returns the number of elements in the segment to be destroy
0632     size_type number_of_elements_in_segment( segment_index_type seg_index ) {
0633         size_type curr_vector_size = this->my_size.load(std::memory_order_relaxed);
0634         size_type curr_segment_base = this->segment_base(seg_index);
0635 
0636         if (seg_index == 0) {
0637             return std::min(curr_vector_size, this->segment_size(seg_index));
0638         } else {
0639             // Perhaps the segment is allocated, but there are no elements in it.
0640             if (curr_vector_size < curr_segment_base) {
0641                 return 0;
0642             }
0643             return curr_segment_base * 2 > curr_vector_size ? curr_vector_size - curr_segment_base : curr_segment_base;
0644         }
0645     }
0646 
0647     segment_type nullify_segment( segment_table_type table, size_type segment_index ) {
0648         segment_type target_segment = table[segment_index].load(std::memory_order_relaxed);
0649         if (segment_index >= this->my_first_block) {
0650             table[segment_index].store(nullptr, std::memory_order_relaxed);
0651         } else {
0652             if (segment_index == 0) {
0653                 for (size_type i = 0; i < this->my_first_block; ++i) {
0654                     table[i].store(nullptr, std::memory_order_relaxed);
0655                 }
0656             }
0657         }
0658 
0659         return target_segment;
0660     }
0661 
0662     void deallocate_segment( segment_type address, segment_index_type seg_index ) {
0663         segment_element_allocator_type segment_allocator(base_type::get_allocator());
0664         size_type first_block = this->my_first_block.load(std::memory_order_relaxed);
0665         if (seg_index >= first_block) {
0666             segment_element_allocator_traits::deallocate(segment_allocator, address, this->segment_size(seg_index));
0667         }
0668         else if (seg_index == 0) {
0669             size_type elements_to_deallocate = first_block > 0 ? this->segment_size(first_block) : this->segment_size(0);
0670             segment_element_allocator_traits::deallocate(segment_allocator, address, elements_to_deallocate);
0671         }
0672     }
0673 
0674     // destroy_segment function is required by the segment_table base class
0675     void destroy_segment( segment_type address, segment_index_type seg_index ) {
0676         size_type elements_to_destroy = number_of_elements_in_segment(seg_index);
0677         segment_element_allocator_type segment_allocator(base_type::get_allocator());
0678 
0679         for (size_type i = 0; i < elements_to_destroy; ++i) {
0680             segment_element_allocator_traits::destroy(segment_allocator, address + i);
0681         }
0682 
0683         deallocate_segment(address, seg_index);
0684     }
0685 
0686     // copy_segment function is required by the segment_table base class
0687     void copy_segment( segment_index_type seg_index, segment_type from, segment_type to ) {
0688         size_type i = 0;
0689         try_call( [&] {
0690             for (; i != number_of_elements_in_segment(seg_index); ++i) {
0691                 segment_table_allocator_traits::construct(base_type::get_allocator(), to + i, from[i]);
0692             }
0693         } ).on_exception( [&] {
0694             // Zero-initialize items left not constructed after the exception
0695             zero_unconstructed_elements(this->get_segment(seg_index) + i, this->segment_size(seg_index) - i);
0696 
0697             segment_index_type last_segment = this->segment_index_of(this->my_size.load(std::memory_order_relaxed));
0698             auto table = this->get_table();
0699             for (segment_index_type j = seg_index + 1; j != last_segment; ++j) {
0700                 auto curr_segment = table[j].load(std::memory_order_relaxed);
0701                 if (curr_segment) {
0702                     zero_unconstructed_elements(curr_segment + this->segment_base(j), this->segment_size(j));
0703                 }
0704             }
0705             this->my_size.store(this->segment_size(seg_index) + i, std::memory_order_relaxed);
0706         });
0707     }
0708 
0709     // move_segment function is required by the segment_table base class
0710     void move_segment( segment_index_type seg_index, segment_type from, segment_type to ) {
0711         size_type i = 0;
0712         try_call( [&] {
0713             for (; i != number_of_elements_in_segment(seg_index); ++i) {
0714                 segment_table_allocator_traits::construct(base_type::get_allocator(), to + i, std::move(from[i]));
0715             }
0716         } ).on_exception( [&] {
0717             // Zero-initialize items left not constructed after the exception
0718             zero_unconstructed_elements(this->get_segment(seg_index) + i, this->segment_size(seg_index) - i);
0719 
0720             segment_index_type last_segment = this->segment_index_of(this->my_size.load(std::memory_order_relaxed));
0721             auto table = this->get_table();
0722             for (segment_index_type j = seg_index + 1; j != last_segment; ++j) {
0723                 auto curr_segment = table[j].load(std::memory_order_relaxed);
0724                 if (curr_segment) {
0725                     zero_unconstructed_elements(curr_segment + this->segment_base(j), this->segment_size(j));
0726                 }
0727             }
0728             this->my_size.store(this->segment_size(seg_index) + i, std::memory_order_relaxed);
0729         });
0730     }
0731 
0732     static constexpr bool is_first_element_in_segment( size_type index ) {
0733         // An element is the first in a segment if its index is equal to a power of two
0734         return is_power_of_two_at_least(index, 2);
0735     }
0736 
0737     const_reference internal_subscript( size_type index ) const {
0738         return const_cast<self_type*>(this)->internal_subscript(index);
0739     }
0740 
0741     reference internal_subscript( size_type index ) {
0742         __TBB_ASSERT(index < this->my_size.load(std::memory_order_relaxed), "Invalid subscript index");
0743         return base_type::template internal_subscript</*allow_out_of_range_access=*/false>(index);
0744     }
0745 
0746     const_reference internal_subscript_with_exceptions( size_type index ) const {
0747         return const_cast<self_type*>(this)->internal_subscript_with_exceptions(index);
0748     }
0749 
0750     reference internal_subscript_with_exceptions( size_type index ) {
0751         if (index >= this->my_size.load(std::memory_order_acquire)) {
0752             tbb::detail::throw_exception(exception_id::out_of_range);
0753         }
0754 
0755         segment_table_type table = this->my_segment_table.load(std::memory_order_acquire);
0756 
0757         size_type seg_index = this->segment_index_of(index);
0758         if (base_type::number_of_segments(table) < seg_index) {
0759             tbb::detail::throw_exception(exception_id::out_of_range);
0760         }
0761 
0762         if (table[seg_index] <= this->segment_allocation_failure_tag) {
0763             tbb::detail::throw_exception(exception_id::out_of_range);
0764         }
0765 
0766         return base_type::template internal_subscript</*allow_out_of_range_access=*/false>(index);
0767     }
0768 
0769     static void zero_unconstructed_elements( pointer start, size_type count ) {
0770         std::memset(static_cast<void *>(start), 0, count * sizeof(value_type));
0771     }
0772 
0773     template <typename... Args>
0774     iterator internal_emplace_back( Args&&... args ) {
0775         size_type old_size = this->my_size++;
0776         this->assign_first_block_if_necessary(default_first_block_size);
0777         auto element_address = &base_type::template internal_subscript</*allow_out_of_range_access=*/true>(old_size);
0778 
0779         // try_call API is not convenient here due to broken
0780         // variadic capture on GCC 4.8.5
0781         auto value_guard = make_raii_guard([&] {
0782             zero_unconstructed_elements(element_address, /*count =*/1);
0783         });
0784 
0785         segment_table_allocator_traits::construct(base_type::get_allocator(), element_address, std::forward<Args>(args)...);
0786         value_guard.dismiss();
0787         return iterator(*this, old_size, element_address);
0788     }
0789 
0790     template <typename... Args>
0791     void internal_loop_construct( segment_table_type table, size_type start_idx, size_type end_idx, const Args&... args ) {
0792         static_assert(sizeof...(Args) < 2, "Too many parameters");
0793         for (size_type idx = start_idx; idx < end_idx; ++idx) {
0794             auto element_address = &base_type::template internal_subscript</*allow_out_of_range_access=*/true>(idx);
0795             // try_call API is not convenient here due to broken
0796             // variadic capture on GCC 4.8.5
0797             auto value_guard = make_raii_guard( [&] {
0798                 segment_index_type last_allocated_segment = this->find_last_allocated_segment(table);
0799                 size_type segment_size = this->segment_size(last_allocated_segment);
0800                 end_idx = end_idx < segment_size ? end_idx : segment_size;
0801                 for (size_type i = idx; i < end_idx; ++i) {
0802                     zero_unconstructed_elements(&this->internal_subscript(i), /*count =*/1);
0803                 }
0804             });
0805             segment_table_allocator_traits::construct(base_type::get_allocator(), element_address, args...);
0806             value_guard.dismiss();
0807         }
0808     }
0809 
0810     template <typename ForwardIterator>
0811     void internal_loop_construct( segment_table_type table, size_type start_idx, size_type end_idx, ForwardIterator first, ForwardIterator ) {
0812         for (size_type idx = start_idx; idx < end_idx; ++idx) {
0813             auto element_address = &base_type::template internal_subscript</*allow_out_of_range_access=*/true>(idx);
0814             try_call( [&] {
0815                 segment_table_allocator_traits::construct(base_type::get_allocator(), element_address, *first++);
0816             } ).on_exception( [&] {
0817                 segment_index_type last_allocated_segment = this->find_last_allocated_segment(table);
0818                 size_type segment_size = this->segment_size(last_allocated_segment);
0819                 end_idx = end_idx < segment_size ? end_idx : segment_size;
0820                 for (size_type i = idx; i < end_idx; ++i) {
0821                     zero_unconstructed_elements(&this->internal_subscript(i), /*count =*/1);
0822                 }
0823             });
0824         }
0825     }
0826 
0827     template <typename... Args>
0828     iterator internal_grow( size_type start_idx, size_type end_idx, const Args&... args ) {
0829         this->assign_first_block_if_necessary(this->segment_index_of(end_idx - 1) + 1);
0830         size_type seg_index = this->segment_index_of(end_idx - 1);
0831         segment_table_type table = this->get_table();
0832         this->extend_table_if_necessary(table, start_idx, end_idx);
0833 
0834         if (seg_index > this->my_first_block.load(std::memory_order_relaxed)) {
0835             // So that other threads be able to work with the last segment of grow_by, allocate it immediately.
0836             // If the last segment is not less than the first block
0837             if (table[seg_index].load(std::memory_order_relaxed) == nullptr) {
0838                 size_type first_element = this->segment_base(seg_index);
0839                 if (first_element >= start_idx && first_element < end_idx) {
0840                     segment_type segment = table[seg_index].load(std::memory_order_relaxed);
0841                     base_type::enable_segment(segment, table, seg_index, first_element);
0842                 }
0843             }
0844         }
0845 
0846         internal_loop_construct(table, start_idx, end_idx, args...);
0847 
0848         return iterator(*this, start_idx, &base_type::template internal_subscript</*allow_out_of_range_access=*/false>(start_idx));
0849     }
0850 
0851 
0852     template <typename... Args>
0853     iterator internal_grow_by_delta( size_type delta, const Args&... args ) {
0854         if (delta == size_type(0)) {
0855             return end();
0856         }
0857         size_type start_idx = this->my_size.fetch_add(delta);
0858         size_type end_idx = start_idx + delta;
0859 
0860         return internal_grow(start_idx, end_idx, args...);
0861     }
0862 
0863     template <typename... Args>
0864     iterator internal_grow_to_at_least( size_type new_size, const Args&... args ) {
0865         size_type old_size = this->my_size.load(std::memory_order_relaxed);
0866         if (new_size == size_type(0)) return iterator(*this, 0);
0867         while (old_size < new_size && !this->my_size.compare_exchange_weak(old_size, new_size))
0868         {}
0869 
0870         int delta = static_cast<int>(new_size) - static_cast<int>(old_size);
0871         if (delta > 0) {
0872             return internal_grow(old_size, new_size, args...);
0873         }
0874 
0875         size_type end_segment = this->segment_index_of(new_size - 1);
0876 
0877         // Check/wait for segments allocation completes
0878         if (end_segment >= this->pointers_per_embedded_table &&
0879             this->get_table() == this->my_embedded_table)
0880         {
0881             spin_wait_while_eq(this->my_segment_table, this->my_embedded_table);
0882         }
0883 
0884         for (segment_index_type seg_idx = 0; seg_idx <= end_segment; ++seg_idx) {
0885             if (this->get_table()[seg_idx].load(std::memory_order_relaxed) == nullptr) {
0886                 atomic_backoff backoff(true);
0887                 while (this->get_table()[seg_idx].load(std::memory_order_relaxed) == nullptr) {
0888                     backoff.pause();
0889                 }
0890             }
0891         }
0892 
0893     #if TBB_USE_DEBUG
0894         size_type cap = capacity();
0895         __TBB_ASSERT( cap >= new_size, nullptr);
0896     #endif
0897         return iterator(*this, size());
0898     }
0899 
0900     template <typename... Args>
0901     void internal_resize( size_type n, const Args&... args ) {
0902         if (n == 0) {
0903             clear();
0904             return;
0905         }
0906 
0907         size_type old_size = this->my_size.load(std::memory_order_acquire);
0908         if (n > old_size) {
0909             reserve(n);
0910             grow_to_at_least(n, args...);
0911         } else {
0912             if (old_size == n) {
0913                 return;
0914             }
0915             size_type last_segment = this->segment_index_of(old_size - 1);
0916             // Delete segments
0917             for (size_type seg_idx = this->segment_index_of(n - 1) + 1; seg_idx <= last_segment; ++seg_idx) {
0918                 this->delete_segment(seg_idx);
0919             }
0920 
0921             // If n > segment_size(n) => we need to destroy all of the items in the first segment
0922             // Otherwise, we need to destroy only items with the index < n
0923             size_type n_segment = this->segment_index_of(n - 1);
0924             size_type last_index_to_destroy = std::min(this->segment_base(n_segment) + this->segment_size(n_segment), old_size);
0925             // Destroy elements in curr segment
0926             for (size_type idx = n; idx < last_index_to_destroy; ++idx) {
0927                 segment_table_allocator_traits::destroy(base_type::get_allocator(), &base_type::template internal_subscript</*allow_out_of_range_access=*/false>(idx));
0928             }
0929             this->my_size.store(n, std::memory_order_release);
0930         }
0931     }
0932 
0933     void destroy_elements() {
0934         allocator_type alloc(base_type::get_allocator());
0935         for (size_type i = 0; i < this->my_size.load(std::memory_order_relaxed); ++i) {
0936             allocator_traits_type::destroy(alloc, &base_type::template internal_subscript</*allow_out_of_range_access=*/false>(i));
0937         }
0938         this->my_size.store(0, std::memory_order_relaxed);
0939     }
0940 
0941     static bool incompact_predicate( size_type size ) {
0942         // memory page size
0943         const size_type page_size = 4096;
0944         return size < page_size || ((size - 1) % page_size < page_size / 2 && size < page_size * 128);
0945     }
0946 
0947     void internal_compact() {
0948         const size_type curr_size = this->my_size.load(std::memory_order_relaxed);
0949         segment_table_type table = this->get_table();
0950         const segment_index_type k_end = this->find_last_allocated_segment(table);                   // allocated segments
0951         const segment_index_type k_stop = curr_size ? this->segment_index_of(curr_size - 1) + 1 : 0; // number of segments to store existing items: 0=>0; 1,2=>1; 3,4=>2; [5-8]=>3;..
0952         const segment_index_type first_block = this->my_first_block;                                 // number of merged segments, getting values from atomics
0953 
0954         segment_index_type k = first_block;
0955         if (k_stop < first_block) {
0956             k = k_stop;
0957         }
0958         else {
0959             while (k < k_stop && incompact_predicate(this->segment_size(k) * sizeof(value_type))) k++;
0960         }
0961 
0962         if (k_stop == k_end && k == first_block) {
0963             return;
0964         }
0965 
0966         // First segment optimization
0967         if (k != first_block && k) {
0968             size_type max_block = std::max(first_block, k);
0969 
0970             auto buffer_table = segment_table_allocator_traits::allocate(base_type::get_allocator(), max_block);
0971 
0972             for (size_type seg_idx = 0; seg_idx < max_block; ++seg_idx) {
0973                 segment_table_allocator_traits::construct(base_type::get_allocator(), &buffer_table[seg_idx],
0974                     table[seg_idx].load(std::memory_order_relaxed));
0975                 table[seg_idx].store(nullptr, std::memory_order_relaxed);
0976             }
0977 
0978             this->my_first_block.store(k, std::memory_order_relaxed);
0979             size_type index = 0;
0980             try_call( [&] {
0981                 for (; index < std::min(this->segment_size(max_block), curr_size); ++index) {
0982                     auto element_address = &static_cast<base_type*>(this)->operator[](index);
0983                     segment_index_type seg_idx = this->segment_index_of(index);
0984                     segment_table_allocator_traits::construct(base_type::get_allocator(), element_address,
0985                     std::move_if_noexcept(buffer_table[seg_idx].load(std::memory_order_relaxed)[index]));
0986                 }
0987             } ).on_exception( [&] {
0988                 segment_element_allocator_type allocator(base_type::get_allocator());
0989                 for (size_type i = 0; i < index; ++i) {
0990                     auto element_adress = &this->operator[](i);
0991                     segment_element_allocator_traits::destroy(allocator, element_adress);
0992                 }
0993                 segment_element_allocator_traits::deallocate(allocator,
0994                     table[0].load(std::memory_order_relaxed), this->segment_size(max_block));
0995 
0996                 for (size_type seg_idx = 0; seg_idx < max_block; ++seg_idx) {
0997                     table[seg_idx].store(buffer_table[seg_idx].load(std::memory_order_relaxed),
0998                         std::memory_order_relaxed);
0999                     buffer_table[seg_idx].store(nullptr, std::memory_order_relaxed);
1000                 }
1001                 segment_table_allocator_traits::deallocate(base_type::get_allocator(),
1002                     buffer_table, max_block);
1003                 this->my_first_block.store(first_block, std::memory_order_relaxed);
1004             });
1005 
1006             // Need to correct deallocate old segments
1007             // Method destroy_segment respect active first_block, therefore,
1008             // in order for the segment deletion to work correctly, set the first_block size that was earlier,
1009             // destroy the unnecessary segments.
1010             this->my_first_block.store(first_block, std::memory_order_relaxed);
1011             for (size_type seg_idx = max_block; seg_idx > 0 ; --seg_idx) {
1012                 auto curr_segment = buffer_table[seg_idx - 1].load(std::memory_order_relaxed);
1013                 if (curr_segment != nullptr) {
1014                     destroy_segment(buffer_table[seg_idx - 1].load(std::memory_order_relaxed) + this->segment_base(seg_idx - 1),
1015                         seg_idx - 1);
1016                 }
1017             }
1018 
1019             this->my_first_block.store(k, std::memory_order_relaxed);
1020 
1021             for (size_type seg_idx = 0; seg_idx < max_block; ++seg_idx) {
1022                 segment_table_allocator_traits::destroy(base_type::get_allocator(), &buffer_table[seg_idx]);
1023             }
1024 
1025             segment_table_allocator_traits::deallocate(base_type::get_allocator(), buffer_table, max_block);
1026         }
1027         // free unnecessary segments allocated by reserve() call
1028         if (k_stop < k_end) {
1029             for (size_type seg_idx = k_end; seg_idx != k_stop; --seg_idx) {
1030                 if (table[seg_idx - 1].load(std::memory_order_relaxed) != nullptr) {
1031                     this->delete_segment(seg_idx - 1);
1032                 }
1033             }
1034             if (!k) this->my_first_block.store(0, std::memory_order_relaxed);
1035         }
1036     }
1037 
1038     // Lever for adjusting the size of first_block at the very first insertion.
1039     // TODO: consider >1 value, check performance
1040     static constexpr size_type default_first_block_size = 1;
1041 
1042     template <typename Vector, typename Value>
1043     friend class vector_iterator;
1044 }; // class concurrent_vector
1045 
1046 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
1047 // Deduction guide for the constructor from two iterators
1048 template <typename It, typename Alloc = tbb::cache_aligned_allocator<iterator_value_t<It>>,
1049           typename = std::enable_if_t<is_input_iterator_v<It>>,
1050           typename = std::enable_if_t<is_allocator_v<Alloc>>>
1051 concurrent_vector( It, It, Alloc = Alloc() )
1052 -> concurrent_vector<iterator_value_t<It>, Alloc>;
1053 #endif
1054 
1055 template <typename T, typename Allocator>
1056 void swap(concurrent_vector<T, Allocator> &lhs,
1057           concurrent_vector<T, Allocator> &rhs)
1058 {
1059     lhs.swap(rhs);
1060 }
1061 
1062 template <typename T, typename Allocator>
1063 bool operator==(const concurrent_vector<T, Allocator> &lhs,
1064                 const concurrent_vector<T, Allocator> &rhs)
1065 {
1066     return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
1067 }
1068 
1069 #if !__TBB_CPP20_COMPARISONS_PRESENT
1070 template <typename T, typename Allocator>
1071 bool operator!=(const concurrent_vector<T, Allocator> &lhs,
1072                 const concurrent_vector<T, Allocator> &rhs)
1073 {
1074     return !(lhs == rhs);
1075 }
1076 #endif // !__TBB_CPP20_COMPARISONS_PRESENT
1077 
1078 #if __TBB_CPP20_COMPARISONS_PRESENT && __TBB_CPP20_CONCEPTS_PRESENT
1079 template <typename T, typename Allocator>
1080 tbb::detail::synthesized_three_way_result<typename concurrent_vector<T, Allocator>::value_type>
1081 operator<=>(const concurrent_vector<T, Allocator> &lhs,
1082             const concurrent_vector<T, Allocator> &rhs)
1083 {
1084     return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(),
1085                                                   rhs.begin(), rhs.end(),
1086                                                   tbb::detail::synthesized_three_way_comparator{});
1087 }
1088 
1089 #else
1090 
1091 template <typename T, typename Allocator>
1092 bool operator<(const concurrent_vector<T, Allocator> &lhs,
1093                const concurrent_vector<T, Allocator> &rhs)
1094 {
1095     return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
1096 }
1097 
1098 template <typename T, typename Allocator>
1099 bool operator<=(const concurrent_vector<T, Allocator> &lhs,
1100                 const concurrent_vector<T, Allocator> &rhs)
1101 {
1102     return !(rhs < lhs);
1103 }
1104 
1105 template <typename T, typename Allocator>
1106 bool operator>(const concurrent_vector<T, Allocator> &lhs,
1107                const concurrent_vector<T, Allocator> &rhs)
1108 {
1109     return rhs < lhs;
1110 }
1111 
1112 template <typename T, typename Allocator>
1113 bool operator>=(const concurrent_vector<T, Allocator> &lhs,
1114                 const concurrent_vector<T, Allocator> &rhs)
1115 {
1116     return !(lhs < rhs);
1117 }
1118 #endif // __TBB_CPP20_COMPARISONS_PRESENT && __TBB_CPP20_CONCEPTS_PRESENT
1119 
1120 } // namespace d1
1121 } // namespace detail
1122 
1123 inline namespace v1 {
1124     using detail::d1::concurrent_vector;
1125 } // namespace v1
1126 
1127 } // namespace tbb
1128 
1129 #endif // __TBB_concurrent_vector_H