2 Copyright 2005-2011 Intel Corporation. All Rights Reserved.
4 This file is part of Threading Building Blocks.
6 Threading Building Blocks is free software; you can redistribute it
7 and/or modify it under the terms of the GNU General Public License
8 version 2 as published by the Free Software Foundation.
10 Threading Building Blocks is distributed in the hope that it will be
11 useful, but WITHOUT ANY WARRANTY; without even the implied warranty
12 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with Threading Building Blocks; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 As a special exception, you may use this file as part of a free software
20 library without restriction. Specifically, if other files instantiate
21 templates or use macros or inline functions from this file, or you compile
22 this file and link it with other files to produce an executable, this
23 file does not by itself cause the resulting executable to be covered by
24 the GNU General Public License. This exception does not however
25 invalidate any other reasons why the executable file might be covered by
26 the GNU General Public License.
29 #ifndef __TBB_concurrent_vector_H
30 #define __TBB_concurrent_vector_H
32 #include "tbb_stddef.h"
33 #include "tbb_exception.h"
35 #include "cache_aligned_allocator.h"
36 #include "blocked_range.h"
37 #include "tbb_machine.h"
38 #include "tbb_profiling.h"
40 #include <cstring> // for memset()
42 #if !TBB_USE_EXCEPTIONS && _MSC_VER
43 // Suppress "C++ exception handler used, but unwind semantics are not enabled" warning in STL headers
44 #pragma warning (push)
45 #pragma warning (disable: 4530)
51 #if !TBB_USE_EXCEPTIONS && _MSC_VER
55 #if _MSC_VER==1500 && !__INTEL_COMPILER
56 // VS2008/VC9 seems to have an issue; limits pull in math.h
57 #pragma warning( push )
58 #pragma warning( disable: 4985 )
60 #include <limits> /* std::numeric_limits */
61 #if _MSC_VER==1500 && !__INTEL_COMPILER
62 #pragma warning( pop )
65 #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) && defined(_Wp64)
66 // Workaround for overzealous compiler warnings in /Wp64 mode
67 #pragma warning (push)
68 #pragma warning (disable: 4267)
73 template<typename T, class A = cache_aligned_allocator<T> >
74 class concurrent_vector;
79 //! Bad allocation marker
80 static void *const vector_allocation_error_flag = reinterpret_cast<void*>(size_t(63));
82 //! Base class of concurrent vector implementation.
83 /** @ingroup containers */
84 class concurrent_vector_base_v3 {
87 // Basic types declarations
88 typedef size_t segment_index_t;
89 typedef size_t size_type;
91 // Using enumerations due to Mac linking problems of static const variables
94 default_initial_segments = 1, // 2 initial items
95 //! Number of slots for segment's pointers inside the class
96 pointers_per_short_table = 3, // to fit into 8 words of entire structure
97 pointers_per_long_table = sizeof(segment_index_t) * 8 // one segment per bit
100 // Segment pointer. Can be zero-initialized
105 __TBB_ASSERT( array <= internal::vector_allocation_error_flag, "should have been freed by clear" );
107 #endif /* TBB_USE_ASSERT */
112 //! allocator function pointer
113 void* (*vector_allocator_ptr)(concurrent_vector_base_v3 &, size_t);
115 //! count of segments in the first block
116 atomic<size_type> my_first_block;
118 //! Requested size of vector
119 atomic<size_type> my_early_size;
121 //! Pointer to the segments table
122 atomic<segment_t*> my_segment;
124 //! embedded storage of segment pointers
125 segment_t my_storage[pointers_per_short_table];
129 concurrent_vector_base_v3() {
131 my_first_block = 0; // here is not default_initial_segments
132 for( segment_index_t i = 0; i < pointers_per_short_table; i++)
133 my_storage[i].array = NULL;
134 my_segment = my_storage;
136 __TBB_EXPORTED_METHOD ~concurrent_vector_base_v3();
138 static segment_index_t segment_index_of( size_type index ) {
139 return segment_index_t( __TBB_Log2( index|1 ) );
142 static segment_index_t segment_base( segment_index_t k ) {
143 return (segment_index_t(1)<<k & ~segment_index_t(1));
146 static inline segment_index_t segment_base_index_of( segment_index_t &index ) {
147 segment_index_t k = segment_index_of( index );
148 index -= segment_base(k);
152 static size_type segment_size( segment_index_t k ) {
153 return segment_index_t(1)<<k; // fake value for k==0
156 //! An operation on an n-element array starting at begin.
157 typedef void (__TBB_EXPORTED_FUNC *internal_array_op1)(void* begin, size_type n );
159 //! An operation on n-element destination array and n-element source array.
160 typedef void (__TBB_EXPORTED_FUNC *internal_array_op2)(void* dst, const void* src, size_type n );
162 //! Internal structure for compact()
163 struct internal_segments_table {
164 segment_index_t first_block;
165 void* table[pointers_per_long_table];
168 void __TBB_EXPORTED_METHOD internal_reserve( size_type n, size_type element_size, size_type max_size );
169 size_type __TBB_EXPORTED_METHOD internal_capacity() const;
170 void internal_grow( size_type start, size_type finish, size_type element_size, internal_array_op2 init, const void *src );
171 size_type __TBB_EXPORTED_METHOD internal_grow_by( size_type delta, size_type element_size, internal_array_op2 init, const void *src );
172 void* __TBB_EXPORTED_METHOD internal_push_back( size_type element_size, size_type& index );
173 segment_index_t __TBB_EXPORTED_METHOD internal_clear( internal_array_op1 destroy );
174 void* __TBB_EXPORTED_METHOD internal_compact( size_type element_size, void *table, internal_array_op1 destroy, internal_array_op2 copy );
175 void __TBB_EXPORTED_METHOD internal_copy( const concurrent_vector_base_v3& src, size_type element_size, internal_array_op2 copy );
176 void __TBB_EXPORTED_METHOD internal_assign( const concurrent_vector_base_v3& src, size_type element_size,
177 internal_array_op1 destroy, internal_array_op2 assign, internal_array_op2 copy );
179 void __TBB_EXPORTED_METHOD internal_throw_exception(size_type) const;
180 void __TBB_EXPORTED_METHOD internal_swap(concurrent_vector_base_v3& v);
182 void __TBB_EXPORTED_METHOD internal_resize( size_type n, size_type element_size, size_type max_size, const void *src,
183 internal_array_op1 destroy, internal_array_op2 init );
184 size_type __TBB_EXPORTED_METHOD internal_grow_to_at_least_with_result( size_type new_size, size_type element_size, internal_array_op2 init, const void *src );
186 //! Deprecated entry point for backwards compatibility to TBB 2.1.
187 void __TBB_EXPORTED_METHOD internal_grow_to_at_least( size_type new_size, size_type element_size, internal_array_op2 init, const void *src );
189 //! Private functionality
194 typedef concurrent_vector_base_v3 concurrent_vector_base;
196 //! Meets requirements of a forward iterator for STL and a Value for a blocked_range.*/
197 /** Value is either the T or const T type of the container.
198 @ingroup containers */
199 template<typename Container, typename Value>
200 class vector_iterator
202 //! concurrent_vector over which we are iterating.
203 Container* my_vector;
205 //! Index into the vector
208 //! Caches my_vector->internal_subscript(my_index)
209 /** NULL if cached value is not available */
210 mutable Value* my_item;
212 template<typename C, typename T>
213 friend vector_iterator<C,T> operator+( ptrdiff_t offset, const vector_iterator<C,T>& v );
215 template<typename C, typename T, typename U>
216 friend bool operator==( const vector_iterator<C,T>& i, const vector_iterator<C,U>& j );
218 template<typename C, typename T, typename U>
219 friend bool operator<( const vector_iterator<C,T>& i, const vector_iterator<C,U>& j );
221 template<typename C, typename T, typename U>
222 friend ptrdiff_t operator-( const vector_iterator<C,T>& i, const vector_iterator<C,U>& j );
224 template<typename C, typename U>
225 friend class internal::vector_iterator;
227 #if !defined(_MSC_VER) || defined(__INTEL_COMPILER)
228 template<typename T, class A>
229 friend class tbb::concurrent_vector;
231 public: // workaround for MSVC
234 vector_iterator( const Container& vector, size_t index, void *ptr = 0 ) :
235 my_vector(const_cast<Container*>(&vector)),
237 my_item(static_cast<Value*>(ptr))
241 //! Default constructor
242 vector_iterator() : my_vector(NULL), my_index(~size_t(0)), my_item(NULL) {}
244 vector_iterator( const vector_iterator<Container,typename Container::value_type>& other ) :
245 my_vector(other.my_vector),
246 my_index(other.my_index),
247 my_item(other.my_item)
250 vector_iterator operator+( ptrdiff_t offset ) const {
251 return vector_iterator( *my_vector, my_index+offset );
253 vector_iterator &operator+=( ptrdiff_t offset ) {
258 vector_iterator operator-( ptrdiff_t offset ) const {
259 return vector_iterator( *my_vector, my_index-offset );
261 vector_iterator &operator-=( ptrdiff_t offset ) {
266 Value& operator*() const {
267 Value* item = my_item;
269 item = my_item = &my_vector->internal_subscript(my_index);
271 __TBB_ASSERT( item==&my_vector->internal_subscript(my_index), "corrupt cache" );
274 Value& operator[]( ptrdiff_t k ) const {
275 return my_vector->internal_subscript(my_index+k);
277 Value* operator->() const {return &operator*();}
280 vector_iterator& operator++() {
281 size_t k = ++my_index;
283 // Following test uses 2's-complement wizardry
284 if( (k& (k-2))==0 ) {
285 // k is a power of two that is at least k-2
295 vector_iterator& operator--() {
296 __TBB_ASSERT( my_index>0, "operator--() applied to iterator already at beginning of concurrent_vector" );
297 size_t k = my_index--;
299 // Following test uses 2's-complement wizardry
300 if( (k& (k-2))==0 ) {
301 // k is a power of two that is at least k-2
311 vector_iterator operator++(int) {
312 vector_iterator result = *this;
318 vector_iterator operator--(int) {
319 vector_iterator result = *this;
326 typedef ptrdiff_t difference_type;
327 typedef Value value_type;
328 typedef Value* pointer;
329 typedef Value& reference;
330 typedef std::random_access_iterator_tag iterator_category;
333 template<typename Container, typename T>
334 vector_iterator<Container,T> operator+( ptrdiff_t offset, const vector_iterator<Container,T>& v ) {
335 return vector_iterator<Container,T>( *v.my_vector, v.my_index+offset );
338 template<typename Container, typename T, typename U>
339 bool operator==( const vector_iterator<Container,T>& i, const vector_iterator<Container,U>& j ) {
340 return i.my_index==j.my_index && i.my_vector == j.my_vector;
343 template<typename Container, typename T, typename U>
344 bool operator!=( const vector_iterator<Container,T>& i, const vector_iterator<Container,U>& j ) {
348 template<typename Container, typename T, typename U>
349 bool operator<( const vector_iterator<Container,T>& i, const vector_iterator<Container,U>& j ) {
350 return i.my_index<j.my_index;
353 template<typename Container, typename T, typename U>
354 bool operator>( const vector_iterator<Container,T>& i, const vector_iterator<Container,U>& j ) {
358 template<typename Container, typename T, typename U>
359 bool operator>=( const vector_iterator<Container,T>& i, const vector_iterator<Container,U>& j ) {
363 template<typename Container, typename T, typename U>
364 bool operator<=( const vector_iterator<Container,T>& i, const vector_iterator<Container,U>& j ) {
368 template<typename Container, typename T, typename U>
369 ptrdiff_t operator-( const vector_iterator<Container,T>& i, const vector_iterator<Container,U>& j ) {
370 return ptrdiff_t(i.my_index)-ptrdiff_t(j.my_index);
373 template<typename T, class A>
374 class allocator_base {
376 typedef typename A::template
377 rebind<T>::other allocator_type;
378 allocator_type my_allocator;
380 allocator_base(const allocator_type &a = allocator_type() ) : my_allocator(a) {}
383 } // namespace internal
386 //! Concurrent vector container
387 /** concurrent_vector is a container having the following main properties:
388 - It provides random indexed access to its elements. The index of the first element is 0.
389 - It ensures safe concurrent growing its size (different threads can safely append new elements).
390 - Adding new elements does not invalidate existing iterators and does not change indices of existing items.
393 The class meets all Container Requirements and Reversible Container Requirements from
394 C++ Standard (See ISO/IEC 14882:2003(E), clause 23.1). But it doesn't meet
395 Sequence Requirements due to absence of insert() and erase() methods.
397 @par Exception Safety
398 Methods working with memory allocation and/or new elements construction can throw an
399 exception if allocator fails to allocate memory or element's default constructor throws one.
400 Concurrent vector's element of type T must conform to the following requirements:
401 - Throwing an exception is forbidden for destructor of T.
402 - Default constructor of T must not throw an exception OR its non-virtual destructor must safely work when its object memory is zero-initialized.
404 Otherwise, the program's behavior is undefined.
406 If an exception happens inside growth or assignment operation, an instance of the vector becomes invalid unless it is stated otherwise in the method documentation.
408 - There are no guaranties that all items were initialized by a constructor. The rest of items is zero-filled, including item where exception happens.
409 - An invalid vector instance cannot be repaired; it is unable to grow anymore.
410 - Size and capacity reported by the vector are incorrect, and calculated as if the failed operation were successful.
411 - Attempt to access not allocated elements using operator[] or iterators results in access violation or segmentation fault exception, and in case of using at() method a C++ exception is thrown.
413 If a concurrent grow operation successfully completes, all the elements it has added to the vector will remain valid and accessible even if one of subsequent grow operations fails.
416 Unlike an STL vector, a concurrent_vector does not move existing elements if it needs
417 to allocate more memory. The container is divided into a series of contiguous arrays of
418 elements. The first reservation, growth, or assignment operation determines the size of
419 the first array. Using small number of elements as initial size incurs fragmentation that
420 may increase element access time. Internal layout can be optimized by method compact() that
421 merges several smaller arrays into one solid.
423 @par Changes since TBB 2.1
424 - Fixed guarantees of concurrent_vector::size() and grow_to_at_least() methods to assure elements are allocated.
425 - Methods end()/rbegin()/back() are partly thread-safe since they use size() to get the end of vector
426 - Added resize() methods (not thread-safe)
427 - Added cbegin/cend/crbegin/crend methods
428 - Changed return type of methods grow* and push_back to iterator
430 @par Changes since TBB 2.0
431 - Implemented exception-safety guaranties
432 - Added template argument for allocator
433 - Added allocator argument in constructors
434 - Faster index calculation
435 - First growth call specifies a number of segments to be merged in the first allocation.
436 - Fixed memory blow up for swarm of vector's instances of small size
437 - Added grow_by(size_type n, const_reference t) growth using copying constructor to init new items.
438 - Added STL-like constructors.
439 - Added operators ==, < and derivatives
440 - Added at() method, approved for using after an exception was thrown inside the vector
441 - Added get_allocator() method.
442 - Added assign() methods
443 - Added compact() method to defragment first segments
444 - Added swap() method
445 - range() defaults on grainsize = 1 supporting auto grainsize algorithms.
447 @ingroup containers */
448 template<typename T, class A>
449 class concurrent_vector: protected internal::allocator_base<T, A>,
450 private internal::concurrent_vector_base {
453 class generic_range_type: public blocked_range<I> {
455 typedef T value_type;
456 typedef T& reference;
457 typedef const T& const_reference;
459 typedef ptrdiff_t difference_type;
460 generic_range_type( I begin_, I end_, size_t grainsize_ = 1) : blocked_range<I>(begin_,end_,grainsize_) {}
462 generic_range_type( const generic_range_type<U>& r) : blocked_range<I>(r.begin(),r.end(),r.grainsize()) {}
463 generic_range_type( generic_range_type& r, split ) : blocked_range<I>(r,split()) {}
466 template<typename C, typename U>
467 friend class internal::vector_iterator;
469 //------------------------------------------------------------------------
470 // STL compatible types
471 //------------------------------------------------------------------------
472 typedef internal::concurrent_vector_base_v3::size_type size_type;
473 typedef typename internal::allocator_base<T, A>::allocator_type allocator_type;
475 typedef T value_type;
476 typedef ptrdiff_t difference_type;
477 typedef T& reference;
478 typedef const T& const_reference;
480 typedef const T *const_pointer;
482 typedef internal::vector_iterator<concurrent_vector,T> iterator;
483 typedef internal::vector_iterator<concurrent_vector,const T> const_iterator;
485 #if !defined(_MSC_VER) || _CPPLIB_VER>=300
486 // Assume ISO standard definition of std::reverse_iterator
487 typedef std::reverse_iterator<iterator> reverse_iterator;
488 typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
490 // Use non-standard std::reverse_iterator
491 typedef std::reverse_iterator<iterator,T,T&,T*> reverse_iterator;
492 typedef std::reverse_iterator<const_iterator,T,const T&,const T*> const_reverse_iterator;
493 #endif /* defined(_MSC_VER) && (_MSC_VER<1300) */
495 //------------------------------------------------------------------------
496 // Parallel algorithm support
497 //------------------------------------------------------------------------
498 typedef generic_range_type<iterator> range_type;
499 typedef generic_range_type<const_iterator> const_range_type;
501 //------------------------------------------------------------------------
502 // STL compatible constructors & destructors
503 //------------------------------------------------------------------------
505 //! Construct empty vector.
506 explicit concurrent_vector(const allocator_type &a = allocator_type())
507 : internal::allocator_base<T, A>(a), internal::concurrent_vector_base()
509 vector_allocator_ptr = &internal_allocator;
512 //! Copying constructor
513 concurrent_vector( const concurrent_vector& vector, const allocator_type& a = allocator_type() )
514 : internal::allocator_base<T, A>(a), internal::concurrent_vector_base()
516 vector_allocator_ptr = &internal_allocator;
518 internal_copy(vector, sizeof(T), ©_array);
520 segment_t *table = my_segment;
521 internal_free_segments( reinterpret_cast<void**>(table), internal_clear(&destroy_array), my_first_block );
526 //! Copying constructor for vector with different allocator type
528 concurrent_vector( const concurrent_vector<T, M>& vector, const allocator_type& a = allocator_type() )
529 : internal::allocator_base<T, A>(a), internal::concurrent_vector_base()
531 vector_allocator_ptr = &internal_allocator;
533 internal_copy(vector.internal_vector_base(), sizeof(T), ©_array);
535 segment_t *table = my_segment;
536 internal_free_segments( reinterpret_cast<void**>(table), internal_clear(&destroy_array), my_first_block );
541 //! Construction with initial size specified by argument n
542 explicit concurrent_vector(size_type n)
544 vector_allocator_ptr = &internal_allocator;
546 internal_resize( n, sizeof(T), max_size(), NULL, &destroy_array, &initialize_array );
548 segment_t *table = my_segment;
549 internal_free_segments( reinterpret_cast<void**>(table), internal_clear(&destroy_array), my_first_block );
554 //! Construction with initial size specified by argument n, initialization by copying of t, and given allocator instance
555 concurrent_vector(size_type n, const_reference t, const allocator_type& a = allocator_type())
556 : internal::allocator_base<T, A>(a)
558 vector_allocator_ptr = &internal_allocator;
560 internal_resize( n, sizeof(T), max_size(), static_cast<const void*>(&t), &destroy_array, &initialize_array_by );
562 segment_t *table = my_segment;
563 internal_free_segments( reinterpret_cast<void**>(table), internal_clear(&destroy_array), my_first_block );
568 //! Construction with copying iteration range and given allocator instance
570 concurrent_vector(I first, I last, const allocator_type &a = allocator_type())
571 : internal::allocator_base<T, A>(a)
573 vector_allocator_ptr = &internal_allocator;
575 internal_assign_range(first, last, static_cast<is_integer_tag<std::numeric_limits<I>::is_integer> *>(0) );
577 segment_t *table = my_segment;
578 internal_free_segments( reinterpret_cast<void**>(table), internal_clear(&destroy_array), my_first_block );
584 concurrent_vector& operator=( const concurrent_vector& vector ) {
585 if( this != &vector )
586 internal_assign(vector, sizeof(T), &destroy_array, &assign_array, ©_array);
590 //! Assignment for vector with different allocator type
592 concurrent_vector& operator=( const concurrent_vector<T, M>& vector ) {
593 if( static_cast<void*>( this ) != static_cast<const void*>( &vector ) )
594 internal_assign(vector.internal_vector_base(),
595 sizeof(T), &destroy_array, &assign_array, ©_array);
599 //------------------------------------------------------------------------
600 // Concurrent operations
601 //------------------------------------------------------------------------
602 //! Grow by "delta" elements.
604 /** Returns old size. */
605 size_type grow_by( size_type delta ) {
606 return delta ? internal_grow_by( delta, sizeof(T), &initialize_array, NULL ) : my_early_size;
609 /** Returns iterator pointing to the first new element. */
610 iterator grow_by( size_type delta ) {
611 return iterator(*this, delta ? internal_grow_by( delta, sizeof(T), &initialize_array, NULL ) : my_early_size);
615 //! Grow by "delta" elements using copying constuctor.
617 /** Returns old size. */
618 size_type grow_by( size_type delta, const_reference t ) {
619 return delta ? internal_grow_by( delta, sizeof(T), &initialize_array_by, static_cast<const void*>(&t) ) : my_early_size;
622 /** Returns iterator pointing to the first new element. */
623 iterator grow_by( size_type delta, const_reference t ) {
624 return iterator(*this, delta ? internal_grow_by( delta, sizeof(T), &initialize_array_by, static_cast<const void*>(&t) ) : my_early_size);
628 //! Append minimal sequence of elements such that size()>=n.
630 /** The new elements are default constructed. Blocks until all elements in range [0..n) are allocated.
631 May return while other elements are being constructed by other threads. */
632 void grow_to_at_least( size_type n ) {
633 if( n ) internal_grow_to_at_least_with_result( n, sizeof(T), &initialize_array, NULL );
636 /** The new elements are default constructed. Blocks until all elements in range [0..n) are allocated.
637 May return while other elements are being constructed by other threads.
638 Returns iterator that points to beginning of appended sequence.
639 If no elements were appended, returns iterator pointing to nth element. */
640 iterator grow_to_at_least( size_type n ) {
643 m = internal_grow_to_at_least_with_result( n, sizeof(T), &initialize_array, NULL );
646 return iterator(*this, m);
652 size_type push_back( const_reference item )
654 /** Returns iterator pointing to the new element. */
655 iterator push_back( const_reference item )
659 void *ptr = internal_push_back(sizeof(T),k);
660 internal_loop_guide loop(1, ptr);
665 return iterator(*this, k, ptr);
669 //! Get reference to element at given index.
670 /** This method is thread-safe for concurrent reads, and also while growing the vector,
671 as long as the calling thread has checked that index<size(). */
672 reference operator[]( size_type index ) {
673 return internal_subscript(index);
676 //! Get const reference to element at given index.
677 const_reference operator[]( size_type index ) const {
678 return internal_subscript(index);
681 //! Get reference to element at given index. Throws exceptions on errors.
682 reference at( size_type index ) {
683 return internal_subscript_with_exceptions(index);
686 //! Get const reference to element at given index. Throws exceptions on errors.
687 const_reference at( size_type index ) const {
688 return internal_subscript_with_exceptions(index);
691 //! Get range for iterating with parallel algorithms
692 range_type range( size_t grainsize = 1) {
693 return range_type( begin(), end(), grainsize );
696 //! Get const range for iterating with parallel algorithms
697 const_range_type range( size_t grainsize = 1 ) const {
698 return const_range_type( begin(), end(), grainsize );
700 //------------------------------------------------------------------------
702 //------------------------------------------------------------------------
703 //! Return size of vector. It may include elements under construction
704 size_type size() const {
705 size_type sz = my_early_size, cp = internal_capacity();
706 return cp < sz ? cp : sz;
709 //! Return true if vector is not empty or has elements under construction at least.
710 bool empty() const {return !my_early_size;}
712 //! Maximum size to which array can grow without allocating more memory. Concurrent allocations are not included in the value.
713 size_type capacity() const {return internal_capacity();}
715 //! Allocate enough space to grow to size n without having to allocate more memory later.
716 /** Like most of the methods provided for STL compatibility, this method is *not* thread safe.
717 The capacity afterwards may be bigger than the requested reservation. */
718 void reserve( size_type n ) {
720 internal_reserve(n, sizeof(T), max_size());
723 //! Resize the vector. Not thread-safe.
724 void resize( size_type n ) {
725 internal_resize( n, sizeof(T), max_size(), NULL, &destroy_array, &initialize_array );
728 //! Resize the vector, copy t for new elements. Not thread-safe.
729 void resize( size_type n, const_reference t ) {
730 internal_resize( n, sizeof(T), max_size(), static_cast<const void*>(&t), &destroy_array, &initialize_array_by );
734 //! An alias for shrink_to_fit()
735 void compact() {shrink_to_fit();}
736 #endif /* TBB_DEPRECATED */
738 //! Optimize memory usage and fragmentation.
739 void shrink_to_fit();
741 //! Upper bound on argument to reserve.
742 size_type max_size() const {return (~size_type(0))/sizeof(T);}
744 //------------------------------------------------------------------------
746 //------------------------------------------------------------------------
749 iterator begin() {return iterator(*this,0);}
751 iterator end() {return iterator(*this,size());}
752 //! start const iterator
753 const_iterator begin() const {return const_iterator(*this,0);}
754 //! end const iterator
755 const_iterator end() const {return const_iterator(*this,size());}
756 //! start const iterator
757 const_iterator cbegin() const {return const_iterator(*this,0);}
758 //! end const iterator
759 const_iterator cend() const {return const_iterator(*this,size());}
760 //! reverse start iterator
761 reverse_iterator rbegin() {return reverse_iterator(end());}
762 //! reverse end iterator
763 reverse_iterator rend() {return reverse_iterator(begin());}
764 //! reverse start const iterator
765 const_reverse_iterator rbegin() const {return const_reverse_iterator(end());}
766 //! reverse end const iterator
767 const_reverse_iterator rend() const {return const_reverse_iterator(begin());}
768 //! reverse start const iterator
769 const_reverse_iterator crbegin() const {return const_reverse_iterator(end());}
770 //! reverse end const iterator
771 const_reverse_iterator crend() const {return const_reverse_iterator(begin());}
774 __TBB_ASSERT( size()>0, NULL);
775 return static_cast<T*>(my_segment[0].array)[0];
777 //! the first item const
778 const_reference front() const {
779 __TBB_ASSERT( size()>0, NULL);
780 return static_cast<const T*>(my_segment[0].array)[0];
784 __TBB_ASSERT( size()>0, NULL);
785 return internal_subscript( size()-1 );
787 //! the last item const
788 const_reference back() const {
789 __TBB_ASSERT( size()>0, NULL);
790 return internal_subscript( size()-1 );
792 //! return allocator object
793 allocator_type get_allocator() const { return this->my_allocator; }
795 //! assign n items by copying t item
796 void assign(size_type n, const_reference t) {
798 internal_resize( n, sizeof(T), max_size(), static_cast<const void*>(&t), &destroy_array, &initialize_array_by );
801 //! assign range [first, last)
803 void assign(I first, I last) {
804 clear(); internal_assign_range( first, last, static_cast<is_integer_tag<std::numeric_limits<I>::is_integer> *>(0) );
807 //! swap two instances
808 void swap(concurrent_vector &vector) {
809 if( this != &vector ) {
810 concurrent_vector_base_v3::internal_swap(static_cast<concurrent_vector_base_v3&>(vector));
811 std::swap(this->my_allocator, vector.my_allocator);
815 //! Clear container while keeping memory allocated.
816 /** To free up the memory, use in conjunction with method compact(). Not thread safe **/
818 internal_clear(&destroy_array);
821 //! Clear and destroy vector.
822 ~concurrent_vector() {
823 segment_t *table = my_segment;
824 internal_free_segments( reinterpret_cast<void**>(table), internal_clear(&destroy_array), my_first_block );
825 // base class destructor call should be then
828 const internal::concurrent_vector_base_v3 &internal_vector_base() const { return *this; }
831 static void *internal_allocator(internal::concurrent_vector_base_v3 &vb, size_t k) {
832 return static_cast<concurrent_vector<T, A>&>(vb).my_allocator.allocate(k);
834 //! Free k segments from table
835 void internal_free_segments(void *table[], segment_index_t k, segment_index_t first_block);
837 //! Get reference to element at given index.
838 T& internal_subscript( size_type index ) const;
840 //! Get reference to element at given index with errors checks
841 T& internal_subscript_with_exceptions( size_type index ) const;
843 //! assign n items by copying t
844 void internal_assign_n(size_type n, const_pointer p) {
845 internal_resize( n, sizeof(T), max_size(), static_cast<const void*>(p), &destroy_array, p? &initialize_array_by : &initialize_array );
849 template<bool B> class is_integer_tag;
851 //! assign integer items by copying when arguments are treated as iterators. See C++ Standard 2003 23.1.1p9
853 void internal_assign_range(I first, I last, is_integer_tag<true> *) {
854 internal_assign_n(static_cast<size_type>(first), &static_cast<T&>(last));
856 //! inline proxy assign by iterators
858 void internal_assign_range(I first, I last, is_integer_tag<false> *) {
859 internal_assign_iterators(first, last);
861 //! assign by iterators
863 void internal_assign_iterators(I first, I last);
865 //! Construct n instances of T, starting at "begin".
866 static void __TBB_EXPORTED_FUNC initialize_array( void* begin, const void*, size_type n );
868 //! Construct n instances of T, starting at "begin".
869 static void __TBB_EXPORTED_FUNC initialize_array_by( void* begin, const void* src, size_type n );
871 //! Construct n instances of T, starting at "begin".
872 static void __TBB_EXPORTED_FUNC copy_array( void* dst, const void* src, size_type n );
874 //! Assign n instances of T, starting at "begin".
875 static void __TBB_EXPORTED_FUNC assign_array( void* dst, const void* src, size_type n );
877 //! Destroy n instances of T, starting at "begin".
878 static void __TBB_EXPORTED_FUNC destroy_array( void* begin, size_type n );
880 //! Exception-aware helper class for filling a segment by exception-danger operators of user class
881 class internal_loop_guide : internal::no_copy {
886 internal_loop_guide(size_type ntrials, void *ptr)
887 : array(static_cast<pointer>(ptr)), n(ntrials), i(0) {}
888 void init() { for(; i < n; ++i) new( &array[i] ) T(); }
889 void init(const void *src) { for(; i < n; ++i) new( &array[i] ) T(*static_cast<const T*>(src)); }
890 void copy(const void *src) { for(; i < n; ++i) new( &array[i] ) T(static_cast<const T*>(src)[i]); }
891 void assign(const void *src) { for(; i < n; ++i) array[i] = static_cast<const T*>(src)[i]; }
892 template<class I> void iterate(I &src) { for(; i < n; ++i, ++src) new( &array[i] ) T( *src ); }
893 ~internal_loop_guide() {
894 if(i < n) // if exception raised, do zerroing on the rest of items
895 std::memset(array+i, 0, (n-i)*sizeof(value_type));
900 #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
901 #pragma warning (push)
902 #pragma warning (disable: 4701) // potentially uninitialized local variable "old"
904 template<typename T, class A>
905 void concurrent_vector<T, A>::shrink_to_fit() {
906 internal_segments_table old;
908 if( internal_compact( sizeof(T), &old, &destroy_array, ©_array ) )
909 internal_free_segments( old.table, pointers_per_long_table, old.first_block ); // free joined and unnecessary segments
911 if( old.first_block ) // free segment allocated for compacting. Only for support of exceptions in ctor of user T[ype]
912 internal_free_segments( old.table, 1, old.first_block );
916 #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
917 #pragma warning (pop)
918 #endif // warning 4701 is back
920 template<typename T, class A>
921 void concurrent_vector<T, A>::internal_free_segments(void *table[], segment_index_t k, segment_index_t first_block) {
923 while( k > first_block ) {
925 T* array = static_cast<T*>(table[k]);
927 if( array > internal::vector_allocation_error_flag ) // check for correct segment pointer
928 this->my_allocator.deallocate( array, segment_size(k) );
930 T* array = static_cast<T*>(table[0]);
931 if( array > internal::vector_allocation_error_flag ) {
932 __TBB_ASSERT( first_block > 0, NULL );
933 while(k > 0) table[--k] = NULL;
934 this->my_allocator.deallocate( array, segment_size(first_block) );
938 template<typename T, class A>
939 T& concurrent_vector<T, A>::internal_subscript( size_type index ) const {
940 __TBB_ASSERT( index < my_early_size, "index out of bounds" );
942 segment_index_t k = segment_base_index_of( j );
943 __TBB_ASSERT( (segment_t*)my_segment != my_storage || k < pointers_per_short_table, "index is being allocated" );
944 // no need in __TBB_load_with_acquire since thread works in own space or gets
945 T* array = static_cast<T*>( tbb::internal::itt_hide_load_word(my_segment[k].array));
946 __TBB_ASSERT( array != internal::vector_allocation_error_flag, "the instance is broken by bad allocation. Use at() instead" );
947 __TBB_ASSERT( array, "index is being allocated" );
951 template<typename T, class A>
952 T& concurrent_vector<T, A>::internal_subscript_with_exceptions( size_type index ) const {
953 if( index >= my_early_size )
954 internal::throw_exception(internal::eid_out_of_range); // throw std::out_of_range
956 segment_index_t k = segment_base_index_of( j );
957 if( (segment_t*)my_segment == my_storage && k >= pointers_per_short_table )
958 internal::throw_exception(internal::eid_segment_range_error); // throw std::range_error
959 void *array = my_segment[k].array; // no need in __TBB_load_with_acquire
960 if( array <= internal::vector_allocation_error_flag ) // check for correct segment pointer
961 internal::throw_exception(internal::eid_index_range_error); // throw std::range_error
962 return static_cast<T*>(array)[j];
965 template<typename T, class A> template<class I>
966 void concurrent_vector<T, A>::internal_assign_iterators(I first, I last) {
967 __TBB_ASSERT(my_early_size == 0, NULL);
968 size_type n = std::distance(first, last);
970 internal_reserve(n, sizeof(T), max_size());
972 segment_index_t k = 0;
973 size_type sz = segment_size( my_first_block );
975 internal_loop_guide loop(sz, my_segment[k].array);
978 if( !k ) k = my_first_block;
979 else { ++k; sz <<= 1; }
981 internal_loop_guide loop(n, my_segment[k].array);
985 template<typename T, class A>
986 void concurrent_vector<T, A>::initialize_array( void* begin, const void *, size_type n ) {
987 internal_loop_guide loop(n, begin); loop.init();
990 template<typename T, class A>
991 void concurrent_vector<T, A>::initialize_array_by( void* begin, const void *src, size_type n ) {
992 internal_loop_guide loop(n, begin); loop.init(src);
995 template<typename T, class A>
996 void concurrent_vector<T, A>::copy_array( void* dst, const void* src, size_type n ) {
997 internal_loop_guide loop(n, dst); loop.copy(src);
1000 template<typename T, class A>
1001 void concurrent_vector<T, A>::assign_array( void* dst, const void* src, size_type n ) {
1002 internal_loop_guide loop(n, dst); loop.assign(src);
1005 #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
1006 // Workaround for overzealous compiler warning
1007 #pragma warning (push)
1008 #pragma warning (disable: 4189)
1010 template<typename T, class A>
1011 void concurrent_vector<T, A>::destroy_array( void* begin, size_type n ) {
1012 T* array = static_cast<T*>(begin);
1013 for( size_type j=n; j>0; --j )
1014 array[j-1].~T(); // destructors are supposed to not throw any exceptions
1016 #if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
1017 #pragma warning (pop)
1018 #endif // warning 4189 is back
1020 // concurrent_vector's template functions
1021 template<typename T, class A1, class A2>
1022 inline bool operator==(const concurrent_vector<T, A1> &a, const concurrent_vector<T, A2> &b) {
1023 // Simply: return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin());
1024 if(a.size() != b.size()) return false;
1025 typename concurrent_vector<T, A1>::const_iterator i(a.begin());
1026 typename concurrent_vector<T, A2>::const_iterator j(b.begin());
1027 for(; i != a.end(); ++i, ++j)
1028 if( !(*i == *j) ) return false;
1032 template<typename T, class A1, class A2>
1033 inline bool operator!=(const concurrent_vector<T, A1> &a, const concurrent_vector<T, A2> &b)
1034 { return !(a == b); }
1036 template<typename T, class A1, class A2>
1037 inline bool operator<(const concurrent_vector<T, A1> &a, const concurrent_vector<T, A2> &b)
1038 { return (std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end())); }
1040 template<typename T, class A1, class A2>
1041 inline bool operator>(const concurrent_vector<T, A1> &a, const concurrent_vector<T, A2> &b)
1044 template<typename T, class A1, class A2>
1045 inline bool operator<=(const concurrent_vector<T, A1> &a, const concurrent_vector<T, A2> &b)
1046 { return !(b < a); }
1048 template<typename T, class A1, class A2>
1049 inline bool operator>=(const concurrent_vector<T, A1> &a, const concurrent_vector<T, A2> &b)
1050 { return !(a < b); }
1052 template<typename T, class A>
1053 inline void swap(concurrent_vector<T, A> &a, concurrent_vector<T, A> &b)
1058 #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) && defined(_Wp64)
1059 #pragma warning (pop)
1060 #endif // warning 4267 is back
1062 #endif /* __TBB_concurrent_vector_H */