/* Copyright 2005-2009 Intel Corporation. All Rights Reserved. This file is part of Threading Building Blocks. Threading Building Blocks is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. Threading Building Blocks is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Threading Building Blocks; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA As a special exception, you may use this file as part of a free software library without restriction. Specifically, if other files instantiate templates or use macros or inline functions from this file, or you compile this file and link it with other files to produce an executable, this file does not by itself cause the resulting executable to be covered by the GNU General Public License. This exception does not however invalidate any other reasons why the executable file might be covered by the GNU General Public License. */ #ifndef __TBB_concurrent_vector_H #define __TBB_concurrent_vector_H #include "tbb/tbb_stddef.h" #include #include #include "tbb/atomic.h" #include "tbb/cache_aligned_allocator.h" #include "tbb/blocked_range.h" #include "tbb/tbb_machine.h" namespace tbb { template class concurrent_vector; //! @cond INTERNAL namespace internal { //! Base class of concurrent vector implementation. /** @ingroup containers */ class concurrent_vector_base { protected: typedef unsigned long segment_index_t; //! Log2 of "min_segment_size". static const int lg_min_segment_size = 4; //! Minimum size (in physical items) of a segment. static const int min_segment_size = segment_index_t(1)<>1< my_early_size; /** Can be zero-initialized. */ struct segment_t { /** Declared volatile because in weak memory model, must have ld.acq/st.rel */ void* volatile array; #if TBB_DO_ASSERT ~segment_t() { __TBB_ASSERT( !array, "should have been set to NULL by clear" ); } #endif /* TBB_DO_ASSERT */ }; atomic my_segment; segment_t my_storage[2]; concurrent_vector_base() { my_early_size = 0; my_storage[0].array = NULL; my_storage[1].array = NULL; my_segment = my_storage; } //! An operation on an n-lement array starting at begin. typedef void(__TBB_EXPORTED_FUNC *internal_array_op1)(void* begin, size_type n ); //! An operation on n-element destination array and n-element source array. typedef void(__TBB_EXPORTED_FUNC *internal_array_op2)(void* dst, const void* src, size_type n ); void __TBB_EXPORTED_METHOD internal_grow_to_at_least( size_type new_size, size_type element_size, internal_array_op1 init ); void internal_grow( size_type start, size_type finish, size_type element_size, internal_array_op1 init ); size_type __TBB_EXPORTED_METHOD internal_grow_by( size_type delta, size_type element_size, internal_array_op1 init ); void* __TBB_EXPORTED_METHOD internal_push_back( size_type element_size, size_type& index ); void __TBB_EXPORTED_METHOD internal_clear( internal_array_op1 destroy, bool reclaim_storage ); void __TBB_EXPORTED_METHOD internal_copy( const concurrent_vector_base& src, size_type element_size, internal_array_op2 copy ); void __TBB_EXPORTED_METHOD internal_assign( const concurrent_vector_base& src, size_type element_size, internal_array_op1 destroy, internal_array_op2 assign, internal_array_op2 copy ); private: //! Private functionality that does not cross DLL boundary. class helper; friend class helper; }; //! Meets requirements of a forward iterator for STL and a Value for a blocked_range.*/ /** Value is either the T or const T type of the container. @ingroup containers */ template class vector_iterator #if defined(_WIN64) && defined(_MSC_VER) // Ensure that Microsoft's internal template function _Val_type works correctly. : public std::iterator #endif /* defined(_WIN64) && defined(_MSC_VER) */ { //! concurrent_vector over which we are iterating. Container* my_vector; //! Index into the vector size_t my_index; //! Caches my_vector->internal_subscript(my_index) /** NULL if cached value is not available */ mutable Value* my_item; template friend bool operator==( const vector_iterator& i, const vector_iterator& j ); template friend bool operator<( const vector_iterator& i, const vector_iterator& j ); template friend ptrdiff_t operator-( const vector_iterator& i, const vector_iterator& j ); template friend class internal::vector_iterator; #if !defined(_MSC_VER) || defined(__INTEL_COMPILER) template friend class tbb::concurrent_vector; #else public: // workaround for MSVC #endif vector_iterator( const Container& vector, size_t index ) : my_vector(const_cast(&vector)), my_index(index), my_item(NULL) {} public: //! Default constructor vector_iterator() : my_vector(NULL), my_index(~size_t(0)), my_item(NULL) {} vector_iterator( const vector_iterator& other ) : my_vector(other.my_vector), my_index(other.my_index), my_item(other.my_item) {} vector_iterator operator+( ptrdiff_t offset ) const { return vector_iterator( *my_vector, my_index+offset ); } friend vector_iterator operator+( ptrdiff_t offset, const vector_iterator& v ) { return vector_iterator( *v.my_vector, v.my_index+offset ); } vector_iterator operator+=( ptrdiff_t offset ) { my_index+=offset; my_item = NULL; return *this; } vector_iterator operator-( ptrdiff_t offset ) const { return vector_iterator( *my_vector, my_index-offset ); } vector_iterator operator-=( ptrdiff_t offset ) { my_index-=offset; my_item = NULL; return *this; } Value& operator*() const { Value* item = my_item; if( !item ) { item = my_item = &my_vector->internal_subscript(my_index); } __TBB_ASSERT( item==&my_vector->internal_subscript(my_index), "corrupt cache" ); return *item; } Value& operator[]( ptrdiff_t k ) const { return my_vector->internal_subscript(my_index+k); } Value* operator->() const {return &operator*();} //! Pre increment vector_iterator& operator++() { size_t k = ++my_index; if( my_item ) { // Following test uses 2's-complement wizardry and fact that // min_segment_size is a power of 2. if( (k& k-concurrent_vector::min_segment_size)==0 ) { // k is a power of two that is at least k-min_segment_size my_item= NULL; } else { ++my_item; } } return *this; } //! Pre decrement vector_iterator& operator--() { __TBB_ASSERT( my_index>0, "operator--() applied to iterator already at beginning of concurrent_vector" ); size_t k = my_index--; if( my_item ) { // Following test uses 2's-complement wizardry and fact that // min_segment_size is a power of 2. if( (k& k-concurrent_vector::min_segment_size)==0 ) { // k is a power of two that is at least k-min_segment_size my_item= NULL; } else { --my_item; } } return *this; } //! Post increment vector_iterator operator++(int) { vector_iterator result = *this; operator++(); return result; } //! Post decrement vector_iterator operator--(int) { vector_iterator result = *this; operator--(); return result; } // STL support typedef ptrdiff_t difference_type; typedef Value value_type; typedef Value* pointer; typedef Value& reference; typedef std::random_access_iterator_tag iterator_category; }; template bool operator==( const vector_iterator& i, const vector_iterator& j ) { return i.my_index==j.my_index; } template bool operator!=( const vector_iterator& i, const vector_iterator& j ) { return !(i==j); } template bool operator<( const vector_iterator& i, const vector_iterator& j ) { return i.my_index bool operator>( const vector_iterator& i, const vector_iterator& j ) { return j bool operator>=( const vector_iterator& i, const vector_iterator& j ) { return !(i bool operator<=( const vector_iterator& i, const vector_iterator& j ) { return !(j ptrdiff_t operator-( const vector_iterator& i, const vector_iterator& j ) { return ptrdiff_t(i.my_index)-ptrdiff_t(j.my_index); } } // namespace internal //! @endcond //! Concurrent vector /** @ingroup containers */ template class concurrent_vector: private internal::concurrent_vector_base { public: using internal::concurrent_vector_base::size_type; private: template class generic_range_type: public blocked_range { public: typedef T value_type; typedef T& reference; typedef const T& const_reference; typedef I iterator; typedef ptrdiff_t difference_type; generic_range_type( I begin_, I end_, size_t grainsize ) : blocked_range(begin_,end_,grainsize) {} generic_range_type( generic_range_type& r, split ) : blocked_range(r,split()) {} }; template friend class internal::vector_iterator; public: typedef T& reference; typedef const T& const_reference; //! Construct empty vector. concurrent_vector() {} //! Copy a vector. concurrent_vector( const concurrent_vector& vector ) {internal_copy(vector,sizeof(T),©_array);} //! Assignment concurrent_vector& operator=( const concurrent_vector& vector ) { if( this!=&vector ) internal_assign(vector,sizeof(T),&destroy_array,&assign_array,©_array); return *this; } //! Clear and destroy vector. ~concurrent_vector() {internal_clear(&destroy_array,/*reclaim_storage=*/true);} //------------------------------------------------------------------------ // Concurrent operations //------------------------------------------------------------------------ //! Grow by "delta" elements. /** Returns old size. */ size_type grow_by( size_type delta ) { return delta ? internal_grow_by( delta, sizeof(T), &initialize_array ) : my_early_size; } //! Grow array until it has at least n elements. void grow_to_at_least( size_type n ) { if( my_early_size iterator; typedef internal::vector_iterator const_iterator; #if !defined(_MSC_VER) || _CPPLIB_VER>=300 // Assume ISO standard definition of std::reverse_iterator typedef std::reverse_iterator reverse_iterator; typedef std::reverse_iterator const_reverse_iterator; #else // Use non-standard std::reverse_iterator typedef std::reverse_iterator reverse_iterator; typedef std::reverse_iterator const_reverse_iterator; #endif /* defined(_MSC_VER) && (_MSC_VER<1300) */ typedef generic_range_type range_type; typedef generic_range_type const_range_type; range_type range( size_t grainsize = 1 ) { return range_type( begin(), end(), grainsize ); } const_range_type range( size_t grainsize = 1 ) const { return const_range_type( begin(), end(), grainsize ); } //------------------------------------------------------------------------ // Capacity //------------------------------------------------------------------------ //! Return size of vector. size_type size() const {return my_early_size;} //! Return size of vector. bool empty() const {return !my_early_size;} //! Maximum size to which array can grow without allocating more memory. size_type capacity() const {return internal_capacity();} //! Allocate enough space to grow to size n without having to allocate more memory later. /** Like most of the methods provided for STL compatibility, this method is *not* thread safe. The capacity afterwards may be bigger than the requested reservation. */ void reserve( size_type n ) { if( n ) internal_reserve(n, sizeof(T), max_size()); } //! Upper bound on argument to reserve. size_type max_size() const {return (~size_t(0))/sizeof(T);} //------------------------------------------------------------------------ // STL support //------------------------------------------------------------------------ typedef T value_type; typedef ptrdiff_t difference_type; iterator begin() {return iterator(*this,0);} iterator end() {return iterator(*this,size());} const_iterator begin() const {return const_iterator(*this,0);} const_iterator end() const {return const_iterator(*this,size());} reverse_iterator rbegin() {return reverse_iterator(end());} reverse_iterator rend() {return reverse_iterator(begin());} const_reverse_iterator rbegin() const {return const_reverse_iterator(end());} const_reverse_iterator rend() const {return const_reverse_iterator(begin());} //! Not thread safe /** Does not change capacity. */ void clear() {internal_clear(&destroy_array,/*reclaim_storage=*/false);} private: //! Get reference to element at given index. T& internal_subscript( size_type index ) const; //! Construct n instances of T, starting at "begin". static void __TBB_EXPORTED_FUNC initialize_array( void* begin, size_type n ); //! Construct n instances of T, starting at "begin". static void __TBB_EXPORTED_FUNC copy_array( void* dst, const void* src, size_type n ); //! Assign n instances of T, starting at "begin". static void __TBB_EXPORTED_FUNC assign_array( void* dst, const void* src, size_type n ); //! Destroy n instances of T, starting at "begin". static void __TBB_EXPORTED_FUNC destroy_array( void* begin, size_type n ); }; template T& concurrent_vector::internal_subscript( size_type index ) const { __TBB_ASSERT( index(my_segment[k].array)[j]; } template void concurrent_vector::initialize_array( void* begin, size_type n ) { T* array = static_cast(begin); for( size_type j=0; j void concurrent_vector::copy_array( void* dst, const void* src, size_type n ) { T* d = static_cast(dst); const T* s = static_cast(src); for( size_type j=0; j void concurrent_vector::assign_array( void* dst, const void* src, size_type n ) { T* d = static_cast(dst); const T* s = static_cast(src); for( size_type j=0; j void concurrent_vector::destroy_array( void* begin, size_type n ) { T* array = static_cast(begin); for( size_type j=n; j>0; --j ) array[j-1].~T(); } } // namespace tbb #endif /* __TBB_concurrent_vector_H */