149e08aacStbbdev /*
213f9f32bSSergey Zheltov     Copyright (c) 2005-2022 Intel Corporation
349e08aacStbbdev 
449e08aacStbbdev     Licensed under the Apache License, Version 2.0 (the "License");
549e08aacStbbdev     you may not use this file except in compliance with the License.
649e08aacStbbdev     You may obtain a copy of the License at
749e08aacStbbdev 
849e08aacStbbdev         http://www.apache.org/licenses/LICENSE-2.0
949e08aacStbbdev 
1049e08aacStbbdev     Unless required by applicable law or agreed to in writing, software
1149e08aacStbbdev     distributed under the License is distributed on an "AS IS" BASIS,
1249e08aacStbbdev     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1349e08aacStbbdev     See the License for the specific language governing permissions and
1449e08aacStbbdev     limitations under the License.
1549e08aacStbbdev */
1649e08aacStbbdev 
1749e08aacStbbdev #ifndef __TBB_concurrent_vector_H
1849e08aacStbbdev #define __TBB_concurrent_vector_H
1949e08aacStbbdev 
2049e08aacStbbdev #include "detail/_namespace_injection.h"
2149e08aacStbbdev #include "detail/_utils.h"
2249e08aacStbbdev #include "detail/_assert.h"
2349e08aacStbbdev #include "detail/_allocator_traits.h"
2449e08aacStbbdev #include "detail/_segment_table.h"
2549e08aacStbbdev #include "detail/_containers_helpers.h"
2649e08aacStbbdev #include "blocked_range.h"
2749e08aacStbbdev #include "cache_aligned_allocator.h"
2849e08aacStbbdev 
2949e08aacStbbdev #include <algorithm>
3049e08aacStbbdev #include <utility> // std::move_if_noexcept
3149e08aacStbbdev #include <algorithm>
32b15aabb3Stbbdev #if __TBB_CPP20_COMPARISONS_PRESENT
33b15aabb3Stbbdev #include <compare>
34b15aabb3Stbbdev #endif
3549e08aacStbbdev 
3649e08aacStbbdev namespace tbb {
3749e08aacStbbdev namespace detail {
3849e08aacStbbdev namespace d1 {
3949e08aacStbbdev 
4049e08aacStbbdev template <typename Vector, typename Value>
4149e08aacStbbdev class vector_iterator {
4249e08aacStbbdev     using vector_type = Vector;
4349e08aacStbbdev 
4449e08aacStbbdev public:
4549e08aacStbbdev     using value_type = Value;
4649e08aacStbbdev     using size_type = typename vector_type::size_type;
4749e08aacStbbdev     using difference_type = typename vector_type::difference_type;
4849e08aacStbbdev     using pointer = value_type*;
4949e08aacStbbdev     using reference = value_type&;
5049e08aacStbbdev     using iterator_category = std::random_access_iterator_tag;
5149e08aacStbbdev 
5249e08aacStbbdev     template <typename Vec, typename Val>
5349e08aacStbbdev     friend vector_iterator<Vec, Val> operator+( typename vector_iterator<Vec, Val>::difference_type, const vector_iterator<Vec, Val>& );
5449e08aacStbbdev 
5549e08aacStbbdev     template <typename Vec, typename Val1, typename Val2>
5649e08aacStbbdev     friend typename vector_iterator<Vec, Val1>::difference_type operator-( const vector_iterator<Vec, Val1>&, const vector_iterator<Vec, Val2>& );
5749e08aacStbbdev 
5849e08aacStbbdev     template <typename Vec, typename Val1, typename Val2>
5949e08aacStbbdev     friend bool operator==( const vector_iterator<Vec, Val1>&, const vector_iterator<Vec, Val2>& );
6049e08aacStbbdev 
6149e08aacStbbdev     template <typename Vec, typename Val1, typename Val2>
6249e08aacStbbdev     friend bool operator<( const vector_iterator<Vec, Val1>&, const vector_iterator<Vec, Val2>& );
6349e08aacStbbdev 
6449e08aacStbbdev     template <typename Vec, typename Val>
6549e08aacStbbdev     friend class vector_iterator;
6649e08aacStbbdev 
6749e08aacStbbdev     template <typename T, typename Allocator>
6849e08aacStbbdev     friend class concurrent_vector;
6949e08aacStbbdev 
7049e08aacStbbdev private:
7149e08aacStbbdev     vector_iterator( const vector_type& vector, size_type index, value_type* item = nullptr )
my_vector(const_cast<vector_type * > (& vector))7249e08aacStbbdev         : my_vector(const_cast<vector_type*>(&vector)), my_index(index), my_item(item)
7349e08aacStbbdev     {}
7449e08aacStbbdev 
7549e08aacStbbdev public:
vector_iterator()7649e08aacStbbdev     vector_iterator() : my_vector(nullptr), my_index(~size_type(0)), my_item(nullptr)
7749e08aacStbbdev     {}
7849e08aacStbbdev 
vector_iterator(const vector_iterator<vector_type,typename vector_type::value_type> & other)7949e08aacStbbdev     vector_iterator( const vector_iterator<vector_type, typename vector_type::value_type>& other )
8049e08aacStbbdev         : my_vector(other.my_vector), my_index(other.my_index), my_item(other.my_item)
8149e08aacStbbdev     {}
8249e08aacStbbdev 
8349e08aacStbbdev     vector_iterator& operator=( const vector_iterator<vector_type, typename vector_type::value_type>& other ) {
8449e08aacStbbdev         my_vector = other.my_vector;
8549e08aacStbbdev         my_index = other.my_index;
8649e08aacStbbdev         my_item = other.my_item;
8749e08aacStbbdev         return *this;
8849e08aacStbbdev     }
8949e08aacStbbdev 
9049e08aacStbbdev     vector_iterator operator+( difference_type offset ) const {
9149e08aacStbbdev         return vector_iterator(*my_vector, my_index + offset);
9249e08aacStbbdev     }
9349e08aacStbbdev 
9449e08aacStbbdev     vector_iterator& operator+=( difference_type offset ) {
9549e08aacStbbdev         my_index += offset;
9649e08aacStbbdev         my_item = nullptr;
9749e08aacStbbdev         return *this;
9849e08aacStbbdev     }
9949e08aacStbbdev 
10049e08aacStbbdev     vector_iterator operator-( difference_type offset ) const {
10149e08aacStbbdev         return vector_iterator(*my_vector, my_index - offset);
10249e08aacStbbdev     }
10349e08aacStbbdev 
10449e08aacStbbdev     vector_iterator& operator-=( difference_type offset ) {
10549e08aacStbbdev         my_index -= offset;
10649e08aacStbbdev         my_item = nullptr;
10749e08aacStbbdev         return *this;
10849e08aacStbbdev     }
10949e08aacStbbdev 
11049e08aacStbbdev     reference operator*() const {
11149e08aacStbbdev         value_type *item = my_item;
11249e08aacStbbdev         if (item == nullptr) {
11349e08aacStbbdev             item = &my_vector->internal_subscript(my_index);
11449e08aacStbbdev         } else {
11549e08aacStbbdev             __TBB_ASSERT(item == &my_vector->internal_subscript(my_index), "corrupt cache");
11649e08aacStbbdev         }
11749e08aacStbbdev         return *item;
11849e08aacStbbdev     }
11949e08aacStbbdev 
12049e08aacStbbdev     pointer operator->() const { return &(operator*()); }
12149e08aacStbbdev 
12249e08aacStbbdev     reference operator[]( difference_type k ) const {
12349e08aacStbbdev         return my_vector->internal_subscript(my_index + k);
12449e08aacStbbdev     }
12549e08aacStbbdev 
12649e08aacStbbdev     vector_iterator& operator++() {
12749e08aacStbbdev         ++my_index;
12849e08aacStbbdev         if (my_item != nullptr) {
12949e08aacStbbdev             if (vector_type::is_first_element_in_segment(my_index)) {
13049e08aacStbbdev                 // If the iterator crosses a segment boundary, the pointer become invalid
13149e08aacStbbdev                 // as possibly next segment is in another memory location
13249e08aacStbbdev                 my_item = nullptr;
13349e08aacStbbdev             } else {
13449e08aacStbbdev                 ++my_item;
13549e08aacStbbdev             }
13649e08aacStbbdev         }
13749e08aacStbbdev         return *this;
13849e08aacStbbdev     }
13949e08aacStbbdev 
14049e08aacStbbdev     vector_iterator operator++(int) {
14149e08aacStbbdev         vector_iterator result = *this;
14249e08aacStbbdev         ++(*this);
14349e08aacStbbdev         return result;
14449e08aacStbbdev     }
14549e08aacStbbdev 
14649e08aacStbbdev     vector_iterator& operator--() {
14749e08aacStbbdev         __TBB_ASSERT(my_index > 0, "operator--() applied to iterator already at beginning of concurrent_vector");
14849e08aacStbbdev         --my_index;
14949e08aacStbbdev         if (my_item != nullptr) {
15049e08aacStbbdev             if (vector_type::is_first_element_in_segment(my_index)) {
15149e08aacStbbdev                 // If the iterator crosses a segment boundary, the pointer become invalid
15249e08aacStbbdev                 // as possibly next segment is in another memory location
15349e08aacStbbdev                 my_item = nullptr;
15449e08aacStbbdev             } else {
15549e08aacStbbdev                 --my_item;
15649e08aacStbbdev             }
15749e08aacStbbdev         }
15849e08aacStbbdev         return *this;
15949e08aacStbbdev     }
16049e08aacStbbdev 
16149e08aacStbbdev     vector_iterator operator--(int) {
16249e08aacStbbdev         vector_iterator result = *this;
16349e08aacStbbdev         --(*this);
16449e08aacStbbdev         return result;
16549e08aacStbbdev     }
16649e08aacStbbdev 
16749e08aacStbbdev private:
16849e08aacStbbdev     // concurrent_vector over which we are iterating.
16949e08aacStbbdev     vector_type* my_vector;
17049e08aacStbbdev 
17149e08aacStbbdev     // Index into the vector
17249e08aacStbbdev     size_type my_index;
17349e08aacStbbdev 
17449e08aacStbbdev     // Caches my_vector *it;
17549e08aacStbbdev     // If my_item == nullptr cached value is not available use internal_subscript(my_index)
17649e08aacStbbdev     mutable value_type* my_item;
17749e08aacStbbdev }; // class vector_iterator
17849e08aacStbbdev 
17949e08aacStbbdev template <typename Vector, typename T>
18049e08aacStbbdev vector_iterator<Vector, T> operator+( typename vector_iterator<Vector, T>::difference_type offset,
18149e08aacStbbdev                                       const vector_iterator<Vector, T>& v )
18249e08aacStbbdev {
18349e08aacStbbdev     return vector_iterator<Vector, T>(*v.my_vector, v.my_index + offset);
18449e08aacStbbdev }
18549e08aacStbbdev 
18649e08aacStbbdev template <typename Vector, typename T, typename U>
18749e08aacStbbdev typename vector_iterator<Vector, T>::difference_type operator-( const vector_iterator<Vector, T>& i,
18849e08aacStbbdev                                                                 const vector_iterator<Vector, U>& j )
18949e08aacStbbdev {
19049e08aacStbbdev     using difference_type = typename vector_iterator<Vector, T>::difference_type;
19149e08aacStbbdev     return static_cast<difference_type>(i.my_index) - static_cast<difference_type>(j.my_index);
19249e08aacStbbdev }
19349e08aacStbbdev 
19449e08aacStbbdev template <typename Vector, typename T, typename U>
19549e08aacStbbdev bool operator==( const vector_iterator<Vector, T>& i, const vector_iterator<Vector, U>& j ) {
19649e08aacStbbdev     return i.my_vector == j.my_vector && i.my_index == j.my_index;
19749e08aacStbbdev }
19849e08aacStbbdev 
19949e08aacStbbdev template <typename Vector, typename T, typename U>
20049e08aacStbbdev bool operator!=( const vector_iterator<Vector, T>& i, const vector_iterator<Vector, U>& j ) {
20149e08aacStbbdev     return !(i == j);
20249e08aacStbbdev }
20349e08aacStbbdev 
20449e08aacStbbdev template <typename Vector, typename T, typename U>
20549e08aacStbbdev bool operator<( const vector_iterator<Vector, T>& i, const vector_iterator<Vector, U>& j ) {
20649e08aacStbbdev     return i.my_index < j.my_index;
20749e08aacStbbdev }
20849e08aacStbbdev 
20949e08aacStbbdev template <typename Vector, typename T, typename U>
21049e08aacStbbdev bool operator>( const vector_iterator<Vector, T>& i, const vector_iterator<Vector, U>& j ) {
21149e08aacStbbdev     return j < i;
21249e08aacStbbdev }
21349e08aacStbbdev 
21449e08aacStbbdev template <typename Vector, typename T, typename U>
21549e08aacStbbdev bool operator>=( const vector_iterator<Vector, T>& i, const vector_iterator<Vector, U>& j ) {
21649e08aacStbbdev     return !(i < j);
21749e08aacStbbdev }
21849e08aacStbbdev 
21949e08aacStbbdev template <typename Vector, typename T, typename U>
22049e08aacStbbdev bool operator<=( const vector_iterator<Vector, T>& i, const vector_iterator<Vector, U>& j ) {
22149e08aacStbbdev     return !(j < i);
22249e08aacStbbdev }
22349e08aacStbbdev 
22449e08aacStbbdev static constexpr std::size_t embedded_table_num_segments = 3;
22549e08aacStbbdev 
22649e08aacStbbdev template <typename T, typename Allocator = tbb::cache_aligned_allocator<T>>
22749e08aacStbbdev class concurrent_vector
22849e08aacStbbdev     : private segment_table<T, Allocator, concurrent_vector<T, Allocator>, embedded_table_num_segments>
22949e08aacStbbdev {
23049e08aacStbbdev     using self_type = concurrent_vector<T, Allocator>;
23149e08aacStbbdev     using base_type = segment_table<T, Allocator, self_type, embedded_table_num_segments>;
23249e08aacStbbdev 
23349e08aacStbbdev     friend class segment_table<T, Allocator, self_type, embedded_table_num_segments>;
23449e08aacStbbdev 
23549e08aacStbbdev     template <typename Iterator>
23649e08aacStbbdev     class generic_range_type : public tbb::blocked_range<Iterator> {
23749e08aacStbbdev         using base_type = tbb::blocked_range<Iterator>;
23849e08aacStbbdev     public:
23949e08aacStbbdev         using value_type = T;
24049e08aacStbbdev         using reference = T&;
24149e08aacStbbdev         using const_reference = const T&;
24249e08aacStbbdev         using iterator = Iterator;
24349e08aacStbbdev         using difference_type = std::ptrdiff_t;
24449e08aacStbbdev 
24549e08aacStbbdev         using base_type::base_type;
24649e08aacStbbdev 
24749e08aacStbbdev         template<typename U>
generic_range_type(const generic_range_type<U> & r)24849e08aacStbbdev         generic_range_type( const generic_range_type<U>& r) : blocked_range<Iterator>(r.begin(), r.end(), r.grainsize()) {}
generic_range_type(generic_range_type & r,split)24949e08aacStbbdev         generic_range_type( generic_range_type& r, split ) : blocked_range<Iterator>(r, split()) {}
25049e08aacStbbdev     }; // class generic_range_type
25149e08aacStbbdev 
25249e08aacStbbdev     static_assert(std::is_same<T, typename Allocator::value_type>::value,
25349e08aacStbbdev                   "value_type of the container must be the same as its allocator's");
25449e08aacStbbdev     using allocator_traits_type = tbb::detail::allocator_traits<Allocator>;
25549e08aacStbbdev     // Segment table for concurrent_vector can be extended
25649e08aacStbbdev     static constexpr bool allow_table_extending = true;
25749e08aacStbbdev     static constexpr bool is_noexcept_assignment = allocator_traits_type::propagate_on_container_move_assignment::value ||
25849e08aacStbbdev                                                    allocator_traits_type::is_always_equal::value;
25949e08aacStbbdev     static constexpr bool is_noexcept_swap = allocator_traits_type::propagate_on_container_swap::value ||
26049e08aacStbbdev                                              allocator_traits_type::is_always_equal::value;
26149e08aacStbbdev 
26249e08aacStbbdev public:
26349e08aacStbbdev     using value_type = T;
26449e08aacStbbdev     using allocator_type = Allocator;
26549e08aacStbbdev     using size_type = std::size_t;
26649e08aacStbbdev     using difference_type = std::ptrdiff_t;
26749e08aacStbbdev     using reference = value_type&;
26849e08aacStbbdev     using const_reference = const value_type&;
26949e08aacStbbdev 
27049e08aacStbbdev     using pointer = typename allocator_traits_type::pointer;
27149e08aacStbbdev     using const_pointer = typename allocator_traits_type::const_pointer;
27249e08aacStbbdev 
27349e08aacStbbdev     using iterator = vector_iterator<concurrent_vector, value_type>;
27449e08aacStbbdev     using const_iterator = vector_iterator<concurrent_vector, const value_type>;
27549e08aacStbbdev     using reverse_iterator = std::reverse_iterator<iterator>;
27649e08aacStbbdev     using const_reverse_iterator = std::reverse_iterator<const_iterator>;
27749e08aacStbbdev 
27849e08aacStbbdev     using range_type = generic_range_type<iterator>;
27949e08aacStbbdev     using const_range_type = generic_range_type<const_iterator>;
28049e08aacStbbdev 
concurrent_vector()28149e08aacStbbdev     concurrent_vector() : concurrent_vector(allocator_type()) {}
28249e08aacStbbdev 
concurrent_vector(const allocator_type & alloc)28349e08aacStbbdev     explicit concurrent_vector( const allocator_type& alloc ) noexcept
28449e08aacStbbdev         : base_type(alloc)
28549e08aacStbbdev     {}
28649e08aacStbbdev 
28749e08aacStbbdev     explicit concurrent_vector( size_type count, const value_type& value,
28849e08aacStbbdev                                 const allocator_type& alloc = allocator_type() )
concurrent_vector(alloc)28949e08aacStbbdev         : concurrent_vector(alloc)
29049e08aacStbbdev     {
29149e08aacStbbdev         try_call( [&] {
29249e08aacStbbdev             grow_by(count, value);
29349e08aacStbbdev         } ).on_exception( [&] {
29449e08aacStbbdev             base_type::clear();
29549e08aacStbbdev         });
29649e08aacStbbdev     }
29749e08aacStbbdev 
29849e08aacStbbdev     explicit concurrent_vector( size_type count, const allocator_type& alloc = allocator_type() )
concurrent_vector(alloc)29949e08aacStbbdev         : concurrent_vector(alloc)
30049e08aacStbbdev     {
30149e08aacStbbdev         try_call( [&] {
30249e08aacStbbdev             grow_by(count);
30349e08aacStbbdev         } ).on_exception( [&] {
30449e08aacStbbdev             base_type::clear();
30549e08aacStbbdev         });
30649e08aacStbbdev     }
30749e08aacStbbdev 
30849e08aacStbbdev     template <typename InputIterator>
30949e08aacStbbdev     concurrent_vector( InputIterator first, InputIterator last, const allocator_type& alloc = allocator_type() )
concurrent_vector(alloc)31049e08aacStbbdev         : concurrent_vector(alloc)
31149e08aacStbbdev     {
31249e08aacStbbdev         try_call( [&] {
31349e08aacStbbdev             grow_by(first, last);
31449e08aacStbbdev         } ).on_exception( [&] {
31549e08aacStbbdev             base_type::clear();
31649e08aacStbbdev         });
31749e08aacStbbdev     }
31849e08aacStbbdev 
concurrent_vector(const concurrent_vector & other)31949e08aacStbbdev     concurrent_vector( const concurrent_vector& other )
32049e08aacStbbdev         : base_type(segment_table_allocator_traits::select_on_container_copy_construction(other.get_allocator()))
32149e08aacStbbdev     {
32249e08aacStbbdev         try_call( [&] {
32349e08aacStbbdev             grow_by(other.begin(), other.end());
32449e08aacStbbdev         } ).on_exception( [&] {
32549e08aacStbbdev             base_type::clear();
32649e08aacStbbdev         });
32749e08aacStbbdev     }
32849e08aacStbbdev 
concurrent_vector(const concurrent_vector & other,const allocator_type & alloc)32949e08aacStbbdev     concurrent_vector( const concurrent_vector& other, const allocator_type& alloc )
33049e08aacStbbdev         : base_type(other, alloc) {}
33149e08aacStbbdev 
concurrent_vector(concurrent_vector && other)33249e08aacStbbdev     concurrent_vector(concurrent_vector&& other) noexcept
33349e08aacStbbdev         : base_type(std::move(other))
33449e08aacStbbdev     {}
33549e08aacStbbdev 
concurrent_vector(concurrent_vector && other,const allocator_type & alloc)33649e08aacStbbdev     concurrent_vector( concurrent_vector&& other, const allocator_type& alloc )
33749e08aacStbbdev         : base_type(std::move(other), alloc)
33849e08aacStbbdev     {}
33949e08aacStbbdev 
34049e08aacStbbdev     concurrent_vector( std::initializer_list<value_type> init,
34149e08aacStbbdev                        const allocator_type& alloc = allocator_type() )
34249e08aacStbbdev         : concurrent_vector(init.begin(), init.end(), alloc)
34349e08aacStbbdev     {}
34449e08aacStbbdev 
~concurrent_vector()34549e08aacStbbdev     ~concurrent_vector() {}
34649e08aacStbbdev 
34749e08aacStbbdev     // Assignment
34849e08aacStbbdev     concurrent_vector& operator=( const concurrent_vector& other ) {
34949e08aacStbbdev         base_type::operator=(other);
35049e08aacStbbdev         return *this;
35149e08aacStbbdev     }
35249e08aacStbbdev 
noexcept(is_noexcept_assignment)35349e08aacStbbdev     concurrent_vector& operator=( concurrent_vector&& other ) noexcept(is_noexcept_assignment) {
35449e08aacStbbdev         base_type::operator=(std::move(other));
35549e08aacStbbdev         return *this;
35649e08aacStbbdev     }
35749e08aacStbbdev 
35849e08aacStbbdev     concurrent_vector& operator=( std::initializer_list<value_type> init ) {
35949e08aacStbbdev         assign(init);
36049e08aacStbbdev         return *this;
36149e08aacStbbdev     }
36249e08aacStbbdev 
assign(size_type count,const value_type & value)36349e08aacStbbdev     void assign( size_type count, const value_type& value ) {
36449e08aacStbbdev         destroy_elements();
36549e08aacStbbdev         grow_by(count, value);
36649e08aacStbbdev     }
36749e08aacStbbdev 
36849e08aacStbbdev     template <typename InputIterator>
369d86ed7fbStbbdev     typename std::enable_if<is_input_iterator<InputIterator>::value, void>::type
assign(InputIterator first,InputIterator last)37049e08aacStbbdev     assign( InputIterator first, InputIterator last ) {
37149e08aacStbbdev         destroy_elements();
37249e08aacStbbdev         grow_by(first, last);
37349e08aacStbbdev     }
37449e08aacStbbdev 
assign(std::initializer_list<value_type> init)37549e08aacStbbdev     void assign( std::initializer_list<value_type> init ) {
37649e08aacStbbdev         destroy_elements();
37749e08aacStbbdev         assign(init.begin(), init.end());
37849e08aacStbbdev     }
37949e08aacStbbdev 
38049e08aacStbbdev     // Concurrent growth
grow_by(size_type delta)38149e08aacStbbdev     iterator grow_by( size_type delta ) {
38249e08aacStbbdev         return internal_grow_by_delta(delta);
38349e08aacStbbdev     }
38449e08aacStbbdev 
grow_by(size_type delta,const value_type & value)38549e08aacStbbdev     iterator grow_by( size_type delta, const value_type& value ) {
38649e08aacStbbdev         return internal_grow_by_delta(delta, value);
38749e08aacStbbdev     }
38849e08aacStbbdev 
38949e08aacStbbdev     template <typename ForwardIterator>
390d86ed7fbStbbdev     typename std::enable_if<is_input_iterator<ForwardIterator>::value, iterator>::type
grow_by(ForwardIterator first,ForwardIterator last)39149e08aacStbbdev     grow_by( ForwardIterator first, ForwardIterator last ) {
39249e08aacStbbdev         auto delta = std::distance(first, last);
39349e08aacStbbdev         return internal_grow_by_delta(delta, first, last);
39449e08aacStbbdev     }
39549e08aacStbbdev 
grow_by(std::initializer_list<value_type> init)39649e08aacStbbdev     iterator grow_by( std::initializer_list<value_type> init ) {
39749e08aacStbbdev         return grow_by(init.begin(), init.end());
39849e08aacStbbdev     }
39949e08aacStbbdev 
grow_to_at_least(size_type n)40049e08aacStbbdev     iterator grow_to_at_least( size_type n ) {
40149e08aacStbbdev         return internal_grow_to_at_least(n);
40249e08aacStbbdev     }
grow_to_at_least(size_type n,const value_type & value)40349e08aacStbbdev     iterator grow_to_at_least( size_type n, const value_type& value ) {
40449e08aacStbbdev         return internal_grow_to_at_least(n, value);
40549e08aacStbbdev     }
40649e08aacStbbdev 
push_back(const value_type & item)40749e08aacStbbdev     iterator push_back( const value_type& item ) {
40849e08aacStbbdev         return internal_emplace_back(item);
40949e08aacStbbdev     }
41049e08aacStbbdev 
push_back(value_type && item)41149e08aacStbbdev     iterator push_back( value_type&& item ) {
41249e08aacStbbdev         return internal_emplace_back(std::move(item));
41349e08aacStbbdev     }
41449e08aacStbbdev 
41549e08aacStbbdev     template <typename... Args>
emplace_back(Args &&...args)41649e08aacStbbdev     iterator emplace_back( Args&&... args ) {
41749e08aacStbbdev         return internal_emplace_back(std::forward<Args>(args)...);
41849e08aacStbbdev     }
41949e08aacStbbdev 
42049e08aacStbbdev     // Items access
42149e08aacStbbdev     reference operator[]( size_type index ) {
42249e08aacStbbdev         return internal_subscript(index);
42349e08aacStbbdev     }
42449e08aacStbbdev     const_reference operator[]( size_type index ) const {
42549e08aacStbbdev         return internal_subscript(index);
42649e08aacStbbdev     }
42749e08aacStbbdev 
at(size_type index)42849e08aacStbbdev     reference at( size_type index ) {
42949e08aacStbbdev         return internal_subscript_with_exceptions(index);
43049e08aacStbbdev     }
at(size_type index)43149e08aacStbbdev     const_reference at( size_type index ) const {
43249e08aacStbbdev         return internal_subscript_with_exceptions(index);
43349e08aacStbbdev     }
43449e08aacStbbdev 
43549e08aacStbbdev     // Get range for iterating with parallel algorithms
43649e08aacStbbdev     range_type range( size_t grainsize = 1 ) {
43749e08aacStbbdev         return range_type(begin(), end(), grainsize);
43849e08aacStbbdev     }
43949e08aacStbbdev 
44049e08aacStbbdev     // Get const range for iterating with parallel algorithms
44149e08aacStbbdev     const_range_type range( size_t grainsize = 1 ) const {
44249e08aacStbbdev         return const_range_type(begin(), end(), grainsize);
44349e08aacStbbdev     }
44449e08aacStbbdev 
front()44549e08aacStbbdev     reference front() {
44649e08aacStbbdev         return internal_subscript(0);
44749e08aacStbbdev     }
44849e08aacStbbdev 
front()44949e08aacStbbdev     const_reference front() const {
45049e08aacStbbdev         return internal_subscript(0);
45149e08aacStbbdev     }
45249e08aacStbbdev 
back()45349e08aacStbbdev     reference back() {
45449e08aacStbbdev         return internal_subscript(size() - 1);
45549e08aacStbbdev     }
45649e08aacStbbdev 
back()45749e08aacStbbdev     const_reference back() const {
45849e08aacStbbdev         return internal_subscript(size() - 1);
45949e08aacStbbdev     }
46049e08aacStbbdev 
46149e08aacStbbdev     // Iterators
begin()46249e08aacStbbdev     iterator begin() { return iterator(*this, 0); }
begin()46349e08aacStbbdev     const_iterator begin() const { return const_iterator(*this, 0); }
cbegin()46449e08aacStbbdev     const_iterator cbegin() const { return const_iterator(*this, 0); }
46549e08aacStbbdev 
end()46649e08aacStbbdev     iterator end() { return iterator(*this, size()); }
end()46749e08aacStbbdev     const_iterator end() const { return const_iterator(*this, size()); }
cend()46849e08aacStbbdev     const_iterator cend() const { return const_iterator(*this, size()); }
46949e08aacStbbdev 
rbegin()47049e08aacStbbdev     reverse_iterator rbegin() { return reverse_iterator(end()); }
rbegin()47149e08aacStbbdev     const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
crbegin()47249e08aacStbbdev     const_reverse_iterator crbegin() const { return const_reverse_iterator(cend()); }
47349e08aacStbbdev 
rend()47449e08aacStbbdev     reverse_iterator rend() { return reverse_iterator(begin()); }
rend()47549e08aacStbbdev     const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }
crend()47649e08aacStbbdev     const_reverse_iterator crend() const { return const_reverse_iterator(cbegin()); }
47749e08aacStbbdev 
get_allocator()47849e08aacStbbdev     allocator_type get_allocator() const {
47949e08aacStbbdev         return base_type::get_allocator();
48049e08aacStbbdev     }
48149e08aacStbbdev 
48249e08aacStbbdev     // Storage
empty()48349e08aacStbbdev     bool empty() const noexcept {
48449e08aacStbbdev         return 0 == size();
48549e08aacStbbdev     }
48649e08aacStbbdev 
size()48749e08aacStbbdev     size_type size() const noexcept {
48849e08aacStbbdev         return std::min(this->my_size.load(std::memory_order_acquire), capacity());
48949e08aacStbbdev     }
49049e08aacStbbdev 
max_size()49149e08aacStbbdev     size_type max_size() const noexcept {
49249e08aacStbbdev         return allocator_traits_type::max_size(base_type::get_allocator());
49349e08aacStbbdev     }
49449e08aacStbbdev 
capacity()49549e08aacStbbdev     size_type capacity() const noexcept {
49649e08aacStbbdev         return base_type::capacity();
49749e08aacStbbdev     }
49849e08aacStbbdev 
reserve(size_type n)49949e08aacStbbdev     void reserve( size_type n ) {
50049e08aacStbbdev         if (n == 0) return;
50149e08aacStbbdev 
50249e08aacStbbdev         if (n > max_size()) {
50349e08aacStbbdev             tbb::detail::throw_exception(exception_id::reservation_length_error);
50449e08aacStbbdev         }
50549e08aacStbbdev 
50649e08aacStbbdev         this->assign_first_block_if_necessary(this->segment_index_of(n - 1) + 1);
50749e08aacStbbdev         base_type::reserve(n);
50849e08aacStbbdev     }
50949e08aacStbbdev 
resize(size_type n)51049e08aacStbbdev     void resize( size_type n ) {
51149e08aacStbbdev         internal_resize(n);
51249e08aacStbbdev     }
51349e08aacStbbdev 
resize(size_type n,const value_type & val)51449e08aacStbbdev     void resize( size_type n, const value_type& val ) {
51549e08aacStbbdev         internal_resize(n, val);
51649e08aacStbbdev     }
51749e08aacStbbdev 
shrink_to_fit()51849e08aacStbbdev     void shrink_to_fit() {
51949e08aacStbbdev         internal_compact();
52049e08aacStbbdev     }
52149e08aacStbbdev 
swap(concurrent_vector & other)52249e08aacStbbdev     void swap(concurrent_vector& other) noexcept(is_noexcept_swap) {
52349e08aacStbbdev         base_type::swap(other);
52449e08aacStbbdev     }
52549e08aacStbbdev 
clear()52649e08aacStbbdev     void clear() {
52749e08aacStbbdev         destroy_elements();
52849e08aacStbbdev     }
52949e08aacStbbdev 
53049e08aacStbbdev private:
53149e08aacStbbdev     using segment_type = typename base_type::segment_type;
53249e08aacStbbdev     using segment_table_type = typename base_type::segment_table_type;
53349e08aacStbbdev     using segment_table_allocator_traits = typename base_type::segment_table_allocator_traits;
53449e08aacStbbdev     using segment_index_type = typename base_type::segment_index_type;
53549e08aacStbbdev 
53649e08aacStbbdev     using segment_element_type = typename base_type::value_type;
53749e08aacStbbdev     using segment_element_allocator_type = typename allocator_traits_type::template rebind_alloc<segment_element_type>;
53849e08aacStbbdev     using segment_element_allocator_traits = tbb::detail::allocator_traits<segment_element_allocator_type>;
53949e08aacStbbdev 
allocate_long_table(const typename base_type::atomic_segment * embedded_table,size_type start_index)54049e08aacStbbdev     segment_table_type allocate_long_table( const typename base_type::atomic_segment* embedded_table, size_type start_index ) {
54149e08aacStbbdev         __TBB_ASSERT(start_index <= this->embedded_table_size, "Start index out of embedded table");
54249e08aacStbbdev 
54349e08aacStbbdev         // If other threads are trying to set pointers in the short segment, wait for them to finish their
54449e08aacStbbdev         // assignments before we copy the short segment to the long segment. Note: grow_to_at_least depends on it
54549e08aacStbbdev         for (segment_index_type i = 0; this->segment_base(i) < start_index; ++i) {
54649e08aacStbbdev             spin_wait_while_eq(embedded_table[i], segment_type(nullptr));
54749e08aacStbbdev         }
54849e08aacStbbdev 
54949e08aacStbbdev         // It is possible that the table was extend by a thread allocating first_block, need to check this.
55049e08aacStbbdev         if (this->get_table() != embedded_table) {
55149e08aacStbbdev             return nullptr;
55249e08aacStbbdev         }
55349e08aacStbbdev 
55449e08aacStbbdev         // Allocate long segment table and fill with null pointers
55549e08aacStbbdev         segment_table_type new_segment_table = segment_table_allocator_traits::allocate(base_type::get_allocator(), this->pointers_per_long_table);
55649e08aacStbbdev         // Copy segment pointers from the embedded table
55749e08aacStbbdev         for (size_type segment_index = 0; segment_index < this->pointers_per_embedded_table; ++segment_index) {
55849e08aacStbbdev             segment_table_allocator_traits::construct(base_type::get_allocator(), &new_segment_table[segment_index],
55949e08aacStbbdev                 embedded_table[segment_index].load(std::memory_order_relaxed));
56049e08aacStbbdev         }
56149e08aacStbbdev         for (size_type segment_index = this->pointers_per_embedded_table; segment_index < this->pointers_per_long_table; ++segment_index) {
56249e08aacStbbdev             segment_table_allocator_traits::construct(base_type::get_allocator(), &new_segment_table[segment_index], nullptr);
56349e08aacStbbdev         }
56449e08aacStbbdev 
56549e08aacStbbdev         return new_segment_table;
56649e08aacStbbdev     }
56749e08aacStbbdev 
56849e08aacStbbdev     // create_segment function is required by the segment_table base class
create_segment(segment_table_type table,segment_index_type seg_index,size_type index)56949e08aacStbbdev     segment_type create_segment( segment_table_type table, segment_index_type seg_index, size_type index ) {
57049e08aacStbbdev         size_type first_block = this->my_first_block.load(std::memory_order_relaxed);
57149e08aacStbbdev         // First block allocation
57249e08aacStbbdev         if (seg_index < first_block) {
57349e08aacStbbdev             // If 0 segment is already allocated, then it remains to wait until the segments are filled to requested
57449e08aacStbbdev             if (table[0].load(std::memory_order_acquire) != nullptr) {
57549e08aacStbbdev                 spin_wait_while_eq(table[seg_index], segment_type(nullptr));
57649e08aacStbbdev                 return nullptr;
57749e08aacStbbdev             }
57849e08aacStbbdev 
57949e08aacStbbdev             segment_element_allocator_type segment_allocator(base_type::get_allocator());
58049e08aacStbbdev             segment_type new_segment = nullptr;
58149e08aacStbbdev             size_type first_block_size = this->segment_size(first_block);
58249e08aacStbbdev             try_call( [&] {
58349e08aacStbbdev                 new_segment = segment_element_allocator_traits::allocate(segment_allocator, first_block_size);
58449e08aacStbbdev             } ).on_exception( [&] {
58549e08aacStbbdev                 segment_type disabled_segment = nullptr;
58649e08aacStbbdev                 if (table[0].compare_exchange_strong(disabled_segment, this->segment_allocation_failure_tag)) {
58749e08aacStbbdev                     size_type end_segment = table == this->my_embedded_table ? this->pointers_per_embedded_table : first_block;
58849e08aacStbbdev                     for (size_type i = 1; i < end_segment; ++i) {
58949e08aacStbbdev                         table[i].store(this->segment_allocation_failure_tag, std::memory_order_release);
59049e08aacStbbdev                     }
59149e08aacStbbdev                 }
59249e08aacStbbdev             });
59349e08aacStbbdev 
59449e08aacStbbdev             segment_type disabled_segment = nullptr;
59549e08aacStbbdev             if (table[0].compare_exchange_strong(disabled_segment, new_segment)) {
59649e08aacStbbdev                 this->extend_table_if_necessary(table, 0, first_block_size);
59749e08aacStbbdev                 for (size_type i = 1; i < first_block; ++i) {
59849e08aacStbbdev                     table[i].store(new_segment, std::memory_order_release);
59949e08aacStbbdev                 }
60049e08aacStbbdev 
60149e08aacStbbdev                 // Other threads can wait on a snapshot of an embedded table, need to fill it.
60249e08aacStbbdev                 for (size_type i = 1; i < first_block && i < this->pointers_per_embedded_table; ++i) {
60349e08aacStbbdev                     this->my_embedded_table[i].store(new_segment, std::memory_order_release);
60449e08aacStbbdev                 }
60549e08aacStbbdev             } else if (new_segment != this->segment_allocation_failure_tag) {
60649e08aacStbbdev                 // Deallocate the memory
60749e08aacStbbdev                 segment_element_allocator_traits::deallocate(segment_allocator, new_segment, first_block_size);
60849e08aacStbbdev                 // 0 segment is already allocated, then it remains to wait until the segments are filled to requested
60949e08aacStbbdev                 spin_wait_while_eq(table[seg_index], segment_type(nullptr));
61049e08aacStbbdev             }
61149e08aacStbbdev         } else {
61249e08aacStbbdev             size_type offset = this->segment_base(seg_index);
61349e08aacStbbdev             if (index == offset) {
61449e08aacStbbdev                 __TBB_ASSERT(table[seg_index].load(std::memory_order_relaxed) == nullptr, "Only this thread can enable this segment");
61549e08aacStbbdev                 segment_element_allocator_type segment_allocator(base_type::get_allocator());
61649e08aacStbbdev                 segment_type new_segment = this->segment_allocation_failure_tag;
61749e08aacStbbdev                 try_call( [&] {
61849e08aacStbbdev                     new_segment = segment_element_allocator_traits::allocate(segment_allocator,this->segment_size(seg_index));
61949e08aacStbbdev                     // Shift base address to simplify access by index
62049e08aacStbbdev                     new_segment -= this->segment_base(seg_index);
62149e08aacStbbdev                 } ).on_completion( [&] {
62249e08aacStbbdev                     table[seg_index].store(new_segment, std::memory_order_release);
62349e08aacStbbdev                 });
62449e08aacStbbdev             } else {
62549e08aacStbbdev                 spin_wait_while_eq(table[seg_index], segment_type(nullptr));
62649e08aacStbbdev             }
62749e08aacStbbdev         }
62849e08aacStbbdev         return nullptr;
62949e08aacStbbdev     }
63049e08aacStbbdev 
63149e08aacStbbdev     // Returns the number of elements in the segment to be destroy
number_of_elements_in_segment(segment_index_type seg_index)63249e08aacStbbdev     size_type number_of_elements_in_segment( segment_index_type seg_index ) {
63349e08aacStbbdev         size_type curr_vector_size = this->my_size.load(std::memory_order_relaxed);
63449e08aacStbbdev         size_type curr_segment_base = this->segment_base(seg_index);
63549e08aacStbbdev 
63649e08aacStbbdev         if (seg_index == 0) {
63749e08aacStbbdev             return std::min(curr_vector_size, this->segment_size(seg_index));
63849e08aacStbbdev         } else {
63949e08aacStbbdev             // Perhaps the segment is allocated, but there are no elements in it.
64049e08aacStbbdev             if (curr_vector_size < curr_segment_base) {
64149e08aacStbbdev                 return 0;
64249e08aacStbbdev             }
64349e08aacStbbdev             return curr_segment_base * 2 > curr_vector_size ? curr_vector_size - curr_segment_base : curr_segment_base;
64449e08aacStbbdev         }
64549e08aacStbbdev     }
64649e08aacStbbdev 
nullify_segment(segment_table_type table,size_type segment_index)647a36bf9cbSPavel Kumbrasev     segment_type nullify_segment( segment_table_type table, size_type segment_index ) {
648a36bf9cbSPavel Kumbrasev         segment_type target_segment = table[segment_index].load(std::memory_order_relaxed);
649a36bf9cbSPavel Kumbrasev         if (segment_index >= this->my_first_block) {
650a36bf9cbSPavel Kumbrasev             table[segment_index].store(nullptr, std::memory_order_relaxed);
651a36bf9cbSPavel Kumbrasev         } else {
652a36bf9cbSPavel Kumbrasev             if (segment_index == 0) {
653a36bf9cbSPavel Kumbrasev                 for (size_type i = 0; i < this->my_first_block; ++i) {
654a36bf9cbSPavel Kumbrasev                     table[i].store(nullptr, std::memory_order_relaxed);
655a36bf9cbSPavel Kumbrasev                 }
656a36bf9cbSPavel Kumbrasev             }
657a36bf9cbSPavel Kumbrasev         }
658a36bf9cbSPavel Kumbrasev 
659a36bf9cbSPavel Kumbrasev         return target_segment;
660a36bf9cbSPavel Kumbrasev     }
661a36bf9cbSPavel Kumbrasev 
deallocate_segment(segment_type address,segment_index_type seg_index)66249e08aacStbbdev     void deallocate_segment( segment_type address, segment_index_type seg_index ) {
66349e08aacStbbdev         segment_element_allocator_type segment_allocator(base_type::get_allocator());
66449e08aacStbbdev         size_type first_block = this->my_first_block.load(std::memory_order_relaxed);
66549e08aacStbbdev         if (seg_index >= first_block) {
66649e08aacStbbdev             segment_element_allocator_traits::deallocate(segment_allocator, address, this->segment_size(seg_index));
66749e08aacStbbdev         }
66849e08aacStbbdev         else if (seg_index == 0) {
66949e08aacStbbdev             size_type elements_to_deallocate = first_block > 0 ? this->segment_size(first_block) : this->segment_size(0);
67049e08aacStbbdev             segment_element_allocator_traits::deallocate(segment_allocator, address, elements_to_deallocate);
67149e08aacStbbdev         }
67249e08aacStbbdev     }
67349e08aacStbbdev 
67449e08aacStbbdev     // destroy_segment function is required by the segment_table base class
destroy_segment(segment_type address,segment_index_type seg_index)67549e08aacStbbdev     void destroy_segment( segment_type address, segment_index_type seg_index ) {
67649e08aacStbbdev         size_type elements_to_destroy = number_of_elements_in_segment(seg_index);
67749e08aacStbbdev         segment_element_allocator_type segment_allocator(base_type::get_allocator());
67849e08aacStbbdev 
67949e08aacStbbdev         for (size_type i = 0; i < elements_to_destroy; ++i) {
68049e08aacStbbdev             segment_element_allocator_traits::destroy(segment_allocator, address + i);
68149e08aacStbbdev         }
68249e08aacStbbdev 
68349e08aacStbbdev         deallocate_segment(address, seg_index);
68449e08aacStbbdev     }
68549e08aacStbbdev 
68649e08aacStbbdev     // copy_segment function is required by the segment_table base class
copy_segment(segment_index_type seg_index,segment_type from,segment_type to)68749e08aacStbbdev     void copy_segment( segment_index_type seg_index, segment_type from, segment_type to ) {
68849e08aacStbbdev         size_type i = 0;
68949e08aacStbbdev         try_call( [&] {
69049e08aacStbbdev             for (; i != number_of_elements_in_segment(seg_index); ++i) {
69149e08aacStbbdev                 segment_table_allocator_traits::construct(base_type::get_allocator(), to + i, from[i]);
69249e08aacStbbdev             }
69349e08aacStbbdev         } ).on_exception( [&] {
69449e08aacStbbdev             // Zero-initialize items left not constructed after the exception
69549e08aacStbbdev             zero_unconstructed_elements(this->get_segment(seg_index) + i, this->segment_size(seg_index) - i);
69649e08aacStbbdev 
69749e08aacStbbdev             segment_index_type last_segment = this->segment_index_of(this->my_size.load(std::memory_order_relaxed));
69849e08aacStbbdev             auto table = this->get_table();
69949e08aacStbbdev             for (segment_index_type j = seg_index + 1; j != last_segment; ++j) {
70049e08aacStbbdev                 auto curr_segment = table[j].load(std::memory_order_relaxed);
70149e08aacStbbdev                 if (curr_segment) {
70249e08aacStbbdev                     zero_unconstructed_elements(curr_segment + this->segment_base(j), this->segment_size(j));
70349e08aacStbbdev                 }
70449e08aacStbbdev             }
70549e08aacStbbdev             this->my_size.store(this->segment_size(seg_index) + i, std::memory_order_relaxed);
70649e08aacStbbdev         });
70749e08aacStbbdev     }
70849e08aacStbbdev 
70949e08aacStbbdev     // move_segment function is required by the segment_table base class
move_segment(segment_index_type seg_index,segment_type from,segment_type to)71049e08aacStbbdev     void move_segment( segment_index_type seg_index, segment_type from, segment_type to ) {
71149e08aacStbbdev         size_type i = 0;
71249e08aacStbbdev         try_call( [&] {
71349e08aacStbbdev             for (; i != number_of_elements_in_segment(seg_index); ++i) {
71449e08aacStbbdev                 segment_table_allocator_traits::construct(base_type::get_allocator(), to + i, std::move(from[i]));
71549e08aacStbbdev             }
71649e08aacStbbdev         } ).on_exception( [&] {
71749e08aacStbbdev             // Zero-initialize items left not constructed after the exception
71849e08aacStbbdev             zero_unconstructed_elements(this->get_segment(seg_index) + i, this->segment_size(seg_index) - i);
71949e08aacStbbdev 
72049e08aacStbbdev             segment_index_type last_segment = this->segment_index_of(this->my_size.load(std::memory_order_relaxed));
72149e08aacStbbdev             auto table = this->get_table();
72249e08aacStbbdev             for (segment_index_type j = seg_index + 1; j != last_segment; ++j) {
72349e08aacStbbdev                 auto curr_segment = table[j].load(std::memory_order_relaxed);
72449e08aacStbbdev                 if (curr_segment) {
72549e08aacStbbdev                     zero_unconstructed_elements(curr_segment + this->segment_base(j), this->segment_size(j));
72649e08aacStbbdev                 }
72749e08aacStbbdev             }
72849e08aacStbbdev             this->my_size.store(this->segment_size(seg_index) + i, std::memory_order_relaxed);
72949e08aacStbbdev         });
73049e08aacStbbdev     }
73149e08aacStbbdev 
is_first_element_in_segment(size_type index)73249e08aacStbbdev     static constexpr bool is_first_element_in_segment( size_type index ) {
73349e08aacStbbdev         // An element is the first in a segment if its index is equal to a power of two
73449e08aacStbbdev         return is_power_of_two_at_least(index, 2);
73549e08aacStbbdev     }
73649e08aacStbbdev 
internal_subscript(size_type index)73749e08aacStbbdev     const_reference internal_subscript( size_type index ) const {
73849e08aacStbbdev         return const_cast<self_type*>(this)->internal_subscript(index);
73949e08aacStbbdev     }
74049e08aacStbbdev 
internal_subscript(size_type index)74149e08aacStbbdev     reference internal_subscript( size_type index ) {
74249e08aacStbbdev         __TBB_ASSERT(index < this->my_size.load(std::memory_order_relaxed), "Invalid subscript index");
74349e08aacStbbdev         return base_type::template internal_subscript</*allow_out_of_range_access=*/false>(index);
74449e08aacStbbdev     }
74549e08aacStbbdev 
internal_subscript_with_exceptions(size_type index)74649e08aacStbbdev     const_reference internal_subscript_with_exceptions( size_type index ) const {
74749e08aacStbbdev         return const_cast<self_type*>(this)->internal_subscript_with_exceptions(index);
74849e08aacStbbdev     }
74949e08aacStbbdev 
internal_subscript_with_exceptions(size_type index)75049e08aacStbbdev     reference internal_subscript_with_exceptions( size_type index ) {
75149e08aacStbbdev         if (index >= this->my_size.load(std::memory_order_acquire)) {
75249e08aacStbbdev             tbb::detail::throw_exception(exception_id::out_of_range);
75349e08aacStbbdev         }
75449e08aacStbbdev 
75549e08aacStbbdev         segment_table_type table = this->my_segment_table.load(std::memory_order_acquire);
75649e08aacStbbdev 
75749e08aacStbbdev         size_type seg_index = this->segment_index_of(index);
75849e08aacStbbdev         if (base_type::number_of_segments(table) < seg_index) {
75949e08aacStbbdev             tbb::detail::throw_exception(exception_id::out_of_range);
76049e08aacStbbdev         }
76149e08aacStbbdev 
76249e08aacStbbdev         if (table[seg_index] <= this->segment_allocation_failure_tag) {
76349e08aacStbbdev             tbb::detail::throw_exception(exception_id::out_of_range);
76449e08aacStbbdev         }
76549e08aacStbbdev 
76649e08aacStbbdev         return base_type::template internal_subscript</*allow_out_of_range_access=*/false>(index);
76749e08aacStbbdev     }
76849e08aacStbbdev 
zero_unconstructed_elements(pointer start,size_type count)76949e08aacStbbdev     static void zero_unconstructed_elements( pointer start, size_type count ) {
77049e08aacStbbdev         std::memset(static_cast<void *>(start), 0, count * sizeof(value_type));
77149e08aacStbbdev     }
77249e08aacStbbdev 
77349e08aacStbbdev     template <typename... Args>
internal_emplace_back(Args &&...args)77449e08aacStbbdev     iterator internal_emplace_back( Args&&... args ) {
77549e08aacStbbdev         size_type old_size = this->my_size++;
77649e08aacStbbdev         this->assign_first_block_if_necessary(default_first_block_size);
77749e08aacStbbdev         auto element_address = &base_type::template internal_subscript</*allow_out_of_range_access=*/true>(old_size);
77849e08aacStbbdev 
77949e08aacStbbdev         // try_call API is not convenient here due to broken
78049e08aacStbbdev         // variadic capture on GCC 4.8.5
78149e08aacStbbdev         auto value_guard = make_raii_guard([&] {
78249e08aacStbbdev             zero_unconstructed_elements(element_address, /*count =*/1);
78349e08aacStbbdev         });
78449e08aacStbbdev 
78549e08aacStbbdev         segment_table_allocator_traits::construct(base_type::get_allocator(), element_address, std::forward<Args>(args)...);
78649e08aacStbbdev         value_guard.dismiss();
78749e08aacStbbdev         return iterator(*this, old_size, element_address);
78849e08aacStbbdev     }
78949e08aacStbbdev 
79049e08aacStbbdev     template <typename... Args>
internal_loop_construct(segment_table_type table,size_type start_idx,size_type end_idx,const Args &...args)79149e08aacStbbdev     void internal_loop_construct( segment_table_type table, size_type start_idx, size_type end_idx, const Args&... args ) {
79249e08aacStbbdev         static_assert(sizeof...(Args) < 2, "Too many parameters");
79349e08aacStbbdev         for (size_type idx = start_idx; idx < end_idx; ++idx) {
79449e08aacStbbdev             auto element_address = &base_type::template internal_subscript</*allow_out_of_range_access=*/true>(idx);
79549e08aacStbbdev             // try_call API is not convenient here due to broken
79649e08aacStbbdev             // variadic capture on GCC 4.8.5
79749e08aacStbbdev             auto value_guard = make_raii_guard( [&] {
79849e08aacStbbdev                 segment_index_type last_allocated_segment = this->find_last_allocated_segment(table);
79949e08aacStbbdev                 size_type segment_size = this->segment_size(last_allocated_segment);
80049e08aacStbbdev                 end_idx = end_idx < segment_size ? end_idx : segment_size;
80149e08aacStbbdev                 for (size_type i = idx; i < end_idx; ++i) {
80249e08aacStbbdev                     zero_unconstructed_elements(&this->internal_subscript(i), /*count =*/1);
80349e08aacStbbdev                 }
80449e08aacStbbdev             });
80549e08aacStbbdev             segment_table_allocator_traits::construct(base_type::get_allocator(), element_address, args...);
80649e08aacStbbdev             value_guard.dismiss();
80749e08aacStbbdev         }
80849e08aacStbbdev     }
80949e08aacStbbdev 
81049e08aacStbbdev     template <typename ForwardIterator>
internal_loop_construct(segment_table_type table,size_type start_idx,size_type end_idx,ForwardIterator first,ForwardIterator)81149e08aacStbbdev     void internal_loop_construct( segment_table_type table, size_type start_idx, size_type end_idx, ForwardIterator first, ForwardIterator ) {
81249e08aacStbbdev         for (size_type idx = start_idx; idx < end_idx; ++idx) {
81349e08aacStbbdev             auto element_address = &base_type::template internal_subscript</*allow_out_of_range_access=*/true>(idx);
81449e08aacStbbdev             try_call( [&] {
81549e08aacStbbdev                 segment_table_allocator_traits::construct(base_type::get_allocator(), element_address, *first++);
81649e08aacStbbdev             } ).on_exception( [&] {
81749e08aacStbbdev                 segment_index_type last_allocated_segment = this->find_last_allocated_segment(table);
81849e08aacStbbdev                 size_type segment_size = this->segment_size(last_allocated_segment);
81949e08aacStbbdev                 end_idx = end_idx < segment_size ? end_idx : segment_size;
82049e08aacStbbdev                 for (size_type i = idx; i < end_idx; ++i) {
82149e08aacStbbdev                     zero_unconstructed_elements(&this->internal_subscript(i), /*count =*/1);
82249e08aacStbbdev                 }
82349e08aacStbbdev             });
82449e08aacStbbdev         }
82549e08aacStbbdev     }
82649e08aacStbbdev 
82749e08aacStbbdev     template <typename... Args>
internal_grow(size_type start_idx,size_type end_idx,const Args &...args)82849e08aacStbbdev     iterator internal_grow( size_type start_idx, size_type end_idx, const Args&... args ) {
82949e08aacStbbdev         this->assign_first_block_if_necessary(this->segment_index_of(end_idx - 1) + 1);
83049e08aacStbbdev         size_type seg_index = this->segment_index_of(end_idx - 1);
83149e08aacStbbdev         segment_table_type table = this->get_table();
83249e08aacStbbdev         this->extend_table_if_necessary(table, start_idx, end_idx);
83349e08aacStbbdev 
83449e08aacStbbdev         if (seg_index > this->my_first_block.load(std::memory_order_relaxed)) {
83549e08aacStbbdev             // So that other threads be able to work with the last segment of grow_by, allocate it immediately.
83649e08aacStbbdev             // If the last segment is not less than the first block
83749e08aacStbbdev             if (table[seg_index].load(std::memory_order_relaxed) == nullptr) {
83849e08aacStbbdev                 size_type first_element = this->segment_base(seg_index);
83949e08aacStbbdev                 if (first_element >= start_idx && first_element < end_idx) {
84049e08aacStbbdev                     segment_type segment = table[seg_index].load(std::memory_order_relaxed);
84149e08aacStbbdev                     base_type::enable_segment(segment, table, seg_index, first_element);
84249e08aacStbbdev                 }
84349e08aacStbbdev             }
84449e08aacStbbdev         }
84549e08aacStbbdev 
84649e08aacStbbdev         internal_loop_construct(table, start_idx, end_idx, args...);
84749e08aacStbbdev 
84849e08aacStbbdev         return iterator(*this, start_idx, &base_type::template internal_subscript</*allow_out_of_range_access=*/false>(start_idx));
84949e08aacStbbdev     }
85049e08aacStbbdev 
85149e08aacStbbdev 
85249e08aacStbbdev     template <typename... Args>
internal_grow_by_delta(size_type delta,const Args &...args)85349e08aacStbbdev     iterator internal_grow_by_delta( size_type delta, const Args&... args ) {
85449e08aacStbbdev         if (delta == size_type(0)) {
85549e08aacStbbdev             return end();
85649e08aacStbbdev         }
85749e08aacStbbdev         size_type start_idx = this->my_size.fetch_add(delta);
85849e08aacStbbdev         size_type end_idx = start_idx + delta;
85949e08aacStbbdev 
86049e08aacStbbdev         return internal_grow(start_idx, end_idx, args...);
86149e08aacStbbdev     }
86249e08aacStbbdev 
86349e08aacStbbdev     template <typename... Args>
internal_grow_to_at_least(size_type new_size,const Args &...args)86449e08aacStbbdev     iterator internal_grow_to_at_least( size_type new_size, const Args&... args ) {
86549e08aacStbbdev         size_type old_size = this->my_size.load(std::memory_order_relaxed);
86649e08aacStbbdev         if (new_size == size_type(0)) return iterator(*this, 0);
86749e08aacStbbdev         while (old_size < new_size && !this->my_size.compare_exchange_weak(old_size, new_size))
86849e08aacStbbdev         {}
86949e08aacStbbdev 
87049e08aacStbbdev         int delta = static_cast<int>(new_size) - static_cast<int>(old_size);
87149e08aacStbbdev         if (delta > 0) {
87249e08aacStbbdev             return internal_grow(old_size, new_size, args...);
87349e08aacStbbdev         }
87449e08aacStbbdev 
87549e08aacStbbdev         size_type end_segment = this->segment_index_of(new_size - 1);
87649e08aacStbbdev 
87749e08aacStbbdev         // Check/wait for segments allocation completes
87849e08aacStbbdev         if (end_segment >= this->pointers_per_embedded_table &&
87949e08aacStbbdev             this->get_table() == this->my_embedded_table)
88049e08aacStbbdev         {
88149e08aacStbbdev             spin_wait_while_eq(this->my_segment_table, this->my_embedded_table);
88249e08aacStbbdev         }
88349e08aacStbbdev 
88449e08aacStbbdev         for (segment_index_type seg_idx = 0; seg_idx <= end_segment; ++seg_idx) {
88549e08aacStbbdev             if (this->get_table()[seg_idx].load(std::memory_order_relaxed) == nullptr) {
88649e08aacStbbdev                 atomic_backoff backoff(true);
88749e08aacStbbdev                 while (this->get_table()[seg_idx].load(std::memory_order_relaxed) == nullptr) {
88849e08aacStbbdev                     backoff.pause();
88949e08aacStbbdev                 }
89049e08aacStbbdev             }
89149e08aacStbbdev         }
89249e08aacStbbdev 
89349e08aacStbbdev     #if TBB_USE_DEBUG
89449e08aacStbbdev         size_type cap = capacity();
89557f524caSIlya Isaev         __TBB_ASSERT( cap >= new_size, nullptr);
89649e08aacStbbdev     #endif
89749e08aacStbbdev         return iterator(*this, size());
89849e08aacStbbdev     }
89949e08aacStbbdev 
90049e08aacStbbdev     template <typename... Args>
internal_resize(size_type n,const Args &...args)90149e08aacStbbdev     void internal_resize( size_type n, const Args&... args ) {
90249e08aacStbbdev         if (n == 0) {
90349e08aacStbbdev             clear();
90449e08aacStbbdev             return;
90549e08aacStbbdev         }
90649e08aacStbbdev 
90749e08aacStbbdev         size_type old_size = this->my_size.load(std::memory_order_acquire);
90849e08aacStbbdev         if (n > old_size) {
90949e08aacStbbdev             reserve(n);
91049e08aacStbbdev             grow_to_at_least(n, args...);
91149e08aacStbbdev         } else {
91249e08aacStbbdev             if (old_size == n) {
91349e08aacStbbdev                 return;
91449e08aacStbbdev             }
91549e08aacStbbdev             size_type last_segment = this->segment_index_of(old_size - 1);
91649e08aacStbbdev             // Delete segments
91749e08aacStbbdev             for (size_type seg_idx = this->segment_index_of(n - 1) + 1; seg_idx <= last_segment; ++seg_idx) {
91849e08aacStbbdev                 this->delete_segment(seg_idx);
91949e08aacStbbdev             }
92049e08aacStbbdev 
92149e08aacStbbdev             // If n > segment_size(n) => we need to destroy all of the items in the first segment
92249e08aacStbbdev             // Otherwise, we need to destroy only items with the index < n
92349e08aacStbbdev             size_type n_segment = this->segment_index_of(n - 1);
92449e08aacStbbdev             size_type last_index_to_destroy = std::min(this->segment_base(n_segment) + this->segment_size(n_segment), old_size);
92549e08aacStbbdev             // Destroy elements in curr segment
92649e08aacStbbdev             for (size_type idx = n; idx < last_index_to_destroy; ++idx) {
92749e08aacStbbdev                 segment_table_allocator_traits::destroy(base_type::get_allocator(), &base_type::template internal_subscript</*allow_out_of_range_access=*/false>(idx));
92849e08aacStbbdev             }
92949e08aacStbbdev             this->my_size.store(n, std::memory_order_release);
93049e08aacStbbdev         }
93149e08aacStbbdev     }
93249e08aacStbbdev 
destroy_elements()93349e08aacStbbdev     void destroy_elements() {
93449e08aacStbbdev         allocator_type alloc(base_type::get_allocator());
93549e08aacStbbdev         for (size_type i = 0; i < this->my_size.load(std::memory_order_relaxed); ++i) {
93649e08aacStbbdev             allocator_traits_type::destroy(alloc, &base_type::template internal_subscript</*allow_out_of_range_access=*/false>(i));
93749e08aacStbbdev         }
93849e08aacStbbdev         this->my_size.store(0, std::memory_order_relaxed);
93949e08aacStbbdev     }
94049e08aacStbbdev 
incompact_predicate(size_type size)94149e08aacStbbdev     static bool incompact_predicate( size_type size ) {
94249e08aacStbbdev         // memory page size
94349e08aacStbbdev         const size_type page_size = 4096;
94449e08aacStbbdev         return size < page_size || ((size - 1) % page_size < page_size / 2 && size < page_size * 128);
94549e08aacStbbdev     }
94649e08aacStbbdev 
internal_compact()94749e08aacStbbdev     void internal_compact() {
94849e08aacStbbdev         const size_type curr_size = this->my_size.load(std::memory_order_relaxed);
94949e08aacStbbdev         segment_table_type table = this->get_table();
95049e08aacStbbdev         const segment_index_type k_end = this->find_last_allocated_segment(table);                   // allocated segments
95149e08aacStbbdev         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;..
95249e08aacStbbdev         const segment_index_type first_block = this->my_first_block;                                 // number of merged segments, getting values from atomics
95349e08aacStbbdev 
95449e08aacStbbdev         segment_index_type k = first_block;
95549e08aacStbbdev         if (k_stop < first_block) {
95649e08aacStbbdev             k = k_stop;
95749e08aacStbbdev         }
95849e08aacStbbdev         else {
95949e08aacStbbdev             while (k < k_stop && incompact_predicate(this->segment_size(k) * sizeof(value_type))) k++;
96049e08aacStbbdev         }
96149e08aacStbbdev 
96249e08aacStbbdev         if (k_stop == k_end && k == first_block) {
96349e08aacStbbdev             return;
96449e08aacStbbdev         }
96549e08aacStbbdev 
96649e08aacStbbdev         // First segment optimization
96749e08aacStbbdev         if (k != first_block && k) {
96849e08aacStbbdev             size_type max_block = std::max(first_block, k);
96949e08aacStbbdev 
97049e08aacStbbdev             auto buffer_table = segment_table_allocator_traits::allocate(base_type::get_allocator(), max_block);
97149e08aacStbbdev 
97249e08aacStbbdev             for (size_type seg_idx = 0; seg_idx < max_block; ++seg_idx) {
97349e08aacStbbdev                 segment_table_allocator_traits::construct(base_type::get_allocator(), &buffer_table[seg_idx],
97449e08aacStbbdev                     table[seg_idx].load(std::memory_order_relaxed));
97549e08aacStbbdev                 table[seg_idx].store(nullptr, std::memory_order_relaxed);
97649e08aacStbbdev             }
97749e08aacStbbdev 
97849e08aacStbbdev             this->my_first_block.store(k, std::memory_order_relaxed);
97949e08aacStbbdev             size_type index = 0;
98049e08aacStbbdev             try_call( [&] {
98149e08aacStbbdev                 for (; index < std::min(this->segment_size(max_block), curr_size); ++index) {
98249e08aacStbbdev                     auto element_address = &static_cast<base_type*>(this)->operator[](index);
98349e08aacStbbdev                     segment_index_type seg_idx = this->segment_index_of(index);
98449e08aacStbbdev                     segment_table_allocator_traits::construct(base_type::get_allocator(), element_address,
98549e08aacStbbdev                     std::move_if_noexcept(buffer_table[seg_idx].load(std::memory_order_relaxed)[index]));
98649e08aacStbbdev                 }
98749e08aacStbbdev             } ).on_exception( [&] {
98849e08aacStbbdev                 segment_element_allocator_type allocator(base_type::get_allocator());
98949e08aacStbbdev                 for (size_type i = 0; i < index; ++i) {
99049e08aacStbbdev                     auto element_adress = &this->operator[](i);
99149e08aacStbbdev                     segment_element_allocator_traits::destroy(allocator, element_adress);
99249e08aacStbbdev                 }
99349e08aacStbbdev                 segment_element_allocator_traits::deallocate(allocator,
99449e08aacStbbdev                     table[0].load(std::memory_order_relaxed), this->segment_size(max_block));
99549e08aacStbbdev 
99649e08aacStbbdev                 for (size_type seg_idx = 0; seg_idx < max_block; ++seg_idx) {
99749e08aacStbbdev                     table[seg_idx].store(buffer_table[seg_idx].load(std::memory_order_relaxed),
99849e08aacStbbdev                         std::memory_order_relaxed);
99949e08aacStbbdev                     buffer_table[seg_idx].store(nullptr, std::memory_order_relaxed);
100049e08aacStbbdev                 }
100149e08aacStbbdev                 segment_table_allocator_traits::deallocate(base_type::get_allocator(),
100249e08aacStbbdev                     buffer_table, max_block);
100349e08aacStbbdev                 this->my_first_block.store(first_block, std::memory_order_relaxed);
100449e08aacStbbdev             });
100549e08aacStbbdev 
100649e08aacStbbdev             // Need to correct deallocate old segments
100749e08aacStbbdev             // Method destroy_segment respect active first_block, therefore,
100849e08aacStbbdev             // in order for the segment deletion to work correctly, set the first_block size that was earlier,
100949e08aacStbbdev             // destroy the unnecessary segments.
101049e08aacStbbdev             this->my_first_block.store(first_block, std::memory_order_relaxed);
101149e08aacStbbdev             for (size_type seg_idx = max_block; seg_idx > 0 ; --seg_idx) {
101249e08aacStbbdev                 auto curr_segment = buffer_table[seg_idx - 1].load(std::memory_order_relaxed);
101349e08aacStbbdev                 if (curr_segment != nullptr) {
101449e08aacStbbdev                     destroy_segment(buffer_table[seg_idx - 1].load(std::memory_order_relaxed) + this->segment_base(seg_idx - 1),
101549e08aacStbbdev                         seg_idx - 1);
101649e08aacStbbdev                 }
101749e08aacStbbdev             }
101849e08aacStbbdev 
101949e08aacStbbdev             this->my_first_block.store(k, std::memory_order_relaxed);
102049e08aacStbbdev 
102149e08aacStbbdev             for (size_type seg_idx = 0; seg_idx < max_block; ++seg_idx) {
102249e08aacStbbdev                 segment_table_allocator_traits::destroy(base_type::get_allocator(), &buffer_table[seg_idx]);
102349e08aacStbbdev             }
102449e08aacStbbdev 
102549e08aacStbbdev             segment_table_allocator_traits::deallocate(base_type::get_allocator(), buffer_table, max_block);
102649e08aacStbbdev         }
102749e08aacStbbdev         // free unnecessary segments allocated by reserve() call
102849e08aacStbbdev         if (k_stop < k_end) {
102949e08aacStbbdev             for (size_type seg_idx = k_end; seg_idx != k_stop; --seg_idx) {
103049e08aacStbbdev                 if (table[seg_idx - 1].load(std::memory_order_relaxed) != nullptr) {
103149e08aacStbbdev                     this->delete_segment(seg_idx - 1);
103249e08aacStbbdev                 }
103349e08aacStbbdev             }
1034*5e91b2c0SVladislav Shchapov             if (!k) this->my_first_block.store(0, std::memory_order_relaxed);
103549e08aacStbbdev         }
103649e08aacStbbdev     }
103749e08aacStbbdev 
103849e08aacStbbdev     // Lever for adjusting the size of first_block at the very first insertion.
103949e08aacStbbdev     // TODO: consider >1 value, check performance
104049e08aacStbbdev     static constexpr size_type default_first_block_size = 1;
104149e08aacStbbdev 
104249e08aacStbbdev     template <typename Vector, typename Value>
104349e08aacStbbdev     friend class vector_iterator;
104449e08aacStbbdev }; // class concurrent_vector
104549e08aacStbbdev 
104649e08aacStbbdev #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
104749e08aacStbbdev // Deduction guide for the constructor from two iterators
1048d86ed7fbStbbdev template <typename It, typename Alloc = tbb::cache_aligned_allocator<iterator_value_t<It>>,
1049d86ed7fbStbbdev           typename = std::enable_if_t<is_input_iterator_v<It>>,
1050d86ed7fbStbbdev           typename = std::enable_if_t<is_allocator_v<Alloc>>>
1051d86ed7fbStbbdev concurrent_vector( It, It, Alloc = Alloc() )
1052d86ed7fbStbbdev -> concurrent_vector<iterator_value_t<It>, Alloc>;
105349e08aacStbbdev #endif
105449e08aacStbbdev 
105549e08aacStbbdev template <typename T, typename Allocator>
swap(concurrent_vector<T,Allocator> & lhs,concurrent_vector<T,Allocator> & rhs)105649e08aacStbbdev void swap(concurrent_vector<T, Allocator> &lhs,
105749e08aacStbbdev           concurrent_vector<T, Allocator> &rhs)
105849e08aacStbbdev {
105949e08aacStbbdev     lhs.swap(rhs);
106049e08aacStbbdev }
106149e08aacStbbdev 
106249e08aacStbbdev template <typename T, typename Allocator>
106349e08aacStbbdev bool operator==(const concurrent_vector<T, Allocator> &lhs,
106449e08aacStbbdev                 const concurrent_vector<T, Allocator> &rhs)
106549e08aacStbbdev {
106649e08aacStbbdev     return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
106749e08aacStbbdev }
106849e08aacStbbdev 
1069b15aabb3Stbbdev #if !__TBB_CPP20_COMPARISONS_PRESENT
107049e08aacStbbdev template <typename T, typename Allocator>
107149e08aacStbbdev bool operator!=(const concurrent_vector<T, Allocator> &lhs,
107249e08aacStbbdev                 const concurrent_vector<T, Allocator> &rhs)
107349e08aacStbbdev {
107449e08aacStbbdev     return !(lhs == rhs);
107549e08aacStbbdev }
1076b15aabb3Stbbdev #endif // !__TBB_CPP20_COMPARISONS_PRESENT
1077b15aabb3Stbbdev 
1078b15aabb3Stbbdev #if __TBB_CPP20_COMPARISONS_PRESENT && __TBB_CPP20_CONCEPTS_PRESENT
1079b15aabb3Stbbdev template <typename T, typename Allocator>
1080b15aabb3Stbbdev tbb::detail::synthesized_three_way_result<typename concurrent_vector<T, Allocator>::value_type>
1081b15aabb3Stbbdev operator<=>(const concurrent_vector<T, Allocator> &lhs,
1082b15aabb3Stbbdev             const concurrent_vector<T, Allocator> &rhs)
1083b15aabb3Stbbdev {
1084b15aabb3Stbbdev     return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(),
1085b15aabb3Stbbdev                                                   rhs.begin(), rhs.end(),
1086b15aabb3Stbbdev                                                   tbb::detail::synthesized_three_way_comparator{});
1087b15aabb3Stbbdev }
1088b15aabb3Stbbdev 
1089b15aabb3Stbbdev #else
109049e08aacStbbdev 
109149e08aacStbbdev template <typename T, typename Allocator>
109249e08aacStbbdev bool operator<(const concurrent_vector<T, Allocator> &lhs,
109349e08aacStbbdev                const concurrent_vector<T, Allocator> &rhs)
109449e08aacStbbdev {
109549e08aacStbbdev     return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
109649e08aacStbbdev }
109749e08aacStbbdev 
109849e08aacStbbdev template <typename T, typename Allocator>
109949e08aacStbbdev bool operator<=(const concurrent_vector<T, Allocator> &lhs,
110049e08aacStbbdev                 const concurrent_vector<T, Allocator> &rhs)
110149e08aacStbbdev {
110249e08aacStbbdev     return !(rhs < lhs);
110349e08aacStbbdev }
110449e08aacStbbdev 
110549e08aacStbbdev template <typename T, typename Allocator>
110649e08aacStbbdev bool operator>(const concurrent_vector<T, Allocator> &lhs,
110749e08aacStbbdev                const concurrent_vector<T, Allocator> &rhs)
110849e08aacStbbdev {
110949e08aacStbbdev     return rhs < lhs;
111049e08aacStbbdev }
111149e08aacStbbdev 
111249e08aacStbbdev template <typename T, typename Allocator>
111349e08aacStbbdev bool operator>=(const concurrent_vector<T, Allocator> &lhs,
111449e08aacStbbdev                 const concurrent_vector<T, Allocator> &rhs)
111549e08aacStbbdev {
111649e08aacStbbdev     return !(lhs < rhs);
111749e08aacStbbdev }
1118b15aabb3Stbbdev #endif // __TBB_CPP20_COMPARISONS_PRESENT && __TBB_CPP20_CONCEPTS_PRESENT
111949e08aacStbbdev 
112049e08aacStbbdev } // namespace d1
112149e08aacStbbdev } // namespace detail
112249e08aacStbbdev 
112349e08aacStbbdev inline namespace v1 {
112449e08aacStbbdev     using detail::d1::concurrent_vector;
112549e08aacStbbdev } // namespace v1
112649e08aacStbbdev 
112749e08aacStbbdev } // namespace tbb
112849e08aacStbbdev 
112949e08aacStbbdev #endif // __TBB_concurrent_vector_H
1130