You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							420 lines
						
					
					
						
							14 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							420 lines
						
					
					
						
							14 KiB
						
					
					
				| /* | |
|     Copyright 2005-2013 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_queue_H | |
| #define __TBB_concurrent_queue_H | |
|  | |
| #include "internal/_concurrent_queue_impl.h" | |
|  | |
| namespace tbb { | |
| 
 | |
| namespace strict_ppl { | |
| 
 | |
| //! A high-performance thread-safe non-blocking concurrent queue. | |
| /** Multiple threads may each push and pop concurrently. | |
|     Assignment construction is not allowed. | |
|     @ingroup containers */ | |
| template<typename T, typename A = cache_aligned_allocator<T> >  | |
| class concurrent_queue: public internal::concurrent_queue_base_v3<T> { | |
|     template<typename Container, typename Value> friend class internal::concurrent_queue_iterator; | |
| 
 | |
|     //! Allocator type | |
|     typedef typename A::template rebind<char>::other page_allocator_type; | |
|     page_allocator_type my_allocator; | |
| 
 | |
|     //! Allocates a block of size n (bytes) | |
|     /*override*/ virtual void *allocate_block( size_t n ) { | |
|         void *b = reinterpret_cast<void*>(my_allocator.allocate( n )); | |
|         if( !b ) | |
|             internal::throw_exception(internal::eid_bad_alloc);  | |
|         return b; | |
|     } | |
| 
 | |
|     //! Deallocates block created by allocate_block. | |
|     /*override*/ virtual void deallocate_block( void *b, size_t n ) { | |
|         my_allocator.deallocate( reinterpret_cast<char*>(b), n ); | |
|     } | |
| 
 | |
| public: | |
|     //! Element type in the queue. | |
|     typedef T value_type; | |
| 
 | |
|     //! Reference type | |
|     typedef T& reference; | |
| 
 | |
|     //! Const reference type | |
|     typedef const T& const_reference; | |
| 
 | |
|     //! Integral type for representing size of the queue. | |
|     typedef size_t size_type; | |
| 
 | |
|     //! Difference type for iterator | |
|     typedef ptrdiff_t difference_type; | |
| 
 | |
|     //! Allocator type | |
|     typedef A allocator_type; | |
| 
 | |
|     //! Construct empty queue | |
|     explicit concurrent_queue(const allocator_type& a = allocator_type()) :  | |
|         my_allocator( a ) | |
|     { | |
|     } | |
| 
 | |
|     //! [begin,end) constructor | |
|     template<typename InputIterator> | |
|     concurrent_queue( InputIterator begin, InputIterator end, const allocator_type& a = allocator_type()) : | |
|         my_allocator( a ) | |
|     { | |
|         for( ; begin != end; ++begin ) | |
|             this->internal_push(&*begin); | |
|     } | |
|      | |
|     //! Copy constructor | |
|     concurrent_queue( const concurrent_queue& src, const allocator_type& a = allocator_type()) :  | |
|         internal::concurrent_queue_base_v3<T>(), my_allocator( a ) | |
|     { | |
|         this->assign( src ); | |
|     } | |
|      | |
|     //! Destroy queue | |
|     ~concurrent_queue(); | |
| 
 | |
|     //! Enqueue an item at tail of queue. | |
|     void push( const T& source ) { | |
|         this->internal_push( &source ); | |
|     } | |
| 
 | |
|     //! Attempt to dequeue an item from head of queue. | |
|     /** Does not wait for item to become available. | |
|         Returns true if successful; false otherwise. */ | |
|     bool try_pop( T& result ) { | |
|         return this->internal_try_pop( &result ); | |
|     } | |
| 
 | |
|     //! Return the number of items in the queue; thread unsafe | |
|     size_type unsafe_size() const {return this->internal_size();} | |
| 
 | |
|     //! Equivalent to size()==0. | |
|     bool empty() const {return this->internal_empty();} | |
| 
 | |
|     //! Clear the queue. not thread-safe. | |
|     void clear() ; | |
| 
 | |
|     //! Return allocator object | |
|     allocator_type get_allocator() const { return this->my_allocator; } | |
| 
 | |
|     typedef internal::concurrent_queue_iterator<concurrent_queue,T> iterator; | |
|     typedef internal::concurrent_queue_iterator<concurrent_queue,const T> const_iterator; | |
| 
 | |
|     //------------------------------------------------------------------------ | |
|     // The iterators are intended only for debugging.  They are slow and not thread safe. | |
|     //------------------------------------------------------------------------ | |
|     iterator unsafe_begin() {return iterator(*this);} | |
|     iterator unsafe_end() {return iterator();} | |
|     const_iterator unsafe_begin() const {return const_iterator(*this);} | |
|     const_iterator unsafe_end() const {return const_iterator();} | |
| } ; | |
| 
 | |
| template<typename T, class A> | |
| concurrent_queue<T,A>::~concurrent_queue() { | |
|     clear(); | |
|     this->internal_finish_clear(); | |
| } | |
| 
 | |
| template<typename T, class A> | |
| void concurrent_queue<T,A>::clear() { | |
|     while( !empty() ) { | |
|         T value; | |
|         this->internal_try_pop(&value); | |
|     } | |
| } | |
| 
 | |
| } // namespace strict_ppl | |
|      | |
| //! A high-performance thread-safe blocking concurrent bounded queue. | |
| /** This is the pre-PPL TBB concurrent queue which supports boundedness and blocking semantics. | |
|     Note that method names agree with the PPL-style concurrent queue. | |
|     Multiple threads may each push and pop concurrently. | |
|     Assignment construction is not allowed. | |
|     @ingroup containers */ | |
| template<typename T, class A = cache_aligned_allocator<T> > | |
| class concurrent_bounded_queue: public internal::concurrent_queue_base_v3 { | |
|     template<typename Container, typename Value> friend class internal::concurrent_queue_iterator; | |
| 
 | |
|     //! Allocator type | |
|     typedef typename A::template rebind<char>::other page_allocator_type; | |
|     page_allocator_type my_allocator; | |
| 
 | |
|     typedef typename concurrent_queue_base_v3::padded_page<T> padded_page; | |
|   | |
|     //! Class used to ensure exception-safety of method "pop"  | |
|     class destroyer: internal::no_copy { | |
|         T& my_value; | |
|     public: | |
|         destroyer( T& value ) : my_value(value) {} | |
|         ~destroyer() {my_value.~T();}           | |
|     }; | |
| 
 | |
|     T& get_ref( page& p, size_t index ) { | |
|         __TBB_ASSERT( index<items_per_page, NULL ); | |
|         return (&static_cast<padded_page*>(static_cast<void*>(&p))->last)[index]; | |
|     } | |
| 
 | |
|     /*override*/ virtual void copy_item( page& dst, size_t index, const void* src ) { | |
|         new( &get_ref(dst,index) ) T(*static_cast<const T*>(src));  | |
|     } | |
| 
 | |
|     /*override*/ virtual void copy_page_item( page& dst, size_t dindex, const page& src, size_t sindex ) { | |
|         new( &get_ref(dst,dindex) ) T( get_ref( const_cast<page&>(src), sindex ) ); | |
|     } | |
| 
 | |
|     /*override*/ virtual void assign_and_destroy_item( void* dst, page& src, size_t index ) { | |
|         T& from = get_ref(src,index); | |
|         destroyer d(from); | |
|         *static_cast<T*>(dst) = from; | |
|     } | |
| 
 | |
|     /*override*/ virtual page *allocate_page() { | |
|         size_t n = sizeof(padded_page) + (items_per_page-1)*sizeof(T); | |
|         page *p = reinterpret_cast<page*>(my_allocator.allocate( n )); | |
|         if( !p ) | |
|             internal::throw_exception(internal::eid_bad_alloc);  | |
|         return p; | |
|     } | |
| 
 | |
|     /*override*/ virtual void deallocate_page( page *p ) { | |
|         size_t n = sizeof(padded_page) + (items_per_page-1)*sizeof(T); | |
|         my_allocator.deallocate( reinterpret_cast<char*>(p), n ); | |
|     } | |
| 
 | |
| public: | |
|     //! Element type in the queue. | |
|     typedef T value_type; | |
| 
 | |
|     //! Allocator type | |
|     typedef A allocator_type; | |
| 
 | |
|     //! Reference type | |
|     typedef T& reference; | |
| 
 | |
|     //! Const reference type | |
|     typedef const T& const_reference; | |
| 
 | |
|     //! Integral type for representing size of the queue. | |
|     /** Note that the size_type is a signed integral type. | |
|         This is because the size can be negative if there are pending pops without corresponding pushes. */ | |
|     typedef std::ptrdiff_t size_type; | |
| 
 | |
|     //! Difference type for iterator | |
|     typedef std::ptrdiff_t difference_type; | |
| 
 | |
|     //! Construct empty queue | |
|     explicit concurrent_bounded_queue(const allocator_type& a = allocator_type()) :  | |
|         concurrent_queue_base_v3( sizeof(T) ), my_allocator( a ) | |
|     { | |
|     } | |
| 
 | |
|     //! Copy constructor | |
|     concurrent_bounded_queue( const concurrent_bounded_queue& src, const allocator_type& a = allocator_type()) :  | |
|         concurrent_queue_base_v3( sizeof(T) ), my_allocator( a ) | |
|     { | |
|         assign( src ); | |
|     } | |
| 
 | |
|     //! [begin,end) constructor | |
|     template<typename InputIterator> | |
|     concurrent_bounded_queue( InputIterator begin, InputIterator end, const allocator_type& a = allocator_type()) : | |
|         concurrent_queue_base_v3( sizeof(T) ), my_allocator( a ) | |
|     { | |
|         for( ; begin != end; ++begin ) | |
|             internal_push_if_not_full(&*begin); | |
|     } | |
| 
 | |
|     //! Destroy queue | |
|     ~concurrent_bounded_queue(); | |
| 
 | |
|     //! Enqueue an item at tail of queue. | |
|     void push( const T& source ) { | |
|         internal_push( &source ); | |
|     } | |
| 
 | |
|     //! Dequeue item from head of queue. | |
|     /** Block until an item becomes available, and then dequeue it. */ | |
|     void pop( T& destination ) { | |
|         internal_pop( &destination ); | |
|     } | |
| 
 | |
| #if TBB_USE_EXCEPTIONS | |
|     //! Abort all pending queue operations | |
|     void abort() { | |
|         internal_abort(); | |
|     } | |
| #endif | |
|  | |
|     //! Enqueue an item at tail of queue if queue is not already full. | |
|     /** Does not wait for queue to become not full. | |
|         Returns true if item is pushed; false if queue was already full. */ | |
|     bool try_push( const T& source ) { | |
|         return internal_push_if_not_full( &source ); | |
|     } | |
| 
 | |
|     //! Attempt to dequeue an item from head of queue. | |
|     /** Does not wait for item to become available. | |
|         Returns true if successful; false otherwise. */ | |
|     bool try_pop( T& destination ) { | |
|         return internal_pop_if_present( &destination ); | |
|     } | |
| 
 | |
|     //! Return number of pushes minus number of pops. | |
|     /** Note that the result can be negative if there are pops waiting for the  | |
|         corresponding pushes.  The result can also exceed capacity() if there  | |
|         are push operations in flight. */ | |
|     size_type size() const {return internal_size();} | |
| 
 | |
|     //! Equivalent to size()<=0. | |
|     bool empty() const {return internal_empty();} | |
| 
 | |
|     //! Maximum number of allowed elements | |
|     size_type capacity() const { | |
|         return my_capacity; | |
|     } | |
| 
 | |
|     //! Set the capacity | |
|     /** Setting the capacity to 0 causes subsequent try_push operations to always fail, | |
|         and subsequent push operations to block forever. */ | |
|     void set_capacity( size_type new_capacity ) { | |
|         internal_set_capacity( new_capacity, sizeof(T) ); | |
|     } | |
| 
 | |
|     //! return allocator object | |
|     allocator_type get_allocator() const { return this->my_allocator; } | |
| 
 | |
|     //! clear the queue. not thread-safe. | |
|     void clear() ; | |
| 
 | |
|     typedef internal::concurrent_queue_iterator<concurrent_bounded_queue,T> iterator; | |
|     typedef internal::concurrent_queue_iterator<concurrent_bounded_queue,const T> const_iterator; | |
| 
 | |
|     //------------------------------------------------------------------------ | |
|     // The iterators are intended only for debugging.  They are slow and not thread safe. | |
|     //------------------------------------------------------------------------ | |
|     iterator unsafe_begin() {return iterator(*this);} | |
|     iterator unsafe_end() {return iterator();} | |
|     const_iterator unsafe_begin() const {return const_iterator(*this);} | |
|     const_iterator unsafe_end() const {return const_iterator();} | |
| 
 | |
| };  | |
| 
 | |
| template<typename T, class A> | |
| concurrent_bounded_queue<T,A>::~concurrent_bounded_queue() { | |
|     clear(); | |
|     internal_finish_clear(); | |
| } | |
| 
 | |
| template<typename T, class A> | |
| void concurrent_bounded_queue<T,A>::clear() { | |
|     while( !empty() ) { | |
|         T value; | |
|         internal_pop_if_present(&value); | |
|     } | |
| } | |
| 
 | |
| namespace deprecated { | |
| 
 | |
| //! A high-performance thread-safe blocking concurrent bounded queue. | |
| /** This is the pre-PPL TBB concurrent queue which support boundedness and blocking semantics. | |
|     Note that method names agree with the PPL-style concurrent queue. | |
|     Multiple threads may each push and pop concurrently. | |
|     Assignment construction is not allowed. | |
|     @ingroup containers */ | |
| template<typename T, class A = cache_aligned_allocator<T> >  | |
| class concurrent_queue: public concurrent_bounded_queue<T,A> { | |
| #if !__TBB_TEMPLATE_FRIENDS_BROKEN | |
|     template<typename Container, typename Value> friend class internal::concurrent_queue_iterator; | |
| #endif  | |
|  | |
| public: | |
|     //! Construct empty queue | |
|     explicit concurrent_queue(const A& a = A()) :  | |
|         concurrent_bounded_queue<T,A>( a ) | |
|     { | |
|     } | |
| 
 | |
|     //! Copy constructor | |
|     concurrent_queue( const concurrent_queue& src, const A& a = A()) :  | |
|         concurrent_bounded_queue<T,A>( src, a ) | |
|     { | |
|     } | |
| 
 | |
|     //! [begin,end) constructor | |
|     template<typename InputIterator> | |
|     concurrent_queue( InputIterator b /*begin*/, InputIterator e /*end*/, const A& a = A()) : | |
|         concurrent_bounded_queue<T,A>( b, e, a ) | |
|     { | |
|     } | |
| 
 | |
|     //! Enqueue an item at tail of queue if queue is not already full. | |
|     /** Does not wait for queue to become not full. | |
|         Returns true if item is pushed; false if queue was already full. */ | |
|     bool push_if_not_full( const T& source ) { | |
|         return this->try_push( source ); | |
|     } | |
| 
 | |
|     //! Attempt to dequeue an item from head of queue. | |
|     /** Does not wait for item to become available. | |
|         Returns true if successful; false otherwise.  | |
|         @deprecated Use try_pop() | |
|         */ | |
|     bool pop_if_present( T& destination ) { | |
|         return this->try_pop( destination ); | |
|     } | |
| 
 | |
|     typedef typename concurrent_bounded_queue<T,A>::iterator iterator; | |
|     typedef typename concurrent_bounded_queue<T,A>::const_iterator const_iterator; | |
|     // | |
|     //------------------------------------------------------------------------ | |
|     // The iterators are intended only for debugging.  They are slow and not thread safe. | |
|     //------------------------------------------------------------------------ | |
|     iterator begin() {return this->unsafe_begin();} | |
|     iterator end() {return this->unsafe_end();} | |
|     const_iterator begin() const {return this->unsafe_begin();} | |
|     const_iterator end() const {return this->unsafe_end();} | |
| };  | |
| 
 | |
| } | |
|      | |
| 
 | |
| #if TBB_DEPRECATED | |
| using deprecated::concurrent_queue; | |
| #else | |
| using strict_ppl::concurrent_queue;     | |
| #endif | |
|  | |
| } // namespace tbb | |
|  | |
| #endif /* __TBB_concurrent_queue_H */
 |