1 /*
2     Copyright (c) 2005-2020 Intel Corporation
3 
4     Licensed under the Apache License, Version 2.0 (the "License");
5     you may not use this file except in compliance with the License.
6     You may obtain a copy of the License at
7 
8         http://www.apache.org/licenses/LICENSE-2.0
9 
10     Unless required by applicable law or agreed to in writing, software
11     distributed under the License is distributed on an "AS IS" BASIS,
12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13     See the License for the specific language governing permissions and
14     limitations under the License.
15 */
16 
17 #ifndef __TBB_concurrent_vector_H
18 #define __TBB_concurrent_vector_H
19 
20 #include "detail/_namespace_injection.h"
21 #include "detail/_utils.h"
22 #include "detail/_assert.h"
23 #include "detail/_allocator_traits.h"
24 #include "detail/_segment_table.h"
25 #include "detail/_containers_helpers.h"
26 #include "blocked_range.h"
27 #include "cache_aligned_allocator.h"
28 
29 #include <algorithm>
30 #include <utility> // std::move_if_noexcept
31 #include <algorithm>
32 
33 namespace tbb {
34 namespace detail {
35 namespace d1 {
36 
37 template <typename Vector, typename Value>
38 class vector_iterator {
39     using vector_type = Vector;
40 
41 public:
42     using value_type = Value;
43     using size_type = typename vector_type::size_type;
44     using difference_type = typename vector_type::difference_type;
45     using pointer = value_type*;
46     using reference = value_type&;
47     using iterator_category = std::random_access_iterator_tag;
48 
49     template <typename Vec, typename Val>
50     friend vector_iterator<Vec, Val> operator+( typename vector_iterator<Vec, Val>::difference_type, const vector_iterator<Vec, Val>& );
51 
52     template <typename Vec, typename Val1, typename Val2>
53     friend typename vector_iterator<Vec, Val1>::difference_type operator-( const vector_iterator<Vec, Val1>&, const vector_iterator<Vec, Val2>& );
54 
55     template <typename Vec, typename Val1, typename Val2>
56     friend bool operator==( const vector_iterator<Vec, Val1>&, const vector_iterator<Vec, Val2>& );
57 
58     template <typename Vec, typename Val1, typename Val2>
59     friend bool operator<( const vector_iterator<Vec, Val1>&, const vector_iterator<Vec, Val2>& );
60 
61     template <typename Vec, typename Val>
62     friend class vector_iterator;
63 
64     template <typename T, typename Allocator>
65     friend class concurrent_vector;
66 
67 private:
68     vector_iterator( const vector_type& vector, size_type index, value_type* item = nullptr )
69         : my_vector(const_cast<vector_type*>(&vector)), my_index(index), my_item(item)
70     {}
71 
72 public:
73     vector_iterator() : my_vector(nullptr), my_index(~size_type(0)), my_item(nullptr)
74     {}
75 
76     vector_iterator( const vector_iterator<vector_type, typename vector_type::value_type>& other )
77         : my_vector(other.my_vector), my_index(other.my_index), my_item(other.my_item)
78     {}
79 
80     vector_iterator& operator=( const vector_iterator<vector_type, typename vector_type::value_type>& other ) {
81         my_vector = other.my_vector;
82         my_index = other.my_index;
83         my_item = other.my_item;
84         return *this;
85     }
86 
87     vector_iterator operator+( difference_type offset ) const {
88         return vector_iterator(*my_vector, my_index + offset);
89     }
90 
91     vector_iterator& operator+=( difference_type offset ) {
92         my_index += offset;
93         my_item = nullptr;
94         return *this;
95     }
96 
97     vector_iterator operator-( difference_type offset ) const {
98         return vector_iterator(*my_vector, my_index - offset);
99     }
100 
101     vector_iterator& operator-=( difference_type offset ) {
102         my_index -= offset;
103         my_item = nullptr;
104         return *this;
105     }
106 
107     reference operator*() const {
108         value_type *item = my_item;
109         if (item == nullptr) {
110             item = &my_vector->internal_subscript(my_index);
111         } else {
112             __TBB_ASSERT(item == &my_vector->internal_subscript(my_index), "corrupt cache");
113         }
114         return *item;
115     }
116 
117     pointer operator->() const { return &(operator*()); }
118 
119     reference operator[]( difference_type k ) const {
120         return my_vector->internal_subscript(my_index + k);
121     }
122 
123     vector_iterator& operator++() {
124         ++my_index;
125         if (my_item != nullptr) {
126             if (vector_type::is_first_element_in_segment(my_index)) {
127                 // If the iterator crosses a segment boundary, the pointer become invalid
128                 // as possibly next segment is in another memory location
129                 my_item = nullptr;
130             } else {
131                 ++my_item;
132             }
133         }
134         return *this;
135     }
136 
137     vector_iterator operator++(int) {
138         vector_iterator result = *this;
139         ++(*this);
140         return result;
141     }
142 
143     vector_iterator& operator--() {
144         __TBB_ASSERT(my_index > 0, "operator--() applied to iterator already at beginning of concurrent_vector");
145         --my_index;
146         if (my_item != nullptr) {
147             if (vector_type::is_first_element_in_segment(my_index)) {
148                 // If the iterator crosses a segment boundary, the pointer become invalid
149                 // as possibly next segment is in another memory location
150                 my_item = nullptr;
151             } else {
152                 --my_item;
153             }
154         }
155         return *this;
156     }
157 
158     vector_iterator operator--(int) {
159         vector_iterator result = *this;
160         --(*this);
161         return result;
162     }
163 
164 private:
165     // concurrent_vector over which we are iterating.
166     vector_type* my_vector;
167 
168     // Index into the vector
169     size_type my_index;
170 
171     // Caches my_vector *it;
172     // If my_item == nullptr cached value is not available use internal_subscript(my_index)
173     mutable value_type* my_item;
174 }; // class vector_iterator
175 
176 template <typename Vector, typename T>
177 vector_iterator<Vector, T> operator+( typename vector_iterator<Vector, T>::difference_type offset,
178                                       const vector_iterator<Vector, T>& v )
179 {
180     return vector_iterator<Vector, T>(*v.my_vector, v.my_index + offset);
181 }
182 
183 template <typename Vector, typename T, typename U>
184 typename vector_iterator<Vector, T>::difference_type operator-( const vector_iterator<Vector, T>& i,
185                                                                 const vector_iterator<Vector, U>& j )
186 {
187     using difference_type = typename vector_iterator<Vector, T>::difference_type;
188     return static_cast<difference_type>(i.my_index) - static_cast<difference_type>(j.my_index);
189 }
190 
191 template <typename Vector, typename T, typename U>
192 bool operator==( const vector_iterator<Vector, T>& i, const vector_iterator<Vector, U>& j ) {
193     return i.my_vector == j.my_vector && i.my_index == j.my_index;
194 }
195 
196 template <typename Vector, typename T, typename U>
197 bool operator!=( const vector_iterator<Vector, T>& i, const vector_iterator<Vector, U>& j ) {
198     return !(i == j);
199 }
200 
201 template <typename Vector, typename T, typename U>
202 bool operator<( const vector_iterator<Vector, T>& i, const vector_iterator<Vector, U>& j ) {
203     return i.my_index < j.my_index;
204 }
205 
206 template <typename Vector, typename T, typename U>
207 bool operator>( const vector_iterator<Vector, T>& i, const vector_iterator<Vector, U>& j ) {
208     return j < i;
209 }
210 
211 template <typename Vector, typename T, typename U>
212 bool operator>=( const vector_iterator<Vector, T>& i, const vector_iterator<Vector, U>& j ) {
213     return !(i < j);
214 }
215 
216 template <typename Vector, typename T, typename U>
217 bool operator<=( const vector_iterator<Vector, T>& i, const vector_iterator<Vector, U>& j ) {
218     return !(j < i);
219 }
220 
221 static constexpr std::size_t embedded_table_num_segments = 3;
222 
223 template <typename T, typename Allocator = tbb::cache_aligned_allocator<T>>
224 class concurrent_vector
225     : private segment_table<T, Allocator, concurrent_vector<T, Allocator>, embedded_table_num_segments>
226 {
227     using self_type = concurrent_vector<T, Allocator>;
228     using base_type = segment_table<T, Allocator, self_type, embedded_table_num_segments>;
229 
230     friend class segment_table<T, Allocator, self_type, embedded_table_num_segments>;
231 
232     template <typename Iterator>
233     class generic_range_type : public tbb::blocked_range<Iterator> {
234         using base_type = tbb::blocked_range<Iterator>;
235     public:
236         using value_type = T;
237         using reference = T&;
238         using const_reference = const T&;
239         using iterator = Iterator;
240         using difference_type = std::ptrdiff_t;
241 
242         using base_type::base_type;
243 
244         template<typename U>
245         generic_range_type( const generic_range_type<U>& r) : blocked_range<Iterator>(r.begin(), r.end(), r.grainsize()) {}
246         generic_range_type( generic_range_type& r, split ) : blocked_range<Iterator>(r, split()) {}
247     }; // class generic_range_type
248 
249     static_assert(std::is_same<T, typename Allocator::value_type>::value,
250                   "value_type of the container must be the same as its allocator's");
251     using allocator_traits_type = tbb::detail::allocator_traits<Allocator>;
252     // Segment table for concurrent_vector can be extended
253     static constexpr bool allow_table_extending = true;
254     static constexpr bool is_noexcept_assignment = allocator_traits_type::propagate_on_container_move_assignment::value ||
255                                                    allocator_traits_type::is_always_equal::value;
256     static constexpr bool is_noexcept_swap = allocator_traits_type::propagate_on_container_swap::value ||
257                                              allocator_traits_type::is_always_equal::value;
258 
259 public:
260     using value_type = T;
261     using allocator_type = Allocator;
262     using size_type = std::size_t;
263     using difference_type = std::ptrdiff_t;
264     using reference = value_type&;
265     using const_reference = const value_type&;
266 
267     using pointer = typename allocator_traits_type::pointer;
268     using const_pointer = typename allocator_traits_type::const_pointer;
269 
270     using iterator = vector_iterator<concurrent_vector, value_type>;
271     using const_iterator = vector_iterator<concurrent_vector, const value_type>;
272     using reverse_iterator = std::reverse_iterator<iterator>;
273     using const_reverse_iterator = std::reverse_iterator<const_iterator>;
274 
275     using range_type = generic_range_type<iterator>;
276     using const_range_type = generic_range_type<const_iterator>;
277 
278     concurrent_vector() : concurrent_vector(allocator_type()) {}
279 
280     explicit concurrent_vector( const allocator_type& alloc ) noexcept
281         : base_type(alloc)
282     {}
283 
284     explicit concurrent_vector( size_type count, const value_type& value,
285                                 const allocator_type& alloc = allocator_type() )
286         : concurrent_vector(alloc)
287     {
288         try_call( [&] {
289             grow_by(count, value);
290         } ).on_exception( [&] {
291             base_type::clear();
292         });
293     }
294 
295     explicit concurrent_vector( size_type count, const allocator_type& alloc = allocator_type() )
296         : concurrent_vector(alloc)
297     {
298         try_call( [&] {
299             grow_by(count);
300         } ).on_exception( [&] {
301             base_type::clear();
302         });
303     }
304 
305     template <typename InputIterator>
306     concurrent_vector( InputIterator first, InputIterator last, const allocator_type& alloc = allocator_type() )
307         : concurrent_vector(alloc)
308     {
309         try_call( [&] {
310             grow_by(first, last);
311         } ).on_exception( [&] {
312             base_type::clear();
313         });
314     }
315 
316     concurrent_vector( const concurrent_vector& other )
317         : base_type(segment_table_allocator_traits::select_on_container_copy_construction(other.get_allocator()))
318     {
319         try_call( [&] {
320             grow_by(other.begin(), other.end());
321         } ).on_exception( [&] {
322             base_type::clear();
323         });
324     }
325 
326     concurrent_vector( const concurrent_vector& other, const allocator_type& alloc )
327         : base_type(other, alloc) {}
328 
329     concurrent_vector(concurrent_vector&& other) noexcept
330         : base_type(std::move(other))
331     {}
332 
333     concurrent_vector( concurrent_vector&& other, const allocator_type& alloc )
334         : base_type(std::move(other), alloc)
335     {}
336 
337     concurrent_vector( std::initializer_list<value_type> init,
338                        const allocator_type& alloc = allocator_type() )
339         : concurrent_vector(init.begin(), init.end(), alloc)
340     {}
341 
342     ~concurrent_vector() {}
343 
344     // Assignment
345     concurrent_vector& operator=( const concurrent_vector& other ) {
346         base_type::operator=(other);
347         return *this;
348     }
349 
350     concurrent_vector& operator=( concurrent_vector&& other ) noexcept(is_noexcept_assignment) {
351         base_type::operator=(std::move(other));
352         return *this;
353     }
354 
355     concurrent_vector& operator=( std::initializer_list<value_type> init ) {
356         assign(init);
357         return *this;
358     }
359 
360     void assign( size_type count, const value_type& value ) {
361         destroy_elements();
362         grow_by(count, value);
363     }
364 
365     template <typename InputIterator>
366     typename std::enable_if<is_input_iterator<InputIterator>::value, void>::type
367     assign( InputIterator first, InputIterator last ) {
368         destroy_elements();
369         grow_by(first, last);
370     }
371 
372     void assign( std::initializer_list<value_type> init ) {
373         destroy_elements();
374         assign(init.begin(), init.end());
375     }
376 
377     // Concurrent growth
378     iterator grow_by( size_type delta ) {
379         return internal_grow_by_delta(delta);
380     }
381 
382     iterator grow_by( size_type delta, const value_type& value ) {
383         return internal_grow_by_delta(delta, value);
384     }
385 
386     template <typename ForwardIterator>
387     typename std::enable_if<is_input_iterator<ForwardIterator>::value, iterator>::type
388     grow_by( ForwardIterator first, ForwardIterator last ) {
389         auto delta = std::distance(first, last);
390         return internal_grow_by_delta(delta, first, last);
391     }
392 
393     iterator grow_by( std::initializer_list<value_type> init ) {
394         return grow_by(init.begin(), init.end());
395     }
396 
397     iterator grow_to_at_least( size_type n ) {
398         return internal_grow_to_at_least(n);
399     }
400     iterator grow_to_at_least( size_type n, const value_type& value ) {
401         return internal_grow_to_at_least(n, value);
402     }
403 
404     iterator push_back( const value_type& item ) {
405         return internal_emplace_back(item);
406     }
407 
408     iterator push_back( value_type&& item ) {
409         return internal_emplace_back(std::move(item));
410     }
411 
412     template <typename... Args>
413     iterator emplace_back( Args&&... args ) {
414         return internal_emplace_back(std::forward<Args>(args)...);
415     }
416 
417     // Items access
418     reference operator[]( size_type index ) {
419         return internal_subscript(index);
420     }
421     const_reference operator[]( size_type index ) const {
422         return internal_subscript(index);
423     }
424 
425     reference at( size_type index ) {
426         return internal_subscript_with_exceptions(index);
427     }
428     const_reference at( size_type index ) const {
429         return internal_subscript_with_exceptions(index);
430     }
431 
432     // Get range for iterating with parallel algorithms
433     range_type range( size_t grainsize = 1 ) {
434         return range_type(begin(), end(), grainsize);
435     }
436 
437     // Get const range for iterating with parallel algorithms
438     const_range_type range( size_t grainsize = 1 ) const {
439         return const_range_type(begin(), end(), grainsize);
440     }
441 
442     reference front() {
443         return internal_subscript(0);
444     }
445 
446     const_reference front() const {
447         return internal_subscript(0);
448     }
449 
450     reference back() {
451         return internal_subscript(size() - 1);
452     }
453 
454     const_reference back() const {
455         return internal_subscript(size() - 1);
456     }
457 
458     // Iterators
459     iterator begin() { return iterator(*this, 0); }
460     const_iterator begin() const { return const_iterator(*this, 0); }
461     const_iterator cbegin() const { return const_iterator(*this, 0); }
462 
463     iterator end() { return iterator(*this, size()); }
464     const_iterator end() const { return const_iterator(*this, size()); }
465     const_iterator cend() const { return const_iterator(*this, size()); }
466 
467     reverse_iterator rbegin() { return reverse_iterator(end()); }
468     const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
469     const_reverse_iterator crbegin() const { return const_reverse_iterator(cend()); }
470 
471     reverse_iterator rend() { return reverse_iterator(begin()); }
472     const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }
473     const_reverse_iterator crend() const { return const_reverse_iterator(cbegin()); }
474 
475     allocator_type get_allocator() const {
476         return base_type::get_allocator();
477     }
478 
479     // Storage
480     bool empty() const noexcept {
481         return 0 == size();
482     }
483 
484     size_type size() const noexcept {
485         return std::min(this->my_size.load(std::memory_order_acquire), capacity());
486     }
487 
488     size_type max_size() const noexcept {
489         return allocator_traits_type::max_size(base_type::get_allocator());
490     }
491 
492     size_type capacity() const noexcept {
493         return base_type::capacity();
494     }
495 
496     void reserve( size_type n ) {
497         if (n == 0) return;
498 
499         if (n > max_size()) {
500             tbb::detail::throw_exception(exception_id::reservation_length_error);
501         }
502 
503         this->assign_first_block_if_necessary(this->segment_index_of(n - 1) + 1);
504         base_type::reserve(n);
505     }
506 
507     void resize( size_type n ) {
508         internal_resize(n);
509     }
510 
511     void resize( size_type n, const value_type& val ) {
512         internal_resize(n, val);
513     }
514 
515     void shrink_to_fit() {
516         internal_compact();
517     }
518 
519     void swap(concurrent_vector& other) noexcept(is_noexcept_swap) {
520         base_type::swap(other);
521     }
522 
523     void clear() {
524         destroy_elements();
525     }
526 
527 private:
528     using segment_type = typename base_type::segment_type;
529     using segment_table_type = typename base_type::segment_table_type;
530     using segment_table_allocator_traits = typename base_type::segment_table_allocator_traits;
531     using segment_index_type = typename base_type::segment_index_type;
532 
533     using segment_element_type = typename base_type::value_type;
534     using segment_element_allocator_type = typename allocator_traits_type::template rebind_alloc<segment_element_type>;
535     using segment_element_allocator_traits = tbb::detail::allocator_traits<segment_element_allocator_type>;
536 
537     segment_table_type allocate_long_table( const typename base_type::atomic_segment* embedded_table, size_type start_index ) {
538         __TBB_ASSERT(start_index <= this->embedded_table_size, "Start index out of embedded table");
539 
540         // If other threads are trying to set pointers in the short segment, wait for them to finish their
541         // assignments before we copy the short segment to the long segment. Note: grow_to_at_least depends on it
542         for (segment_index_type i = 0; this->segment_base(i) < start_index; ++i) {
543             spin_wait_while_eq(embedded_table[i], segment_type(nullptr));
544         }
545 
546         // It is possible that the table was extend by a thread allocating first_block, need to check this.
547         if (this->get_table() != embedded_table) {
548             return nullptr;
549         }
550 
551         // Allocate long segment table and fill with null pointers
552         segment_table_type new_segment_table = segment_table_allocator_traits::allocate(base_type::get_allocator(), this->pointers_per_long_table);
553         // Copy segment pointers from the embedded table
554         for (size_type segment_index = 0; segment_index < this->pointers_per_embedded_table; ++segment_index) {
555             segment_table_allocator_traits::construct(base_type::get_allocator(), &new_segment_table[segment_index],
556                 embedded_table[segment_index].load(std::memory_order_relaxed));
557         }
558         for (size_type segment_index = this->pointers_per_embedded_table; segment_index < this->pointers_per_long_table; ++segment_index) {
559             segment_table_allocator_traits::construct(base_type::get_allocator(), &new_segment_table[segment_index], nullptr);
560         }
561 
562         return new_segment_table;
563     }
564 
565     // create_segment function is required by the segment_table base class
566     segment_type create_segment( segment_table_type table, segment_index_type seg_index, size_type index ) {
567         size_type first_block = this->my_first_block.load(std::memory_order_relaxed);
568         // First block allocation
569         if (seg_index < first_block) {
570             // If 0 segment is already allocated, then it remains to wait until the segments are filled to requested
571             if (table[0].load(std::memory_order_acquire) != nullptr) {
572                 spin_wait_while_eq(table[seg_index], segment_type(nullptr));
573                 return nullptr;
574             }
575 
576             segment_element_allocator_type segment_allocator(base_type::get_allocator());
577             segment_type new_segment = nullptr;
578             size_type first_block_size = this->segment_size(first_block);
579             try_call( [&] {
580                 new_segment = segment_element_allocator_traits::allocate(segment_allocator, first_block_size);
581             } ).on_exception( [&] {
582                 segment_type disabled_segment = nullptr;
583                 if (table[0].compare_exchange_strong(disabled_segment, this->segment_allocation_failure_tag)) {
584                     size_type end_segment = table == this->my_embedded_table ? this->pointers_per_embedded_table : first_block;
585                     for (size_type i = 1; i < end_segment; ++i) {
586                         table[i].store(this->segment_allocation_failure_tag, std::memory_order_release);
587                     }
588                 }
589             });
590 
591             segment_type disabled_segment = nullptr;
592             if (table[0].compare_exchange_strong(disabled_segment, new_segment)) {
593                 this->extend_table_if_necessary(table, 0, first_block_size);
594                 for (size_type i = 1; i < first_block; ++i) {
595                     table[i].store(new_segment, std::memory_order_release);
596                 }
597 
598                 // Other threads can wait on a snapshot of an embedded table, need to fill it.
599                 for (size_type i = 1; i < first_block && i < this->pointers_per_embedded_table; ++i) {
600                     this->my_embedded_table[i].store(new_segment, std::memory_order_release);
601                 }
602             } else if (new_segment != this->segment_allocation_failure_tag) {
603                 // Deallocate the memory
604                 segment_element_allocator_traits::deallocate(segment_allocator, new_segment, first_block_size);
605                 // 0 segment is already allocated, then it remains to wait until the segments are filled to requested
606                 spin_wait_while_eq(table[seg_index], segment_type(nullptr));
607             }
608         } else {
609             size_type offset = this->segment_base(seg_index);
610             if (index == offset) {
611                 __TBB_ASSERT(table[seg_index].load(std::memory_order_relaxed) == nullptr, "Only this thread can enable this segment");
612                 segment_element_allocator_type segment_allocator(base_type::get_allocator());
613                 segment_type new_segment = this->segment_allocation_failure_tag;
614                 try_call( [&] {
615                     new_segment = segment_element_allocator_traits::allocate(segment_allocator,this->segment_size(seg_index));
616                     // Shift base address to simplify access by index
617                     new_segment -= this->segment_base(seg_index);
618                 } ).on_completion( [&] {
619                     table[seg_index].store(new_segment, std::memory_order_release);
620                 });
621             } else {
622                 spin_wait_while_eq(table[seg_index], segment_type(nullptr));
623             }
624         }
625         return nullptr;
626     }
627 
628     // Returns the number of elements in the segment to be destroy
629     size_type number_of_elements_in_segment( segment_index_type seg_index ) {
630         size_type curr_vector_size = this->my_size.load(std::memory_order_relaxed);
631         size_type curr_segment_base = this->segment_base(seg_index);
632 
633         if (seg_index == 0) {
634             return std::min(curr_vector_size, this->segment_size(seg_index));
635         } else {
636             // Perhaps the segment is allocated, but there are no elements in it.
637             if (curr_vector_size < curr_segment_base) {
638                 return 0;
639             }
640             return curr_segment_base * 2 > curr_vector_size ? curr_vector_size - curr_segment_base : curr_segment_base;
641         }
642     }
643 
644     void deallocate_segment( segment_type address, segment_index_type seg_index ) {
645         segment_element_allocator_type segment_allocator(base_type::get_allocator());
646         size_type first_block = this->my_first_block.load(std::memory_order_relaxed);
647         if (seg_index >= first_block) {
648             segment_element_allocator_traits::deallocate(segment_allocator, address, this->segment_size(seg_index));
649         }
650         else if (seg_index == 0) {
651             size_type elements_to_deallocate = first_block > 0 ? this->segment_size(first_block) : this->segment_size(0);
652             segment_element_allocator_traits::deallocate(segment_allocator, address, elements_to_deallocate);
653         }
654     }
655 
656     // destroy_segment function is required by the segment_table base class
657     void destroy_segment( segment_type address, segment_index_type seg_index ) {
658         size_type elements_to_destroy = number_of_elements_in_segment(seg_index);
659         segment_element_allocator_type segment_allocator(base_type::get_allocator());
660 
661         for (size_type i = 0; i < elements_to_destroy; ++i) {
662             segment_element_allocator_traits::destroy(segment_allocator, address + i);
663         }
664 
665         deallocate_segment(address, seg_index);
666     }
667 
668     // copy_segment function is required by the segment_table base class
669     void copy_segment( segment_index_type seg_index, segment_type from, segment_type to ) {
670         size_type i = 0;
671         try_call( [&] {
672             for (; i != number_of_elements_in_segment(seg_index); ++i) {
673                 segment_table_allocator_traits::construct(base_type::get_allocator(), to + i, from[i]);
674             }
675         } ).on_exception( [&] {
676             // Zero-initialize items left not constructed after the exception
677             zero_unconstructed_elements(this->get_segment(seg_index) + i, this->segment_size(seg_index) - i);
678 
679             segment_index_type last_segment = this->segment_index_of(this->my_size.load(std::memory_order_relaxed));
680             auto table = this->get_table();
681             for (segment_index_type j = seg_index + 1; j != last_segment; ++j) {
682                 auto curr_segment = table[j].load(std::memory_order_relaxed);
683                 if (curr_segment) {
684                     zero_unconstructed_elements(curr_segment + this->segment_base(j), this->segment_size(j));
685                 }
686             }
687             this->my_size.store(this->segment_size(seg_index) + i, std::memory_order_relaxed);
688         });
689     }
690 
691     // move_segment function is required by the segment_table base class
692     void move_segment( segment_index_type seg_index, segment_type from, segment_type to ) {
693         size_type i = 0;
694         try_call( [&] {
695             for (; i != number_of_elements_in_segment(seg_index); ++i) {
696                 segment_table_allocator_traits::construct(base_type::get_allocator(), to + i, std::move(from[i]));
697             }
698         } ).on_exception( [&] {
699             // Zero-initialize items left not constructed after the exception
700             zero_unconstructed_elements(this->get_segment(seg_index) + i, this->segment_size(seg_index) - i);
701 
702             segment_index_type last_segment = this->segment_index_of(this->my_size.load(std::memory_order_relaxed));
703             auto table = this->get_table();
704             for (segment_index_type j = seg_index + 1; j != last_segment; ++j) {
705                 auto curr_segment = table[j].load(std::memory_order_relaxed);
706                 if (curr_segment) {
707                     zero_unconstructed_elements(curr_segment + this->segment_base(j), this->segment_size(j));
708                 }
709             }
710             this->my_size.store(this->segment_size(seg_index) + i, std::memory_order_relaxed);
711         });
712     }
713 
714     static constexpr bool is_first_element_in_segment( size_type index ) {
715         // An element is the first in a segment if its index is equal to a power of two
716         return is_power_of_two_at_least(index, 2);
717     }
718 
719     const_reference internal_subscript( size_type index ) const {
720         return const_cast<self_type*>(this)->internal_subscript(index);
721     }
722 
723     reference internal_subscript( size_type index ) {
724         __TBB_ASSERT(index < this->my_size.load(std::memory_order_relaxed), "Invalid subscript index");
725         return base_type::template internal_subscript</*allow_out_of_range_access=*/false>(index);
726     }
727 
728     const_reference internal_subscript_with_exceptions( size_type index ) const {
729         return const_cast<self_type*>(this)->internal_subscript_with_exceptions(index);
730     }
731 
732     reference internal_subscript_with_exceptions( size_type index ) {
733         if (index >= this->my_size.load(std::memory_order_acquire)) {
734             tbb::detail::throw_exception(exception_id::out_of_range);
735         }
736 
737         segment_table_type table = this->my_segment_table.load(std::memory_order_acquire);
738 
739         size_type seg_index = this->segment_index_of(index);
740         if (base_type::number_of_segments(table) < seg_index) {
741             tbb::detail::throw_exception(exception_id::out_of_range);
742         }
743 
744         if (table[seg_index] <= this->segment_allocation_failure_tag) {
745             tbb::detail::throw_exception(exception_id::out_of_range);
746         }
747 
748         return base_type::template internal_subscript</*allow_out_of_range_access=*/false>(index);
749     }
750 
751     static void zero_unconstructed_elements( pointer start, size_type count ) {
752         std::memset(static_cast<void *>(start), 0, count * sizeof(value_type));
753     }
754 
755     template <typename... Args>
756     iterator internal_emplace_back( Args&&... args ) {
757         size_type old_size = this->my_size++;
758         this->assign_first_block_if_necessary(default_first_block_size);
759         auto element_address = &base_type::template internal_subscript</*allow_out_of_range_access=*/true>(old_size);
760 
761         // try_call API is not convenient here due to broken
762         // variadic capture on GCC 4.8.5
763         auto value_guard = make_raii_guard([&] {
764             zero_unconstructed_elements(element_address, /*count =*/1);
765         });
766 
767         segment_table_allocator_traits::construct(base_type::get_allocator(), element_address, std::forward<Args>(args)...);
768         value_guard.dismiss();
769         return iterator(*this, old_size, element_address);
770     }
771 
772     template <typename... Args>
773     void internal_loop_construct( segment_table_type table, size_type start_idx, size_type end_idx, const Args&... args ) {
774         static_assert(sizeof...(Args) < 2, "Too many parameters");
775         for (size_type idx = start_idx; idx < end_idx; ++idx) {
776             auto element_address = &base_type::template internal_subscript</*allow_out_of_range_access=*/true>(idx);
777             // try_call API is not convenient here due to broken
778             // variadic capture on GCC 4.8.5
779             auto value_guard = make_raii_guard( [&] {
780                 segment_index_type last_allocated_segment = this->find_last_allocated_segment(table);
781                 size_type segment_size = this->segment_size(last_allocated_segment);
782                 end_idx = end_idx < segment_size ? end_idx : segment_size;
783                 for (size_type i = idx; i < end_idx; ++i) {
784                     zero_unconstructed_elements(&this->internal_subscript(i), /*count =*/1);
785                 }
786             });
787             segment_table_allocator_traits::construct(base_type::get_allocator(), element_address, args...);
788             value_guard.dismiss();
789         }
790     }
791 
792     template <typename ForwardIterator>
793     void internal_loop_construct( segment_table_type table, size_type start_idx, size_type end_idx, ForwardIterator first, ForwardIterator ) {
794         for (size_type idx = start_idx; idx < end_idx; ++idx) {
795             auto element_address = &base_type::template internal_subscript</*allow_out_of_range_access=*/true>(idx);
796             try_call( [&] {
797                 segment_table_allocator_traits::construct(base_type::get_allocator(), element_address, *first++);
798             } ).on_exception( [&] {
799                 segment_index_type last_allocated_segment = this->find_last_allocated_segment(table);
800                 size_type segment_size = this->segment_size(last_allocated_segment);
801                 end_idx = end_idx < segment_size ? end_idx : segment_size;
802                 for (size_type i = idx; i < end_idx; ++i) {
803                     zero_unconstructed_elements(&this->internal_subscript(i), /*count =*/1);
804                 }
805             });
806         }
807     }
808 
809     template <typename... Args>
810     iterator internal_grow( size_type start_idx, size_type end_idx, const Args&... args ) {
811         this->assign_first_block_if_necessary(this->segment_index_of(end_idx - 1) + 1);
812         size_type seg_index = this->segment_index_of(end_idx - 1);
813         segment_table_type table = this->get_table();
814         this->extend_table_if_necessary(table, start_idx, end_idx);
815 
816         if (seg_index > this->my_first_block.load(std::memory_order_relaxed)) {
817             // So that other threads be able to work with the last segment of grow_by, allocate it immediately.
818             // If the last segment is not less than the first block
819             if (table[seg_index].load(std::memory_order_relaxed) == nullptr) {
820                 size_type first_element = this->segment_base(seg_index);
821                 if (first_element >= start_idx && first_element < end_idx) {
822                     segment_type segment = table[seg_index].load(std::memory_order_relaxed);
823                     base_type::enable_segment(segment, table, seg_index, first_element);
824                 }
825             }
826         }
827 
828         internal_loop_construct(table, start_idx, end_idx, args...);
829 
830         return iterator(*this, start_idx, &base_type::template internal_subscript</*allow_out_of_range_access=*/false>(start_idx));
831     }
832 
833 
834     template <typename... Args>
835     iterator internal_grow_by_delta( size_type delta, const Args&... args ) {
836         if (delta == size_type(0)) {
837             return end();
838         }
839         size_type start_idx = this->my_size.fetch_add(delta);
840         size_type end_idx = start_idx + delta;
841 
842         return internal_grow(start_idx, end_idx, args...);
843     }
844 
845     template <typename... Args>
846     iterator internal_grow_to_at_least( size_type new_size, const Args&... args ) {
847         size_type old_size = this->my_size.load(std::memory_order_relaxed);
848         if (new_size == size_type(0)) return iterator(*this, 0);
849         while (old_size < new_size && !this->my_size.compare_exchange_weak(old_size, new_size))
850         {}
851 
852         int delta = static_cast<int>(new_size) - static_cast<int>(old_size);
853         if (delta > 0) {
854             return internal_grow(old_size, new_size, args...);
855         }
856 
857         size_type end_segment = this->segment_index_of(new_size - 1);
858 
859         // Check/wait for segments allocation completes
860         if (end_segment >= this->pointers_per_embedded_table &&
861             this->get_table() == this->my_embedded_table)
862         {
863             spin_wait_while_eq(this->my_segment_table, this->my_embedded_table);
864         }
865 
866         for (segment_index_type seg_idx = 0; seg_idx <= end_segment; ++seg_idx) {
867             if (this->get_table()[seg_idx].load(std::memory_order_relaxed) == nullptr) {
868                 atomic_backoff backoff(true);
869                 while (this->get_table()[seg_idx].load(std::memory_order_relaxed) == nullptr) {
870                     backoff.pause();
871                 }
872             }
873         }
874 
875     #if TBB_USE_DEBUG
876         size_type cap = capacity();
877         __TBB_ASSERT( cap >= new_size, NULL);
878     #endif
879         return iterator(*this, size());
880     }
881 
882     template <typename... Args>
883     void internal_resize( size_type n, const Args&... args ) {
884         if (n == 0) {
885             clear();
886             return;
887         }
888 
889         size_type old_size = this->my_size.load(std::memory_order_acquire);
890         if (n > old_size) {
891             reserve(n);
892             grow_to_at_least(n, args...);
893         } else {
894             if (old_size == n) {
895                 return;
896             }
897             size_type last_segment = this->segment_index_of(old_size - 1);
898             // Delete segments
899             for (size_type seg_idx = this->segment_index_of(n - 1) + 1; seg_idx <= last_segment; ++seg_idx) {
900                 this->delete_segment(seg_idx);
901             }
902 
903             // If n > segment_size(n) => we need to destroy all of the items in the first segment
904             // Otherwise, we need to destroy only items with the index < n
905             size_type n_segment = this->segment_index_of(n - 1);
906             size_type last_index_to_destroy = std::min(this->segment_base(n_segment) + this->segment_size(n_segment), old_size);
907             // Destroy elements in curr segment
908             for (size_type idx = n; idx < last_index_to_destroy; ++idx) {
909                 segment_table_allocator_traits::destroy(base_type::get_allocator(), &base_type::template internal_subscript</*allow_out_of_range_access=*/false>(idx));
910             }
911             this->my_size.store(n, std::memory_order_release);
912         }
913     }
914 
915     void destroy_elements() {
916         allocator_type alloc(base_type::get_allocator());
917         for (size_type i = 0; i < this->my_size.load(std::memory_order_relaxed); ++i) {
918             allocator_traits_type::destroy(alloc, &base_type::template internal_subscript</*allow_out_of_range_access=*/false>(i));
919         }
920         this->my_size.store(0, std::memory_order_relaxed);
921     }
922 
923     static bool incompact_predicate( size_type size ) {
924         // memory page size
925         const size_type page_size = 4096;
926         return size < page_size || ((size - 1) % page_size < page_size / 2 && size < page_size * 128);
927     }
928 
929     void internal_compact() {
930         const size_type curr_size = this->my_size.load(std::memory_order_relaxed);
931         segment_table_type table = this->get_table();
932         const segment_index_type k_end = this->find_last_allocated_segment(table);                   // allocated segments
933         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;..
934         const segment_index_type first_block = this->my_first_block;                                 // number of merged segments, getting values from atomics
935 
936         segment_index_type k = first_block;
937         if (k_stop < first_block) {
938             k = k_stop;
939         }
940         else {
941             while (k < k_stop && incompact_predicate(this->segment_size(k) * sizeof(value_type))) k++;
942         }
943 
944         if (k_stop == k_end && k == first_block) {
945             return;
946         }
947 
948         // First segment optimization
949         if (k != first_block && k) {
950             size_type max_block = std::max(first_block, k);
951 
952             auto buffer_table = segment_table_allocator_traits::allocate(base_type::get_allocator(), max_block);
953 
954             for (size_type seg_idx = 0; seg_idx < max_block; ++seg_idx) {
955                 segment_table_allocator_traits::construct(base_type::get_allocator(), &buffer_table[seg_idx],
956                     table[seg_idx].load(std::memory_order_relaxed));
957                 table[seg_idx].store(nullptr, std::memory_order_relaxed);
958             }
959 
960             this->my_first_block.store(k, std::memory_order_relaxed);
961             size_type index = 0;
962             try_call( [&] {
963                 for (; index < std::min(this->segment_size(max_block), curr_size); ++index) {
964                     auto element_address = &static_cast<base_type*>(this)->operator[](index);
965                     segment_index_type seg_idx = this->segment_index_of(index);
966                     segment_table_allocator_traits::construct(base_type::get_allocator(), element_address,
967                     std::move_if_noexcept(buffer_table[seg_idx].load(std::memory_order_relaxed)[index]));
968                 }
969             } ).on_exception( [&] {
970                 segment_element_allocator_type allocator(base_type::get_allocator());
971                 for (size_type i = 0; i < index; ++i) {
972                     auto element_adress = &this->operator[](i);
973                     segment_element_allocator_traits::destroy(allocator, element_adress);
974                 }
975                 segment_element_allocator_traits::deallocate(allocator,
976                     table[0].load(std::memory_order_relaxed), this->segment_size(max_block));
977 
978                 for (size_type seg_idx = 0; seg_idx < max_block; ++seg_idx) {
979                     table[seg_idx].store(buffer_table[seg_idx].load(std::memory_order_relaxed),
980                         std::memory_order_relaxed);
981                     buffer_table[seg_idx].store(nullptr, std::memory_order_relaxed);
982                 }
983                 segment_table_allocator_traits::deallocate(base_type::get_allocator(),
984                     buffer_table, max_block);
985                 this->my_first_block.store(first_block, std::memory_order_relaxed);
986             });
987 
988             // Need to correct deallocate old segments
989             // Method destroy_segment respect active first_block, therefore,
990             // in order for the segment deletion to work correctly, set the first_block size that was earlier,
991             // destroy the unnecessary segments.
992             this->my_first_block.store(first_block, std::memory_order_relaxed);
993             for (size_type seg_idx = max_block; seg_idx > 0 ; --seg_idx) {
994                 auto curr_segment = buffer_table[seg_idx - 1].load(std::memory_order_relaxed);
995                 if (curr_segment != nullptr) {
996                     destroy_segment(buffer_table[seg_idx - 1].load(std::memory_order_relaxed) + this->segment_base(seg_idx - 1),
997                         seg_idx - 1);
998                 }
999             }
1000 
1001             this->my_first_block.store(k, std::memory_order_relaxed);
1002 
1003             for (size_type seg_idx = 0; seg_idx < max_block; ++seg_idx) {
1004                 segment_table_allocator_traits::destroy(base_type::get_allocator(), &buffer_table[seg_idx]);
1005             }
1006 
1007             segment_table_allocator_traits::deallocate(base_type::get_allocator(), buffer_table, max_block);
1008         }
1009         // free unnecessary segments allocated by reserve() call
1010         if (k_stop < k_end) {
1011             for (size_type seg_idx = k_end; seg_idx != k_stop; --seg_idx) {
1012                 if (table[seg_idx - 1].load(std::memory_order_relaxed) != nullptr) {
1013                     this->delete_segment(seg_idx - 1);
1014                 }
1015             }
1016             if (!k) this->my_first_block.store(0, std::memory_order_relaxed);;
1017         }
1018     }
1019 
1020     // Lever for adjusting the size of first_block at the very first insertion.
1021     // TODO: consider >1 value, check performance
1022     static constexpr size_type default_first_block_size = 1;
1023 
1024     template <typename Vector, typename Value>
1025     friend class vector_iterator;
1026 }; // class concurrent_vector
1027 
1028 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
1029 // Deduction guide for the constructor from two iterators
1030 template <typename It, typename Alloc = tbb::cache_aligned_allocator<iterator_value_t<It>>,
1031           typename = std::enable_if_t<is_input_iterator_v<It>>,
1032           typename = std::enable_if_t<is_allocator_v<Alloc>>>
1033 concurrent_vector( It, It, Alloc = Alloc() )
1034 -> concurrent_vector<iterator_value_t<It>, Alloc>;
1035 #endif
1036 
1037 template <typename T, typename Allocator>
1038 void swap(concurrent_vector<T, Allocator> &lhs,
1039           concurrent_vector<T, Allocator> &rhs)
1040 {
1041     lhs.swap(rhs);
1042 }
1043 
1044 template <typename T, typename Allocator>
1045 bool operator==(const concurrent_vector<T, Allocator> &lhs,
1046                 const concurrent_vector<T, Allocator> &rhs)
1047 {
1048     return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
1049 }
1050 
1051 template <typename T, typename Allocator>
1052 bool operator!=(const concurrent_vector<T, Allocator> &lhs,
1053                 const concurrent_vector<T, Allocator> &rhs)
1054 {
1055     return !(lhs == rhs);
1056 }
1057 
1058 template <typename T, typename Allocator>
1059 bool operator<(const concurrent_vector<T, Allocator> &lhs,
1060                const concurrent_vector<T, Allocator> &rhs)
1061 {
1062     return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
1063 }
1064 
1065 template <typename T, typename Allocator>
1066 bool operator<=(const concurrent_vector<T, Allocator> &lhs,
1067                 const concurrent_vector<T, Allocator> &rhs)
1068 {
1069     return !(rhs < lhs);
1070 }
1071 
1072 template <typename T, typename Allocator>
1073 bool operator>(const concurrent_vector<T, Allocator> &lhs,
1074                const concurrent_vector<T, Allocator> &rhs)
1075 {
1076     return rhs < lhs;
1077 }
1078 
1079 template <typename T, typename Allocator>
1080 bool operator>=(const concurrent_vector<T, Allocator> &lhs,
1081                 const concurrent_vector<T, Allocator> &rhs)
1082 {
1083     return !(lhs < rhs);
1084 }
1085 
1086 } // namespace d1
1087 } // namespace detail
1088 
1089 inline namespace v1 {
1090     using detail::d1::concurrent_vector;
1091 } // namespace v1
1092 
1093 } // namespace tbb
1094 
1095 #endif // __TBB_concurrent_vector_H
1096