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