X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=cds%2Fintrusive%2Fsegmented_queue.h;h=95d8e2e459c6b5e60bc495b0b341c058081030fc;hb=df4d0c52b3eff17a49505093870a37ea8c9d565d;hp=e707cc17b2d8ab8ccd7db005dadf242ac63d2a63;hpb=6d378dfd7f930f50b21ed0df9c53e2c764c3ea90;p=libcds.git diff --git a/cds/intrusive/segmented_queue.h b/cds/intrusive/segmented_queue.h index e707cc17..95d8e2e4 100644 --- a/cds/intrusive/segmented_queue.h +++ b/cds/intrusive/segmented_queue.h @@ -1,4 +1,32 @@ -//$$CDS-header$$ +/* + This file is a part of libcds - Concurrent Data Structures library + + (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2017 + + Source code repo: http://github.com/khizmax/libcds/ + Download: http://sourceforge.net/projects/libcds/files/ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ #ifndef CDSLIB_INTRUSIVE_SEGMENTED_QUEUE_H #define CDSLIB_INTRUSIVE_SEGMENTED_QUEUE_H @@ -124,7 +152,7 @@ namespace cds { namespace intrusive { all other \p %segmented_queue::traits members left unchanged. \p Options are: - - \p opt::disposer - the functor used for dispose removed items. + - \p opt::disposer - the functor used to dispose removed items. - \p opt::stat - internal statistics, possible type: \p segmented_queue::stat, \p segmented_queue::empty_stat (the default) - \p opt::item_counter - item counting feature. Note that \p atomicity::empty_item_counetr is not suitable for segmented queue. @@ -228,7 +256,7 @@ namespace cds { namespace intrusive { // cell array is placed here in one continuous memory block // Initializes the segment - segment( size_t nCellCount ) + explicit segment( size_t nCellCount ) // MSVC warning C4355: 'this': used in base member initializer list : cells( reinterpret_cast< cell *>( this + 1 )) , version( 0 ) @@ -296,7 +324,7 @@ namespace cds { namespace intrusive { ~segment_list() { - m_List.clear_and_dispose( gc_segment_disposer() ); + m_List.clear_and_dispose( gc_segment_disposer()); } segment * head( typename gc::Guard& guard ) @@ -315,7 +343,7 @@ namespace cds { namespace intrusive { // The lock should be held cell const * pLastCell = s.cells + quasi_factor(); for ( cell const * pCell = s.cells; pCell < pLastCell; ++pCell ) { - if ( !pCell->data.load( memory_model::memory_order_relaxed ).all() ) + if ( !pCell->data.load( memory_model::memory_order_relaxed ).all()) return false; } return true; @@ -325,7 +353,7 @@ namespace cds { namespace intrusive { // The lock should be held cell const * pLastCell = s.cells + quasi_factor(); for ( cell const * pCell = s.cells; pCell < pLastCell; ++pCell ) { - if ( !pCell->data.load( memory_model::memory_order_relaxed ).bits() ) + if ( !pCell->data.load( memory_model::memory_order_relaxed ).bits()) return false; } return true; @@ -343,15 +371,17 @@ namespace cds { namespace intrusive { if ( !m_List.empty() && ( pTail != &m_List.back() || get_version(pTail) != m_List.back().version )) { m_pTail.store( &m_List.back(), memory_model::memory_order_relaxed ); - return guard.assign( &m_List.back() ); + return guard.assign( &m_List.back()); } - assert( m_List.empty() || populated( m_List.back() )); +# ifdef _DEBUG + assert( m_List.empty() || populated( m_List.back())); +# endif segment * pNew = allocate_segment(); m_Stat.onSegmentCreated(); - if ( m_List.empty() ) + if ( m_List.empty()) m_pHead.store( pNew, memory_model::memory_order_release ); m_List.push_back( *pNew ); m_pTail.store( pNew, memory_model::memory_order_release ); @@ -367,7 +397,7 @@ namespace cds { namespace intrusive { { scoped_lock l( m_Lock ); - if ( m_List.empty() ) { + if ( m_List.empty()) { m_pTail.store( nullptr, memory_model::memory_order_relaxed ); m_pHead.store( nullptr, memory_model::memory_order_relaxed ); return guard.assign( nullptr ); @@ -375,18 +405,20 @@ namespace cds { namespace intrusive { if ( pHead != &m_List.front() || get_version(pHead) != m_List.front().version ) { m_pHead.store( &m_List.front(), memory_model::memory_order_relaxed ); - return guard.assign( &m_List.front() ); + return guard.assign( &m_List.front()); } - assert( exhausted(m_List.front()) ); +# ifdef _DEBUG + assert( exhausted( m_List.front())); +# endif m_List.pop_front(); - if ( m_List.empty() ) { + if ( m_List.empty()) { pRet = guard.assign( nullptr ); m_pTail.store( nullptr, memory_model::memory_order_relaxed ); } else - pRet = guard.assign( &m_List.front() ); + pRet = guard.assign( &m_List.front()); m_pHead.store( pRet, memory_model::memory_order_release ); } @@ -411,7 +443,7 @@ namespace cds { namespace intrusive { segment * allocate_segment() { - return segment_allocator().NewBlock( sizeof(segment) + sizeof(cell) * m_nQuasiFactor, quasi_factor() ); + return segment_allocator().NewBlock( sizeof(segment) + sizeof(cell) * m_nQuasiFactor, quasi_factor()); } static void free_segment( segment * pSegment ) @@ -465,7 +497,7 @@ namespace cds { namespace intrusive { assert( pTailSegment ); } - permutation_generator gen( quasi_factor() ); + permutation_generator gen( quasi_factor()); // First, increment item counter. // We sure that the item will be enqueued @@ -478,7 +510,7 @@ namespace cds { namespace intrusive { do { typename permutation_generator::integer_type i = gen; CDS_DEBUG_ONLY( ++nLoopCount ); - if ( pTailSegment->cells[i].data.load(memory_model::memory_order_relaxed).all() ) { + if ( pTailSegment->cells[i].data.load(memory_model::memory_order_relaxed).all()) { // Cell is not empty, go next m_Stat.onPushPopulated(); } @@ -492,10 +524,10 @@ namespace cds { namespace intrusive { m_Stat.onPush(); return true; } - assert( nullCell.ptr() ); + assert( nullCell.ptr()); m_Stat.onPushContended(); } - } while ( gen.next() ); + } while ( gen.next()); assert( nLoopCount == quasi_factor()); @@ -513,7 +545,7 @@ namespace cds { namespace intrusive { The disposer specified in \p Traits template argument is not called for returned item. You should manually dispose the item: - + \code struct my_disposer { void operator()( foo * p ) { @@ -531,7 +563,7 @@ namespace cds { namespace intrusive { // pItem is not longer needed and can be deleted // Do it via gc::HP::retire cds::gc::HP::template retire< my_disposer >( pItem ); - + \endcode */ value_type * dequeue() { @@ -571,12 +603,12 @@ namespace cds { namespace intrusive { /// Clear the queue /** - The function repeatedly calls \ref dequeue until it returns \p nullptr. + The function repeatedly calls \p dequeue() until it returns \p nullptr. The disposer specified in \p Traits template argument is called for each removed item. */ void clear() { - clear_with( disposer() ); + clear_with( disposer()); } /// Clear the queue @@ -588,9 +620,9 @@ namespace cds { namespace intrusive { void clear_with( Disposer ) { typename gc::Guard itemGuard; - while ( do_dequeue( itemGuard ) ) { - assert( itemGuard.template get() ); - gc::template retire( itemGuard.template get() ); + while ( do_dequeue( itemGuard )) { + assert( itemGuard.template get()); + gc::template retire( itemGuard.template get()); itemGuard.clear(); } } @@ -623,7 +655,7 @@ namespace cds { namespace intrusive { typename gc::Guard segmentGuard; segment * pHeadSegment = m_SegmentList.head( segmentGuard ); - permutation_generator gen( quasi_factor() ); + permutation_generator gen( quasi_factor()); while ( true ) { if ( !pHeadSegment ) { // Queue is empty @@ -642,15 +674,15 @@ namespace cds { namespace intrusive { // In segmented queue the cell cannot be reused // So no loop is needed here to protect the cell item = pHeadSegment->cells[i].data.load( memory_model::memory_order_relaxed ); - itemGuard.assign( item.ptr() ); + itemGuard.assign( item.ptr()); // Check if this cell is empty, which means an element // can be enqueued to this cell in the future - if ( !item.ptr() ) + if ( !item.ptr()) bHadNullValue = true; else { // If the item is not deleted yet - if ( !item.bits() ) { + if ( !item.bits()) { // Try to mark the cell as deleted if ( pHeadSegment->cells[i].data.compare_exchange_strong( item, item | 1, memory_model::memory_order_acquire, atomics::memory_order_relaxed )) @@ -660,13 +692,13 @@ namespace cds { namespace intrusive { return true; } - assert( item.bits() ); + assert( item.bits()); m_Stat.onPopContended(); } } - } while ( gen.next() ); + } while ( gen.next()); - assert( nLoopCount == quasi_factor() ); + assert( nLoopCount == quasi_factor()); // scanning the entire segment without finding a candidate to dequeue // If there was an empty cell, the queue is considered empty