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