From ba00a98a2b4313beab4bd8e3452fcf2852e6bc51 Mon Sep 17 00:00:00 2001 From: Komarov Daniil Date: Sun, 3 Dec 2017 16:49:15 +0300 Subject: [PATCH 1/9] [2014] Henzinger, Kirsch, Payer, Sezgin, Sokolova Quantitative Relaxation of Concurrent Data Structures --- cds/container/segmented_stack.h | 328 +++++++++++++++++ cds/intrusive/segmented_stack.h | 620 ++++++++++++++++++++++++++++++++ 2 files changed, 948 insertions(+) create mode 100644 cds/container/segmented_stack.h create mode 100644 cds/intrusive/segmented_stack.h diff --git a/cds/container/segmented_stack.h b/cds/container/segmented_stack.h new file mode 100644 index 000000000..725080ef8 --- /dev/null +++ b/cds/container/segmented_stack.h @@ -0,0 +1,328 @@ +/* + 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_CONTAINER_SEGMENTED_STACK_H +#define CDSLIB_CONTAINER_SEGMENTED_STACK_H + +#include +#include // ref +#include +#include + +namespace cds { namespace container { + + /// SegmentedStack -related declarations + namespace segmented_stack { + +# ifdef CDS_DOXYGEN_INVOKED + /// SegmentedStack internal statistics + typedef cds::intrusive::segmented_stack::stat stat; +# else + using cds::intrusive::segmented_stack::stat; +# endif + + /// SegmentedStack empty internal statistics (no overhead) + typedef cds::intrusive::segmented_stack::empty_stat empty_stat; + + /// SegmentedStack default type traits + struct traits { + + /// Item allocator. Default is \ref CDS_DEFAULT_ALLOCATOR + typedef CDS_DEFAULT_ALLOCATOR node_allocator; + + typedef atomicity::item_counter item_counter; + + /// Internal statistics, possible predefined types are \ref stat, \ref empty_stat (the default) + typedef segmented_stack::empty_stat stat; + + /// Memory model, default is opt::v::relaxed_ordering. See cds::opt::memory_model for the full list of possible types + typedef opt::v::relaxed_ordering memory_model; + + /// Alignment of critical data, default is cache line alignment. See cds::opt::alignment option specification + enum { alignment = opt::cache_line_alignment }; + + enum { padding = cds::intrusive::segmented_stack::traits::padding }; + + /// Segment allocator. Default is \ref CDS_DEFAULT_ALLOCATOR + typedef CDS_DEFAULT_ALLOCATOR allocator; + + /// Lock type used to maintain an internal list of allocated segments + typedef cds::sync::spin lock_type; + + /// Random \ref cds::opt::permutation_generator "permutation generator" for sequence [0, quasi_factor) + typedef cds::opt::v::random2_permutation permutation_generator; + }; + + template + struct make_traits { +# ifdef CDS_DOXYGEN_INVOKED + typedef implementation_defined type ; ///< Metafunction result +# else + typedef typename cds::opt::make_options< + typename cds::opt::find_type_traits< traits, Options... >::type + ,Options... + >::type type; +# endif + }; + + } // namespace segmented_stack + + //@cond + namespace details { + + template + struct make_segmented_stack + { + typedef GC gc; + typedef T value_type; + typedef Traits original_type_traits; + + typedef cds::details::Allocator< T, typename original_type_traits::node_allocator > cxx_node_allocator; + struct node_disposer { + void operator()( T * p ) + { + cxx_node_allocator().Delete( p ); + } + }; + + struct intrusive_type_traits: public original_type_traits + { + typedef node_disposer disposer; + }; + + typedef cds::intrusive::SegmentedStack< gc, value_type, intrusive_type_traits > type; + }; + + } // namespace details + //@endcond + + /// Segmented stack + /** @ingroup cds_nonintrusive_stack + + The stack is based on work + - [2014] Henzinger, Kirsch, Payer, Sezgin, Sokolova Quantitative Relaxation of Concurrent Data Structures + + Template parameters: + - \p GC - a garbage collector, possible types are cds::gc::HP, cds::gc::DHP + - \p T - the type of values stored in the stack + - \p Traits - stack type traits, default is \p segmented_stack::traits. + \p segmented_stack::make_traits metafunction can be used to construct your + type traits. + */ + template + class SegmentedStack: +#ifdef CDS_DOXYGEN_INVOKED + public cds::intrusive::SegmentedStack< GC, T, Traits > +#else + public details::make_segmented_stack< GC, T, Traits >::type +#endif + { + //@cond + typedef details::make_segmented_stack< GC, T, Traits > maker; + typedef typename maker::type base_class; + //@endcond + public: + typedef GC gc; ///< Garbage collector + typedef T value_type; ///< type of the value stored in the stack + typedef Traits traits; ///< Stack traits + + typedef typename traits::node_allocator node_allocator; ///< Node allocator + typedef typename base_class::memory_model memory_model; ///< Memory ordering. See cds::opt::memory_model option + typedef typename base_class::item_counter item_counter; ///< Item counting policy, see cds::opt::item_counter option setter + typedef typename base_class::stat stat ; ///< Internal statistics policy + typedef typename base_class::lock_type lock_type ; ///< Type of mutex for maintaining an internal list of allocated segments. + typedef typename base_class::permutation_generator permutation_generator; ///< Random permutation generator for sequence [0, quasi-factor) + + static const size_t c_nHazardPtrCount = base_class::c_nHazardPtrCount ; ///< Count of hazard pointer required for the algorithm + + protected: + //@cond + typedef typename maker::cxx_node_allocator cxx_node_allocator; + typedef std::unique_ptr< value_type, typename maker::node_disposer > scoped_node_ptr; + + static value_type * alloc_node( value_type const& v ) + { + return cxx_node_allocator().New( v ); + } + + static value_type * alloc_node() + { + return cxx_node_allocator().New(); + } + + template + static value_type * alloc_node_move( Args&&... args ) + { + return cxx_node_allocator().MoveNew( std::forward( args )... ); + } + //@endcond + + public: + /// Initializes the empty stack + SegmentedStack( + size_t nQuasiFactor ///< Quasi factor. If it is not a power of 2 it is rounded up to nearest power of 2. Minimum is 2. + ) + : base_class( nQuasiFactor ) + {} + + /// Clears the stack and deletes all internal data + ~SegmentedStack() + {} + + bool _push( value_type const& val ) + { + scoped_node_ptr p( alloc_node(val)); + if ( base_class::_push( *p )) { + p.release(); + return true; + } + return false; + } + + /// Inserts a new element at last segment of the stack, move semantics + bool _push( value_type&& val ) + { + scoped_node_ptr p( alloc_node_move( std::move( val ))); + if ( base_class::_push( *p )) { + p.release(); + return true; + } + return false; + } + + template + bool _push_with( Func f ) + { + scoped_node_ptr p( alloc_node()); + f( *p ); + if ( base_class::_push( *p )) { + p.release(); + return true; + } + return false; + } + + + /// Synonym for \p _push ( value_type const& ) member function + bool push( value_type const& val ) + { + return _push( val ); + } + + /// Synonym for \p _push( value_type&& ) member function + bool push( value_type&& val ) + { + return _push( std::move( val )); + } + + /// Synonym for \p _push_with() member function + template + bool push_with( Func f ) + { + return _push_with( f ); + } + + template + bool emplace( Args&&... args ) + { + scoped_node_ptr p( alloc_node_move( std::forward(args)... )); + if ( base_class::_push( *p )) { + p.release(); + return true; + } + return false; + } + + /// Pop a value from the stack + bool _pop( value_type& dest ) + { + return _pop_with( [&dest]( value_type& src ) { dest = std::move( src );}); + } + + /// Pop a value using a functor + template + bool _pop_with( Func f ) + { + value_type * p = base_class::_pop(); + if ( p ) { + f( *p ); + gc::template retire< typename maker::node_disposer >( p ); + return true; + } + return false; + } + + /// Synonym for \p _pop_with() function + template + bool pop_with( Func f ) + { + return _pop_with( f ); + } + + /// Synonym for \p _pop() function + bool pop( value_type& dest ) + { + return _pop( dest ); + } + + /// Checks if the stack is empty + bool empty() const + { + return base_class::empty(); + } + + /// Clear the stack + void clear() + { + base_class::clear(); + } + + /// Returns stack's item count + size_t size() const + { + return base_class::size(); + } + + /// Returns reference to internal statistics + const stat& statistics() const + { + return base_class::statistics(); + } + + /// Returns quasi factor, a power-of-two number + size_t quasi_factor() const + { + return base_class::quasi_factor(); + } + }; + +}} // namespace cds::container + +#endif // #ifndef CDSLIB_CONTAINER_SEGMENTED_STACK_H diff --git a/cds/intrusive/segmented_stack.h b/cds/intrusive/segmented_stack.h new file mode 100644 index 000000000..dfc3b2744 --- /dev/null +++ b/cds/intrusive/segmented_stack.h @@ -0,0 +1,620 @@ +/* + 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_STACK_H +#define CDSLIB_INTRUSIVE_SEGMENTED_STACK_H + +#include +#include +#include +#include +#include +#include + +#include + +#if CDS_COMPILER == CDS_COMPILER_MSVC +# pragma warning( push ) +# pragma warning( disable: 4355 ) // warning C4355: 'this' : used in base member initializer list +#endif + +namespace cds { namespace intrusive { + + /// SegmentedStack -related declarations + namespace segmented_stack { + + /// SegmentedStack internal statistics. May be used for debugging or profiling + template + struct stat + { + typedef Counter counter_type; ///< Counter type + + counter_type m_nPush; ///< Push count + counter_type m_nPushPopulated; ///< Number of attempts to push to populated (non-empty) cell + counter_type m_nPushContended; ///< Number of failed CAS when pushing + counter_type m_nPop; ///< Pop count + counter_type m_nPopEmpty; ///< Number of poping from empty stack + counter_type m_nPopContended; ///< Number of failed CAS when popping + + counter_type m_nCreateSegmentReq; ///< Number of request to create new segment + counter_type m_nDeleteSegmentReq; ///< Number to request to delete segment + counter_type m_nSegmentCreated; ///< Number of created segments + counter_type m_nSegmentDeleted; ///< Number of deleted segments + + //@cond + void onPush() { ++m_nPush; } + void onPushPopulated() { ++m_nPushPopulated; } + void onPushContended() { ++m_nPushContended; } + void onPop() { ++m_nPop; } + void onPopEmpty() { ++m_nPopEmpty; } + void onPopContended() { ++m_nPopContended; } + void onCreateSegmentReq() { ++m_nCreateSegmentReq; } + void onDeleteSegmentReq() { ++m_nDeleteSegmentReq; } + void onSegmentCreated() { ++m_nSegmentCreated; } + void onSegmentDeleted() { ++m_nSegmentDeleted; } + //@endcond + }; + + /// Dummy SegmentedStack statistics, no overhead + struct empty_stat { + //@cond + void onPush() const {} + void onPushPopulated() const {} + void onPushContended() const {} + void onPop() const {} + void onPopEmpty() const {} + void onPopContended() const {} + void onCreateSegmentReq() const {} + void onDeleteSegmentReq() const {} + void onSegmentCreated() const {} + void onSegmentDeleted() const {} + //@endcond + }; + + /// SegmentedStack default traits + struct traits { + /// Element disposer that is called when the item to be pop. Default is opt::v::empty_disposer (no disposer) + typedef opt::v::empty_disposer disposer; + + /// Item counter, default is atomicity::item_counter + /** + The item counting is an essential part of segmented stack algorithm. + The \p empty() member function is based on checking size() == 0. + Therefore, dummy item counter like atomicity::empty_item_counter is not the proper counter. + */ + typedef atomicity::item_counter item_counter; + + /// Internal statistics, possible predefined types are \ref stat, \ref empty_stat (the default) + typedef segmented_stack::empty_stat stat; + + /// Memory model, default is opt::v::relaxed_ordering. See cds::opt::memory_model for the full list of possible types + typedef opt::v::relaxed_ordering memory_model; + + /// Alignment of critical data, default is cache line alignment. See cds::opt::alignment option specification + enum { alignment = opt::cache_line_alignment }; + + /// Padding of segment data, default is no special padding + /** + The segment is just an array of atomic data pointers, + so, the high load leads to false sharing and performance degradation. + A padding of segment data can eliminate false sharing issue. + On the other hand, the padding leads to increase segment size. + */ + enum { padding = opt::no_special_padding }; + + /// Segment allocator. Default is \ref CDS_DEFAULT_ALLOCATOR + typedef CDS_DEFAULT_ALLOCATOR allocator; + + /// Lock type used to maintain an internal list of allocated segments + typedef cds::sync::spin lock_type; + + /// Random \ref cds::opt::permutation_generator "permutation generator" for sequence [0, quasi_factor) + typedef cds::opt::v::random2_permutation permutation_generator; + }; + + template + struct make_traits { +# ifdef CDS_DOXYGEN_INVOKED + typedef implementation_defined type ; ///< Metafunction result +# else + typedef typename cds::opt::make_options< + typename cds::opt::find_type_traits< traits, Options... >::type + ,Options... + >::type type; +# endif + }; + } // namespace segmented_stack + + /// Segmented stack + /** @ingroup cds_intrusive_stack + + The stack is based on work + - [2014] Henzinger, Kirsch, Payer, Sezgin, Sokolova Quantitative Relaxation of Concurrent Data Structures + + Template parameters: + - \p GC - a garbage collector, possible types are cds::gc::HP, cds::gc::DHP + - \p T - the type of values stored in the stack + - \p Traits - stack type traits, default is \p segmented_stack::traits. + \p segmented_stack::make_traits metafunction can be used to construct the + type traits. + + */ + template + class SegmentedStack + { + public: + typedef GC gc; ///< Garbage collector + typedef T value_type; ///< type of the value stored in the stack + typedef Traits traits; ///< Stack traits + + typedef typename traits::disposer disposer ; ///< value disposer, called only in \p clear() when the element to be pop + typedef typename traits::allocator allocator; ///< Allocator maintaining the segments + typedef typename traits::memory_model memory_model; ///< Memory ordering. See cds::opt::memory_model option + typedef typename traits::item_counter item_counter; ///< Item counting policy, see cds::opt::item_counter option setter + typedef typename traits::stat stat; ///< Internal statistics policy + typedef typename traits::lock_type lock_type; ///< Type of mutex for maintaining an internal list of allocated segments. + typedef typename traits::permutation_generator permutation_generator; ///< Random permutation generator for sequence [0, quasi-factor) + + static const size_t c_nHazardPtrCount = 2 ; ///< Count of hazard pointer required for the algorithm + + protected: + //@cond + // Segment cell. LSB is used as deleted mark + typedef cds::details::marked_ptr< value_type, 1 > regular_cell; + typedef atomics::atomic< regular_cell > atomic_cell; + typedef typename cds::opt::details::apply_padding< atomic_cell, traits::padding >::type cell; + + // Segment + struct segment: public boost::intrusive::slist_base_hook<> + { + cell * cells; // Cell array of size \ref m_nQuasiFactor + size_t version; // version tag (ABA prevention tag) + // cell array is placed here in one continuous memory block + + // Initializes the segment + explicit segment( size_t nCellCount ) + // MSVC warning C4355: 'this': used in base member initializer list + : cells( reinterpret_cast< cell *>( this + 1 )) + , version( 0 ) + { + init( nCellCount ); + } + + segment() = delete; + + void init( size_t nCellCount ) + { + cell * pLastCell = cells + nCellCount; + for ( cell* pCell = cells; pCell < pLastCell; ++pCell ) + pCell->data.store( regular_cell(), atomics::memory_order_relaxed ); + atomics::atomic_thread_fence( memory_model::memory_order_release ); + } + }; + + typedef typename opt::details::alignment_setter< atomics::atomic, traits::alignment >::type aligned_segment_ptr; + //@endcond + + protected: + //@cond + class segment_list + { + typedef boost::intrusive::slist< segment, boost::intrusive::cache_last< true > > list_impl; + typedef std::unique_lock< lock_type > scoped_lock; + + aligned_segment_ptr m_pHead; + aligned_segment_ptr m_pTail; + + list_impl m_List; + mutable lock_type m_Lock; + size_t const m_nQuasiFactor; + stat& m_Stat; + + private: + struct segment_disposer + { + void operator()( segment * pSegment ) + { + assert( pSegment != nullptr ); + free_segment( pSegment ); + } + }; + + struct gc_segment_disposer + { + void operator()( segment * pSegment ) + { + assert( pSegment != nullptr ); + retire_segment( pSegment ); + } + }; + + public: + segment_list( size_t nQuasiFactor, stat& st ) + : m_pHead( nullptr ) + , m_pTail( nullptr ) + , m_nQuasiFactor( nQuasiFactor ) + , m_Stat( st ) + { + assert( cds::beans::is_power2( nQuasiFactor )); + } + + ~segment_list() + { + m_List.clear_and_dispose( gc_segment_disposer()); + } + + segment * head( typename gc::Guard& guard ) + { + return guard.protect( m_pHead ); + } + + segment * tail( typename gc::Guard& guard ) + { + return guard.protect( m_pTail ); + } + +# ifdef _DEBUG + bool populated( segment const& s ) const + { + // 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()) + return false; + } + return true; + } + bool exhausted( segment const& s ) const + { + // 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()) + return false; + } + return true; + } +# endif + + segment * create_head( segment * pHead, typename gc::Guard& guard ) + { + // pHead is guarded by GC + + m_Stat.onCreateSegmentReq(); + + scoped_lock l( m_Lock ); + + if ( !m_List.empty() && (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()); + } + +# ifdef _DEBUG + assert( m_List.empty() || populated( m_List.front())); +# endif + + segment * pNew = allocate_segment(); + m_Stat.onSegmentCreated(); + + if ( m_List.empty()) + m_pTail.store(pNew, memory_model::memory_order_release); + m_List.push_front( *pNew ); + m_pHead.store(pNew, memory_model::memory_order_release); + return guard.assign( pNew ); + } + + segment * remove_head( segment * pHead, typename gc::Guard& guard ) + { + // pHead is guarded by GC + m_Stat.onDeleteSegmentReq(); + + segment * pRet; + { + scoped_lock l( m_Lock ); + + 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 ); + } + + 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()); + } + +# ifdef _DEBUG + assert( exhausted( m_List.front())); +# endif + + m_List.pop_front(); + if ( m_List.empty()) { + pRet = guard.assign( nullptr ); + m_pTail.store( nullptr, memory_model::memory_order_relaxed ); + } + else + pRet = guard.assign( &m_List.front()); + m_pHead.store( pRet, memory_model::memory_order_release ); + } + + retire_segment( pHead ); + m_Stat.onSegmentDeleted(); + + return pRet; + } + + size_t quasi_factor() const + { + return m_nQuasiFactor; + } + + private: + typedef cds::details::Allocator< segment, allocator > segment_allocator; + + static size_t get_version( segment * pSegment ) + { + return pSegment ? pSegment->version : 0; + } + + segment * allocate_segment() + { + return segment_allocator().NewBlock( sizeof(segment) + sizeof(cell) * m_nQuasiFactor, quasi_factor()); + } + + static void free_segment( segment * pSegment ) + { + segment_allocator().Delete( pSegment ); + } + + static void retire_segment( segment * pSegment ) + { + gc::template retire( pSegment ); + } + }; + //@endcond + + protected: + segment_list m_SegmentList; ///< List of segments + + item_counter m_ItemCounter; ///< Item counter + stat m_Stat; ///< Internal statistics + + public: + /// Initializes the empty stack + SegmentedStack( + size_t nQuasiFactor ///< Quasi factor. If it is not a power of 2 it is rounded up to nearest power of 2. Minimum is 2. + ) + : m_SegmentList( cds::beans::ceil2(nQuasiFactor), m_Stat ) + { + static_assert( (!std::is_same< item_counter, cds::atomicity::empty_item_counter >::value), + "cds::atomicity::empty_item_counter is not supported for SegmentedStack" + ); + assert( m_SegmentList.quasi_factor() > 1 ); + } + + /// Clears the stack and deletes all internal data + ~SegmentedStack() + { + clear(); + } + + /// Inserts a new element at last segment of the stack + bool _push( value_type& val ) + { + // LSB is used as a flag in marked pointer + assert( (reinterpret_cast( &val ) & 1) == 0 ); + + typename gc::Guard segmentGuard; + segment * pHeadSegment = m_SegmentList.head( segmentGuard ); + if ( !pHeadSegment) { + // no segments, create the new one + pHeadSegment = m_SegmentList.create_head(pHeadSegment, segmentGuard ); + assert(pHeadSegment); + } + + permutation_generator gen( quasi_factor()); + + ++m_ItemCounter; + + while ( true ) { + CDS_DEBUG_ONLY( size_t nLoopCount = 0); + do { + typename permutation_generator::integer_type i = gen; + CDS_DEBUG_ONLY( ++nLoopCount ); + if (pHeadSegment->cells[i].data.load(memory_model::memory_order_relaxed).all()) { + // Cell is not empty, go next + m_Stat.onPushPopulated(); + } + else { + // Empty cell found, try to push here + regular_cell nullCell; + if (pHeadSegment->cells[i].data.compare_exchange_strong( nullCell, regular_cell( &val ), + memory_model::memory_order_release, atomics::memory_order_relaxed )) + { + // Ok to push item + m_Stat.onPush(); + return true; + } + assert( nullCell.ptr()); + m_Stat.onPushContended(); + } + } while ( gen.next()); + + assert( nLoopCount == quasi_factor()); + + // No available position, create a new segment + pHeadSegment = m_SegmentList.create_head(pHeadSegment, segmentGuard ); + + // Get new permutation + gen.reset(); + } + } + + value_type * _pop() + { + typename gc::Guard itemGuard; + if ( do_pop( itemGuard )) { + value_type * pVal = itemGuard.template get(); + assert( pVal ); + return pVal; + } + return nullptr; + + } + + /// Synonym for \p _push(value_type&) member function + bool push( value_type& val ) + { + return _push( val ); + } + + /// Synonym for \p pop() member function + value_type * pop() + { + return _pop(); + } + + /// Checks if the stack is empty + bool empty() const + { + return size() == 0; + } + + /// Clear the stack + void clear() + { + clear_with( disposer()); + } + + template + void clear_with( Disposer ) + { + typename gc::Guard itemGuard; + while ( do_pop( itemGuard )) { + assert( itemGuard.template get()); + gc::template retire( itemGuard.template get()); + itemGuard.clear(); + } + } + + /// Returns stack's item count + size_t size() const + { + return m_ItemCounter.value(); + } + + /// Returns reference to internal statistics + const stat& statistics() const + { + return m_Stat; + } + + /// Returns quasi factor, a power-of-two number + size_t quasi_factor() const + { + return m_SegmentList.quasi_factor(); + } + + protected: + //@cond + bool do_pop( typename gc::Guard& itemGuard ) + { + typename gc::Guard segmentGuard; + segment * pHeadSegment = m_SegmentList.head( segmentGuard ); + + permutation_generator gen( quasi_factor()); + while ( true ) { + if ( !pHeadSegment ) { + // Stack is empty + m_Stat.onPopEmpty(); + return false; + } + + bool bHadNullValue = false; + regular_cell item; + CDS_DEBUG_ONLY( size_t nLoopCount = 0 ); + do { + typename permutation_generator::integer_type i = gen; + CDS_DEBUG_ONLY( ++nLoopCount ); + + // Guard the item + // In segmented stack 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()); + + // Check if this cell is empty, which means an element + // can be pushed to this cell in the future + if ( !item.ptr()) + bHadNullValue = true; + else { + // If the item is not deleted yet + 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 )) + { + --m_ItemCounter; + m_Stat.onPop(); + + return true; + } + assert( item.bits()); + m_Stat.onPopContended(); + } + } + } while ( gen.next()); + + assert( nLoopCount == quasi_factor()); + + // scanning the entire segment without finding a candidate to pop + // If there was an empty cell, the stack is considered empty + if ( bHadNullValue ) { + m_Stat.onPopEmpty(); + return false; + } + + // All nodes have been poped, we can safely remove the first segment + pHeadSegment = m_SegmentList.remove_head( pHeadSegment, segmentGuard ); + + // Get new permutation + gen.reset(); + } + } + //@endcond + }; +}} // namespace cds::intrusive + +#if CDS_COMPILER == CDS_COMPILER_MSVC +# pragma warning( pop ) +#endif + +#endif // #ifndef CDSLIB_INTRUSIVE_SEGMENTED_STACK_H From e3ccdbc72b250049e8c5b92bbba02563e9114f88 Mon Sep 17 00:00:00 2001 From: Komarov Daniil Date: Wed, 13 Dec 2017 18:34:45 +0300 Subject: [PATCH 2/9] added unit tests --- test/stress/stack/intrusive_stack_type.h | 55 ++++++ test/stress/stack/stack_type.h | 55 ++++++ test/unit/stack/CMakeLists.txt | 4 + .../stack/intrusive_segmented_stack_dhp.cpp | 166 ++++++++++++++++ .../stack/intrusive_segmented_stack_hp.cpp | 166 ++++++++++++++++ test/unit/stack/segmented_stack_dhp.cpp | 114 +++++++++++ test/unit/stack/segmented_stack_hp.cpp | 114 +++++++++++ .../stack/test_intrusive_segmented_stack.h | 183 ++++++++++++++++++ test/unit/stack/test_segmented_stack.h | 97 ++++++++++ 9 files changed, 954 insertions(+) create mode 100644 test/unit/stack/intrusive_segmented_stack_dhp.cpp create mode 100644 test/unit/stack/intrusive_segmented_stack_hp.cpp create mode 100644 test/unit/stack/segmented_stack_dhp.cpp create mode 100644 test/unit/stack/segmented_stack_hp.cpp create mode 100644 test/unit/stack/test_intrusive_segmented_stack.h create mode 100644 test/unit/stack/test_segmented_stack.h diff --git a/test/stress/stack/intrusive_stack_type.h b/test/stress/stack/intrusive_stack_type.h index 87cdba5cd..13a45406d 100644 --- a/test/stress/stack/intrusive_stack_type.h +++ b/test/stress/stack/intrusive_stack_type.h @@ -33,6 +33,7 @@ #include #include +#include #include #include @@ -369,6 +370,49 @@ namespace istack { typedef details::StdStack< T, std::stack< T*, std::list >, std::mutex > StdStack_List_Mutex; typedef details::StdStack< T, std::stack< T*, std::list >, cds::sync::spin > StdStack_List_Spin; + // SegmentedStack + class traits_SegmentedStack_spin_stat : + public cds::intrusive::segmented_stack::make_traits< + cds::opt::stat< cds::intrusive::segmented_stack::stat<> > + >::type + {}; + class traits_SegmentedStack_spin_padding : + public cds::intrusive::segmented_stack::make_traits< + cds::opt::padding< cds::opt::cache_line_padding > + >::type + {}; + class traits_SegmentedStack_mutex_stat : + public cds::intrusive::segmented_stack::make_traits< + cds::opt::stat< cds::intrusive::segmented_stack::stat<> > + , cds::opt::lock_type< std::mutex > + >::type + {}; + class traits_SegmentedStack_mutex : + public cds::intrusive::segmented_stack::make_traits< + cds::opt::lock_type< std::mutex > + >::type + {}; + class traits_SegmentedStack_mutex_padding : + public cds::intrusive::segmented_stack::make_traits< + cds::opt::lock_type< std::mutex > + , cds::opt::padding< cds::opt::cache_line_padding > + >::type + {}; + + typedef cds::intrusive::SegmentedStack< cds::gc::HP, T > SegmentedStack_HP_spin; + typedef cds::intrusive::SegmentedStack< cds::gc::HP, T, traits_SegmentedStack_spin_padding > SegmentedStack_HP_spin_padding; + typedef cds::intrusive::SegmentedStack< cds::gc::HP, T, traits_SegmentedStack_spin_stat > SegmentedStack_HP_spin_stat; + typedef cds::intrusive::SegmentedStack< cds::gc::HP, T, traits_SegmentedStack_mutex > SegmentedStack_HP_mutex; + typedef cds::intrusive::SegmentedStack< cds::gc::HP, T, traits_SegmentedStack_mutex_padding > SegmentedStack_HP_mutex_padding; + typedef cds::intrusive::SegmentedStack< cds::gc::HP, T, traits_SegmentedStack_mutex_stat > SegmentedStack_HP_mutex_stat; + + typedef cds::intrusive::SegmentedStack< cds::gc::DHP, T > SegmentedStack_DHP_spin; + typedef cds::intrusive::SegmentedStack< cds::gc::DHP, T, traits_SegmentedStack_spin_padding > SegmentedStack_DHP_spin_padding; + typedef cds::intrusive::SegmentedStack< cds::gc::DHP, T, traits_SegmentedStack_spin_stat > SegmentedStack_DHP_spin_stat; + typedef cds::intrusive::SegmentedStack< cds::gc::DHP, T, traits_SegmentedStack_mutex > SegmentedStack_DHP_mutex; + typedef cds::intrusive::SegmentedStack< cds::gc::DHP, T, traits_SegmentedStack_mutex_padding > SegmentedStack_DHP_mutex_padding; + typedef cds::intrusive::SegmentedStack< cds::gc::DHP, T, traits_SegmentedStack_mutex_stat > SegmentedStack_DHP_mutex_stat; + }; } // namespace istack @@ -456,6 +500,17 @@ namespace cds_test { CDSSTRESS_Stack_F( test_fixture, Elimination_DHP_dyn ) \ CDSSTRESS_Stack_F( test_fixture, Elimination_DHP_dyn_stat ) +#define CDSSTRESS_SegmentedStack( test_fixture ) \ + CDSSTRESS_Stack_F( test_fixture, SegmentedStack_HP_spin ) \ + CDSSTRESS_Stack_F( test_fixture, SegmentedStack_HP_spin_padding ) \ + CDSSTRESS_Stack_F( test_fixture, SegmentedStack_HP_spin_stat ) \ + CDSSTRESS_Stack_F( test_fixture, SegmentedStack_HP_mutex ) \ + CDSSTRESS_Stack_F( test_fixture, SegmentedStack_HP_mutex_stat ) \ + CDSSTRESS_Stack_F( test_fixture, SegmentedStack_DHP_spin ) \ + CDSSTRESS_Stack_F( test_fixture, SegmentedStack_DHP_spin_stat ) \ + CDSSTRESS_Stack_F( test_fixture, SegmentedStack_DHP_mutex ) \ + CDSSTRESS_Stack_F( test_fixture, SegmentedStack_DHP_mutex_stat ) \ + #define CDSSTRESS_FCStack_slist( test_fixture ) \ CDSSTRESS_Stack_F( test_fixture, FCStack_slist ) \ CDSSTRESS_Stack_F( test_fixture, FCStack_slist_stat ) \ diff --git a/test/stress/stack/stack_type.h b/test/stress/stack/stack_type.h index a9a7350d0..ff5e3fcaa 100644 --- a/test/stress/stack/stack_type.h +++ b/test/stress/stack/stack_type.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -328,6 +329,49 @@ namespace stack { typedef cds::container::TreiberStack< cds::gc::HP, T, traits_Elimination_exp > Elimination_HP_exp; typedef cds::container::TreiberStack< cds::gc::DHP, T, traits_Elimination_exp > Elimination_DHP_exp; + // SegmentedStack + class traits_SegmentedStack_spin_stat : + public cds::container::segmented_stack::make_traits< + cds::opt::stat< cds::intrusive::segmented_stack::stat<> > + >::type + {}; + class traits_SegmentedStack_spin_padding : + public cds::container::segmented_stack::make_traits< + cds::opt::padding< cds::opt::cache_line_padding > + >::type + {}; + class traits_SegmentedStack_mutex_stat : + public cds::container::segmented_stack::make_traits< + cds::opt::stat< cds::intrusive::segmented_stack::stat<> > + , cds::opt::lock_type< std::mutex > + >::type + {}; + class traits_SegmentedStack_mutex : + public cds::container::segmented_stack::make_traits< + cds::opt::lock_type< std::mutex > + >::type + {}; + class traits_SegmentedStack_mutex_padding : + public cds::container::segmented_stack::make_traits< + cds::opt::lock_type< std::mutex > + , cds::opt::padding< cds::opt::cache_line_padding > + >::type + {}; + + typedef cds::container::SegmentedStack< cds::gc::HP, T > SegmentedStack_HP_spin; + typedef cds::container::SegmentedStack< cds::gc::HP, T, traits_SegmentedStack_spin_padding > SegmentedStack_HP_spin_padding; + typedef cds::container::SegmentedStack< cds::gc::HP, T, traits_SegmentedStack_spin_stat > SegmentedStack_HP_spin_stat; + typedef cds::container::SegmentedStack< cds::gc::HP, T, traits_SegmentedStack_mutex > SegmentedStack_HP_mutex; + typedef cds::container::SegmentedStack< cds::gc::HP, T, traits_SegmentedStack_mutex_padding > SegmentedStack_HP_mutex_padding; + typedef cds::container::SegmentedStack< cds::gc::HP, T, traits_SegmentedStack_mutex_stat > SegmentedStack_HP_mutex_stat; + + typedef cds::container::SegmentedStack< cds::gc::DHP, T > SegmentedStack_DHP_spin; + typedef cds::container::SegmentedStack< cds::gc::DHP, T, traits_SegmentedStack_spin_padding > SegmentedStack_DHP_spin_padding; + typedef cds::container::SegmentedStack< cds::gc::DHP, T, traits_SegmentedStack_spin_stat > SegmentedStack_DHP_spin_stat; + typedef cds::container::SegmentedStack< cds::gc::DHP, T, traits_SegmentedStack_mutex > SegmentedStack_DHP_mutex; + typedef cds::container::SegmentedStack< cds::gc::DHP, T, traits_SegmentedStack_mutex_padding > SegmentedStack_DHP_mutex_padding; + typedef cds::container::SegmentedStack< cds::gc::DHP, T, traits_SegmentedStack_mutex_stat > SegmentedStack_DHP_mutex_stat; + }; // FCStack typedef cds::container::FCStack< T > FCStack_deque; @@ -528,6 +572,17 @@ namespace cds_test { CDSSTRESS_EliminationStack_F( test_fixture, Elimination_DHP_dyn ) \ CDSSTRESS_EliminationStack_F( test_fixture, Elimination_DHP_dyn_stat) +#define CDSSTRESS_SegmentedStack( test_fixture ) \ + CDSSTRESS_Stack_F( test_fixture, SegmentedStack_HP_spin ) \ + CDSSTRESS_Stack_F( test_fixture, SegmentedStack_HP_spin_padding ) \ + CDSSTRESS_Stack_F( test_fixture, SegmentedStack_HP_spin_stat ) \ + CDSSTRESS_Stack_F( test_fixture, SegmentedStack_HP_mutex ) \ + CDSSTRESS_Stack_F( test_fixture, SegmentedStack_HP_mutex_stat ) \ + CDSSTRESS_Stack_F( test_fixture, SegmentedStack_DHP_spin ) \ + CDSSTRESS_Stack_F( test_fixture, SegmentedStack_DHP_spin_stat ) \ + CDSSTRESS_Stack_F( test_fixture, SegmentedStack_DHP_mutex ) \ + CDSSTRESS_Stack_F( test_fixture, SegmentedStack_DHP_mutex_stat ) \ + #define CDSSTRESS_FCStack( test_fixture ) \ CDSSTRESS_Stack_F( test_fixture, FCStack_deque ) \ CDSSTRESS_Stack_F( test_fixture, FCStack_deque_mutex ) \ diff --git a/test/unit/stack/CMakeLists.txt b/test/unit/stack/CMakeLists.txt index 2a35488cc..c1489a9a0 100644 --- a/test/unit/stack/CMakeLists.txt +++ b/test/unit/stack/CMakeLists.txt @@ -8,6 +8,10 @@ set(CDSGTEST_STACK_SOURCES intrusive_treiber_stack_hp.cpp treiber_stack_dhp.cpp treiber_stack_hp.cpp + segmented_stack_hp.cpp + segmented_queue_dhp.cpp + intrusive_segmented_queue_hp.cpp + intrusive_segmented_queue_dhp.cpp ) include_directories( diff --git a/test/unit/stack/intrusive_segmented_stack_dhp.cpp b/test/unit/stack/intrusive_segmented_stack_dhp.cpp new file mode 100644 index 000000000..a82872773 --- /dev/null +++ b/test/unit/stack/intrusive_segmented_stack_dhp.cpp @@ -0,0 +1,166 @@ +/* + 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. +*/ + +#include "test_intrusive_segmented_stack.h" + +#include +#include +#include + +namespace { + namespace ci = cds::intrusive; + typedef cds::gc::DHP gc_type; + + class IntrusiveSegmentedStack_DHP : public cds_test::intrusive_segmented_stack + { + typedef cds_test::intrusive_segmented_stack base_class; + + protected: + static const size_t c_QuasiFactor = 15; + + void SetUp() + { + typedef ci::SegmentedStack< gc_type, item > stack_type; + + cds::gc::dhp::smr::construct( stack_type::c_nHazardPtrCount ); + cds::threading::Manager::attachThread(); + } + + void TearDown() + { + cds::threading::Manager::detachThread(); + cds::gc::dhp::smr::destruct(); + } + + template + void check_array( V& arr ) + { + for ( size_t i = 0; i < arr.size(); ++i ) { + EXPECT_EQ( arr[i].nDisposeCount, 2u ); + EXPECT_EQ( arr[i].nDispose2Count, 1u ); + } + } + }; + + TEST_F( IntrusiveSegmentedStack_DHP, defaulted ) + { + struct stack_traits : public cds::intrusive::segmented_stack::traits + { + typedef Disposer disposer; + }; + typedef cds::intrusive::SegmentedStack< gc_type, item, stack_traits > stack_type; + + std::vector arr; + { + stack_type s( c_QuasiFactor ); + test( s, arr ); + } + stack_type::gc::force_dispose(); + check_array( arr ); + } + + TEST_F( IntrusiveSegmentedStack_DHP, mutex ) + { + struct stack_traits : public + cds::intrusive::segmented_stack::make_traits < + cds::intrusive::opt::disposer< Disposer > + ,cds::opt::lock_type < std::mutex > + > ::type + {}; + typedef cds::intrusive::SegmentedStack< gc_type, item, stack_traits > stack_type; + + std::vector arr; + { + stack_type s( c_QuasiFactor ); + test( s, arr ); + } + stack_type::gc::force_dispose(); + check_array( arr ); + } + + TEST_F( IntrusiveSegmentedStack_DHP, shuffle ) + { + typedef cds::intrusive::SegmentedStack< gc_type, item, + cds::intrusive::segmented_stack::make_traits< + cds::intrusive::opt::disposer< Disposer > + ,cds::opt::item_counter< cds::atomicity::item_counter > + ,cds::opt::permutation_generator< cds::opt::v::random_shuffle_permutation<> > + >::type + > stack_type; + + std::vector arr; + { + stack_type s( c_QuasiFactor ); + test( s, arr ); + } + stack_type::gc::force_dispose(); + check_array( arr ); + } + + TEST_F( IntrusiveSegmentedStack_DHP, padding ) + { + struct stack_traits : public cds::intrusive::segmented_stack::traits + { + typedef Disposer disposer; + enum { padding = cds::opt::cache_line_padding }; + typedef ci::segmented_stack::stat<> stat; + }; + typedef cds::intrusive::SegmentedStack< gc_type, item, stack_traits > stack_type; + + std::vector arr; + { + stack_type s( c_QuasiFactor ); + test( s, arr ); + } + stack_type::gc::force_dispose(); + check_array( arr ); + } + + TEST_F( IntrusiveSegmentedStack_DHP, bigdata_padding ) + { + struct stack_traits : public cds::intrusive::segmented_stack::traits + { + typedef Disposer disposer; + enum { padding = cds::opt::cache_line_padding | cds::opt::padding_tiny_data_only }; + typedef cds::opt::v::sequential_consistent memory_model; + }; + typedef cds::intrusive::SegmentedStack< gc_type, big_item, stack_traits > stack_type; + + std::vector arr; + { + stack_type s( c_QuasiFactor ); + test( s, arr ); + } + stack_type::gc::force_dispose(); + check_array( arr ); + } + +} // namespace + diff --git a/test/unit/stack/intrusive_segmented_stack_hp.cpp b/test/unit/stack/intrusive_segmented_stack_hp.cpp new file mode 100644 index 000000000..1eeb37d54 --- /dev/null +++ b/test/unit/stack/intrusive_segmented_stack_hp.cpp @@ -0,0 +1,166 @@ +/* + 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. +*/ + +#include "test_intrusive_segmented_stack.h" + +#include +#include +#include + +namespace { + namespace ci = cds::intrusive; + typedef cds::gc::HP gc_type; + + class IntrusiveSegmentedStack_HP : public cds_test::intrusive_segmented_stack + { + typedef cds_test::intrusive_segmented_stack base_class; + + protected: + static const size_t c_QuasiFactor = 15; + + void SetUp() + { + typedef ci::SegmentedStack< gc_type, item > stack_type; + + cds::gc::hp::GarbageCollector::Construct( stack_type::c_nHazardPtrCount, 1, 16 ); + cds::threading::Manager::attachThread(); + } + + void TearDown() + { + cds::threading::Manager::detachThread(); + cds::gc::hp::GarbageCollector::Destruct( true ); + } + + template + void check_array( V& arr ) + { + for ( size_t i = 0; i < arr.size(); ++i ) { + EXPECT_EQ( arr[i].nDisposeCount, 2u ); + EXPECT_EQ( arr[i].nDispose2Count, 1u ); + } + } + }; + + TEST_F( IntrusiveSegmentedStack_HP, defaulted ) + { + struct stack_traits : public cds::intrusive::segmented_stack::traits + { + typedef Disposer disposer; + }; + typedef cds::intrusive::SegmentedStack< gc_type, item, stack_traits > stack_type; + + std::vector arr; + { + stack_type s( c_QuasiFactor ); + test( s, arr ); + } + stack_type::gc::force_dispose(); + check_array( arr ); + } + + TEST_F( IntrusiveSegmentedStack_HP, mutex ) + { + struct stack_traits : public + cds::intrusive::segmented_stack::make_traits < + cds::intrusive::opt::disposer< Disposer > + ,cds::opt::lock_type < std::mutex > + > ::type + {}; + typedef cds::intrusive::SegmentedStack< gc_type, item, stack_traits > stack_type; + + std::vector arr; + { + stack_type s( c_QuasiFactor ); + test( s, arr ); + } + stack_type::gc::force_dispose(); + check_array( arr ); + } + + TEST_F( IntrusiveSegmentedStack_HP, shuffle ) + { + typedef cds::intrusive::SegmentedStack< gc_type, item, + cds::intrusive::segmented_stack::make_traits< + cds::intrusive::opt::disposer< Disposer > + ,cds::opt::item_counter< cds::atomicity::item_counter > + ,cds::opt::permutation_generator< cds::opt::v::random_shuffle_permutation<> > + >::type + > stack_type; + + std::vector arr; + { + stack_type s( c_QuasiFactor ); + test( s, arr ); + } + stack_type::gc::force_dispose(); + check_array( arr ); + } + + TEST_F( IntrusiveSegmentedStack_HP, padding ) + { + struct stack_traits : public cds::intrusive::segmented_stack::traits + { + typedef Disposer disposer; + enum { padding = cds::opt::cache_line_padding }; + typedef ci::segmented_stack::stat<> stat; + }; + typedef cds::intrusive::SegmentedStack< gc_type, item, stack_traits > stack_type; + + std::vector arr; + { + stack_type s( c_QuasiFactor ); + test( s, arr ); + } + stack_type::gc::force_dispose(); + check_array( arr ); + } + + TEST_F( IntrusiveSegmentedStack_HP, bigdata_padding ) + { + struct stack_traits : public cds::intrusive::segmented_stack::traits + { + typedef Disposer disposer; + enum { padding = cds::opt::cache_line_padding | cds::opt::padding_tiny_data_only }; + typedef cds::opt::v::sequential_consistent memory_model; + }; + typedef cds::intrusive::SegmentedStack< gc_type, big_item, stack_traits > stack_type; + + std::vector arr; + { + stack_type s( c_QuasiFactor ); + test( s, arr ); + } + stack_type::gc::force_dispose(); + check_array( arr ); + } + +} // namespace + diff --git a/test/unit/stack/segmented_stack_dhp.cpp b/test/unit/stack/segmented_stack_dhp.cpp new file mode 100644 index 000000000..021c84c11 --- /dev/null +++ b/test/unit/stack/segmented_stack_dhp.cpp @@ -0,0 +1,114 @@ +/* + 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. +*/ + +#include "test_segmented_stack.h" + +#include +#include + +namespace { + namespace cc = cds::container; + typedef cds::gc::DHP gc_type; + + + class SegmentedStack_DHP : public cds_test::segmented_stack + { + protected: + static const size_t c_QuasiFactor = 15; + void SetUp() + { + typedef cc::SegmentedStack< gc_type, int > stack_type; + + cds::gc::dhp::smr::construct( stack_type::c_nHazardPtrCount ); + cds::threading::Manager::attachThread(); + } + + void TearDown() + { + cds::threading::Manager::detachThread(); + cds::gc::dhp::smr::destruct(); + } + }; + + TEST_F( SegmentedStack_DHP, defaulted ) + { + typedef cds::container::SegmentedStack< gc_type, int > test_stack; + + test_stack s( c_QuasiFactor ); + ASSERT_EQ( s.quasi_factor(), cds::beans::ceil2( c_QuasiFactor )); + test(s); + } + + TEST_F( SegmentedStack_DHP, mutex ) + { + struct traits : public cds::container::segmented_stack::traits + { + typedef cds::atomicity::item_counter item_counter; + typedef cds::opt::v::random_shuffle_permutation<> permutation_generator; + }; + typedef cds::container::SegmentedStack< gc_type, int, traits > test_stack; + + test_stack s( c_QuasiFactor ); + ASSERT_EQ( s.quasi_factor(), cds::beans::ceil2( c_QuasiFactor )); + test( s ); + } + + TEST_F( SegmentedStack_DHP, shuffle ) + { + struct traits : public cds::container::segmented_stack::traits + { + typedef cds::atomicity::item_counter item_counter; + typedef cds::opt::v::random_shuffle_permutation<> permutation_generator; + }; + typedef cds::container::SegmentedStack< gc_type, int, traits > test_stack; + + test_stack s( c_QuasiFactor ); + ASSERT_EQ( s.quasi_factor(), cds::beans::ceil2( c_QuasiFactor )); + test( s ); + } + + TEST_F( SegmentedStack_DHP, stat ) + { + struct traits : public + cds::container::segmented_stack::make_traits < + cds::opt::item_counter< cds::atomicity::item_counter > + , cds::opt::permutation_generator< cds::opt::v::random_permutation<> > + , cds::opt::stat < cds::container::segmented_stack::stat<> > + > ::type + {}; + typedef cds::container::SegmentedStack< gc_type, int, traits > test_stack; + + test_stack s( c_QuasiFactor ); + ASSERT_EQ( s.quasi_factor(), cds::beans::ceil2( c_QuasiFactor )); + test( s ); + } + +} // namespace + diff --git a/test/unit/stack/segmented_stack_hp.cpp b/test/unit/stack/segmented_stack_hp.cpp new file mode 100644 index 000000000..5d8d63850 --- /dev/null +++ b/test/unit/stack/segmented_stack_hp.cpp @@ -0,0 +1,114 @@ +/* + 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. +*/ + +#include "test_segmented_stack.h" + +#include +#include + +namespace { + namespace cc = cds::container; + typedef cds::gc::HP gc_type; + + + class SegmentedStack_HP : public cds_test::segmented_stack + { + protected: + static const size_t c_QuasiFactor = 15; + void SetUp() + { + typedef cc::SegmentedStack< gc_type, int > stack_type; + + cds::gc::hp::GarbageCollector::Construct( stack_type::c_nHazardPtrCount, 1, 16 ); + cds::threading::Manager::attachThread(); + } + + void TearDown() + { + cds::threading::Manager::detachThread(); + cds::gc::hp::GarbageCollector::Destruct( true ); + } + }; + + TEST_F( SegmentedStack_HP, defaulted ) + { + typedef cds::container::SegmentedStack< gc_type, int > test_stack; + + test_stack s( c_QuasiFactor ); + ASSERT_EQ( s.quasi_factor(), cds::beans::ceil2( c_QuasiFactor )); + test(s); + } + + TEST_F( SegmentedStack_HP, mutex ) + { + struct traits : public cds::container::segmented_stack::traits + { + typedef cds::atomicity::item_counter item_counter; + typedef cds::opt::v::random_shuffle_permutation<> permutation_generator; + }; + typedef cds::container::SegmentedStack< cds::gc::HP, int, traits > test_stack; + + test_stack s( c_QuasiFactor ); + ASSERT_EQ( s.quasi_factor(), cds::beans::ceil2( c_QuasiFactor )); + test( s ); + } + + TEST_F( SegmentedStack_HP, shuffle ) + { + struct traits : public cds::container::segmented_stack::traits + { + typedef cds::atomicity::item_counter item_counter; + typedef cds::opt::v::random_shuffle_permutation<> permutation_generator; + }; + typedef cds::container::SegmentedStack< cds::gc::HP, int, traits > test_stack; + + test_stack s( c_QuasiFactor ); + ASSERT_EQ( s.quasi_factor(), cds::beans::ceil2( c_QuasiFactor )); + test( s ); + } + + TEST_F( SegmentedStack_HP, stat ) + { + struct traits : public + cds::container::segmented_stack::make_traits < + cds::opt::item_counter< cds::atomicity::item_counter > + , cds::opt::permutation_generator< cds::opt::v::random_permutation<> > + , cds::opt::stat < cds::container::segmented_stack::stat<> > + > ::type + {}; + typedef cds::container::SegmentedStack< cds::gc::HP, int, traits > test_stack; + + test_stack s( c_QuasiFactor ); + ASSERT_EQ( s.quasi_factor(), cds::beans::ceil2( c_QuasiFactor )); + test( s ); + } + +} // namespace + diff --git a/test/unit/stack/test_intrusive_segmented_stack.h b/test/unit/stack/test_intrusive_segmented_stack.h new file mode 100644 index 000000000..f9cc57da5 --- /dev/null +++ b/test/unit/stack/test_intrusive_segmented_stack.h @@ -0,0 +1,183 @@ +/* + 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 CDSUNIT_STACK_TEST_INTRUSIVE_SEGMENTED_STACK_H +#define CDSUNIT_STACK_TEST_INTRUSIVE_SEGMENTED_STACK_H + +#include + +namespace cds_test { + + class intrusive_segmented_stack : public ::testing::Test + { + protected: + struct item { + int nValue; + + size_t nDisposeCount; + size_t nDispose2Count; + + item() + : nValue( 0 ) + , nDisposeCount( 0 ) + , nDispose2Count( 0 ) + {} + + item( int nVal ) + : nValue( nVal ) + , nDisposeCount( 0 ) + , nDispose2Count( 0 ) + {} + }; + + struct big_item : public item + { + big_item() + {} + + big_item( int nVal ) + : item( nVal ) + {} + + int arr[80]; + }; + + struct Disposer + { + void operator()( item * p ) + { + ++p->nDisposeCount; + } + }; + + struct Disposer2 + { + void operator()( item * p ) + { + ++p->nDispose2Count; + } + }; + + template + void test( Stack& s, Data& val ) + { + typedef typename Stack::value_type value_type; + val.resize( 100 ); + for ( size_t i = 0; i < val.size(); ++i ) + val[i].nValue = static_cast( i ); + + ASSERT_TRUE( s.empty()); + ASSERT_CONTAINER_SIZE( s, 0u ); + + // push + for ( size_t i = 0; i < val.size(); ++i ) { + ASSERT_TRUE( s.push( val[i] )); + + ASSERT_CONTAINER_SIZE( q, i + 1 ); + } + EXPECT_TRUE( !s.empty()); + + // pop + size_t nCount = 0; + while ( !s.empty()) { + value_type * pVal; + pVal = s.pop(); + + ASSERT_TRUE( pVal != nullptr ); + + int nSegment = int( nCount / s.quasi_factor()); + int nMin = nSegment * int( s.quasi_factor()); + int nMax = nMin + int( s.quasi_factor()) - 1; + EXPECT_TRUE( nMin <= pVal->nValue && pVal->nValue <= nMax ) << nMin << " <= " << pVal->nValue << " <= " << nMax; + + ++nCount; + EXPECT_CONTAINER_SIZE( s, val.size() - nCount ); + } + EXPECT_EQ( nCount, val.size()); + EXPECT_TRUE( s.empty()); + EXPECT_CONTAINER_SIZE( s, 0u ); + + // pop from empty stack + ASSERT_TRUE( s.pop() == nullptr ); + EXPECT_TRUE( s.empty()); + EXPECT_CONTAINER_SIZE( s, 0u ); + + // check that Disposer has not been called + Stack::gc::force_dispose(); + for ( size_t i = 0; i < val.size(); ++i ) { + EXPECT_EQ( val[i].nDisposeCount, 0u ); + EXPECT_EQ( val[i].nDispose2Count, 0u ); + } + + // clear + for ( size_t i = 0; i < val.size(); ++i ) + EXPECT_TRUE( s.push( val[i] )); + EXPECT_CONTAINER_SIZE( s, val.size()); + EXPECT_TRUE( !s.empty()); + + s.clear(); + EXPECT_CONTAINER_SIZE( s, 0u ); + EXPECT_TRUE( s.empty()); + + // check if Disposer has been called + Stack::gc::force_dispose(); + for ( size_t i = 0; i < val.size(); ++i ) { + EXPECT_EQ( val[i].nDisposeCount, 1u ); + EXPECT_EQ( val[i].nDispose2Count, 0u ); + } + + // clear_with + for ( size_t i = 0; i < val.size(); ++i ) + EXPECT_TRUE( s.push( val[i] )); + EXPECT_CONTAINER_SIZE( s, val.size()); + EXPECT_TRUE( !s.empty()); + + s.clear_with( Disposer2()); + EXPECT_CONTAINER_SIZE( s, 0u ); + EXPECT_TRUE( s.empty()); + + // check if Disposer has been called + Stack::gc::force_dispose(); + for ( size_t i = 0; i < val.size(); ++i ) { + EXPECT_EQ( val[i].nDisposeCount, 1u ); + EXPECT_EQ( val[i].nDispose2Count, 1u ); + } + + // check clear on destruct + for ( size_t i = 0; i < val.size(); ++i ) + EXPECT_TRUE( s.push( val[i] )); + EXPECT_CONTAINER_SIZE( s, val.size()); + EXPECT_TRUE( !s.empty()); + } + }; + +} // namespace cds_test + +#endif // CDSUNIT_STACK_TEST_INTRUSIVE_SEGMENTED_STACK_H diff --git a/test/unit/stack/test_segmented_stack.h b/test/unit/stack/test_segmented_stack.h new file mode 100644 index 000000000..788398d32 --- /dev/null +++ b/test/unit/stack/test_segmented_stack.h @@ -0,0 +1,97 @@ +/* + 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 CDSUNIT_STACK_TEST_SEGMENTED_STACK_H +#define CDSUNIT_STACK_TEST_SEGMENTED_STACK_H + +#include + +namespace cds_test { + + class segmented_stack : public ::testing::Test + { + protected: + template + void test( Stack& s ) + { + typedef typename Stack::value_type value_type; + value_type it; + + const size_t nSize = 100; + + ASSERT_TRUE( s.empty()); + ASSERT_CONTAINER_SIZE( s, 0 ); + // push/pop + for ( size_t i = 0; i < nSize; ++i ) { + it = static_cast(i); + ASSERT_TRUE( s.push( it )); + ASSERT_CONTAINER_SIZE( s, i + 1 ); + } + ASSERT_FALSE( s.empty()); + ASSERT_CONTAINER_SIZE( s, nSize ); + + for ( size_t i = 0; i < nSize; ++i ) { + it = -1; + ASSERT_TRUE( s.pop( it )); + ASSERT_CONTAINER_SIZE( s, nSize - i - 1 ); + + int nSegment = int( i / s.quasi_factor()); + int nMin = nSegment * int( s.quasi_factor()); + int nMax = nMin + int( s.quasi_factor()) - 1; + EXPECT_LE( nMin, it ); + EXPECT_LE( it, nMax ); + } + ASSERT_TRUE( s.empty()); + ASSERT_CONTAINER_SIZE( s, 0 ); + + + // clear + for ( size_t i = 0; i < nSize; ++i ) { + ASSERT_TRUE( s.push( static_cast(i))); + } + ASSERT_FALSE( s.empty()); + ASSERT_CONTAINER_SIZE( s, nSize ); + + s.clear(); + ASSERT_TRUE( s.empty()); + ASSERT_CONTAINER_SIZE( s, 0 ); + + // pop from empty stack + it = nSize * 2; + ASSERT_FALSE( s.pop( it )); + ASSERT_EQ( it, static_cast( nSize * 2 )); + ASSERT_TRUE( s.empty()); + ASSERT_CONTAINER_SIZE( s, 0 ); + } + }; + +} // namespace cds_test + +#endif // CDSUNIT_STACK_TEST_SEGMENTED_STACK_H From a3c816e71aaf288a2a718848c70d4bf509f83fd5 Mon Sep 17 00:00:00 2001 From: Komarov Daniil Date: Sun, 17 Dec 2017 10:44:34 +0300 Subject: [PATCH 3/9] Update CMakeLists.txt --- test/unit/stack/CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/unit/stack/CMakeLists.txt b/test/unit/stack/CMakeLists.txt index c1489a9a0..4d514ce96 100644 --- a/test/unit/stack/CMakeLists.txt +++ b/test/unit/stack/CMakeLists.txt @@ -9,9 +9,9 @@ set(CDSGTEST_STACK_SOURCES treiber_stack_dhp.cpp treiber_stack_hp.cpp segmented_stack_hp.cpp - segmented_queue_dhp.cpp - intrusive_segmented_queue_hp.cpp - intrusive_segmented_queue_dhp.cpp + segmented_stack_dhp.cpp + intrusive_segmented_stack_hp.cpp + intrusive_segmented_stack_dhp.cpp ) include_directories( @@ -21,4 +21,4 @@ include_directories( add_executable(${PACKAGE_NAME} ${CDSGTEST_STACK_SOURCES}) target_link_libraries(${PACKAGE_NAME} ${CDS_TEST_LIBRARIES}) -add_test(NAME ${PACKAGE_NAME} COMMAND ${PACKAGE_NAME} WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) \ No newline at end of file +add_test(NAME ${PACKAGE_NAME} COMMAND ${PACKAGE_NAME} WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) From a022edc320b4028abd5465b4df2ae2ca3561b262 Mon Sep 17 00:00:00 2001 From: Komarov Daniil Date: Sun, 17 Dec 2017 11:12:45 +0300 Subject: [PATCH 4/9] Update test_intrusive_segmented_stack.h --- test/unit/stack/test_intrusive_segmented_stack.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/stack/test_intrusive_segmented_stack.h b/test/unit/stack/test_intrusive_segmented_stack.h index f9cc57da5..0e619b505 100644 --- a/test/unit/stack/test_intrusive_segmented_stack.h +++ b/test/unit/stack/test_intrusive_segmented_stack.h @@ -100,7 +100,7 @@ namespace cds_test { for ( size_t i = 0; i < val.size(); ++i ) { ASSERT_TRUE( s.push( val[i] )); - ASSERT_CONTAINER_SIZE( q, i + 1 ); + ASSERT_CONTAINER_SIZE( s, i + 1 ); } EXPECT_TRUE( !s.empty()); From 9553b385571c77dd917ef5f5faad2e901c28c9bb Mon Sep 17 00:00:00 2001 From: komarkom Date: Sun, 17 Dec 2017 15:26:15 +0300 Subject: [PATCH 5/9] passed unit tests --- cds/container/segmented_stack.h | 53 ++------ cds/intrusive/segmented_stack.h | 124 ++++++++---------- .../stack/test_intrusive_segmented_stack.h | 2 +- test/unit/stack/test_segmented_stack.h | 10 +- 4 files changed, 68 insertions(+), 121 deletions(-) diff --git a/cds/container/segmented_stack.h b/cds/container/segmented_stack.h index 725080ef8..76cf47d1d 100644 --- a/cds/container/segmented_stack.h +++ b/cds/container/segmented_stack.h @@ -196,10 +196,10 @@ namespace cds { namespace container { ~SegmentedStack() {} - bool _push( value_type const& val ) + bool push( value_type const& val ) { scoped_node_ptr p( alloc_node(val)); - if ( base_class::_push( *p )) { + if ( base_class::push( *p )) { p.release(); return true; } @@ -207,10 +207,10 @@ namespace cds { namespace container { } /// Inserts a new element at last segment of the stack, move semantics - bool _push( value_type&& val ) + bool push( value_type&& val ) { scoped_node_ptr p( alloc_node_move( std::move( val ))); - if ( base_class::_push( *p )) { + if ( base_class::push( *p )) { p.release(); return true; } @@ -218,11 +218,11 @@ namespace cds { namespace container { } template - bool _push_with( Func f ) + bool push_with( Func f ) { scoped_node_ptr p( alloc_node()); f( *p ); - if ( base_class::_push( *p )) { + if ( base_class::push( *p )) { p.release(); return true; } @@ -230,30 +230,11 @@ namespace cds { namespace container { } - /// Synonym for \p _push ( value_type const& ) member function - bool push( value_type const& val ) - { - return _push( val ); - } - - /// Synonym for \p _push( value_type&& ) member function - bool push( value_type&& val ) - { - return _push( std::move( val )); - } - - /// Synonym for \p _push_with() member function - template - bool push_with( Func f ) - { - return _push_with( f ); - } - template bool emplace( Args&&... args ) { scoped_node_ptr p( alloc_node_move( std::forward(args)... )); - if ( base_class::_push( *p )) { + if ( base_class::push( *p )) { p.release(); return true; } @@ -261,16 +242,16 @@ namespace cds { namespace container { } /// Pop a value from the stack - bool _pop( value_type& dest ) + bool pop( value_type& dest ) { - return _pop_with( [&dest]( value_type& src ) { dest = std::move( src );}); + return pop_with( [&dest]( value_type& src ) { dest = std::move( src );}); } /// Pop a value using a functor template - bool _pop_with( Func f ) + bool pop_with( Func f ) { - value_type * p = base_class::_pop(); + value_type * p = base_class::pop(); if ( p ) { f( *p ); gc::template retire< typename maker::node_disposer >( p ); @@ -279,18 +260,6 @@ namespace cds { namespace container { return false; } - /// Synonym for \p _pop_with() function - template - bool pop_with( Func f ) - { - return _pop_with( f ); - } - - /// Synonym for \p _pop() function - bool pop( value_type& dest ) - { - return _pop( dest ); - } /// Checks if the stack is empty bool empty() const diff --git a/cds/intrusive/segmented_stack.h b/cds/intrusive/segmented_stack.h index dfc3b2744..01b975eac 100644 --- a/cds/intrusive/segmented_stack.h +++ b/cds/intrusive/segmented_stack.h @@ -324,8 +324,8 @@ namespace cds { namespace intrusive { segment * pNew = allocate_segment(); m_Stat.onSegmentCreated(); - if ( m_List.empty()) - m_pTail.store(pNew, memory_model::memory_order_release); +// if ( m_List.empty()) +// m_pTail.store(pNew, memory_model::memory_order_release); m_List.push_front( *pNew ); m_pHead.store(pNew, memory_model::memory_order_release); return guard.assign( pNew ); @@ -341,7 +341,7 @@ namespace cds { namespace intrusive { scoped_lock l( m_Lock ); if ( m_List.empty()) { - m_pTail.store( nullptr, memory_model::memory_order_relaxed ); +// m_pTail.store( nullptr, memory_model::memory_order_relaxed ); m_pHead.store( nullptr, memory_model::memory_order_relaxed ); return guard.assign( nullptr ); } @@ -358,7 +358,7 @@ namespace cds { namespace intrusive { m_List.pop_front(); if ( m_List.empty()) { pRet = guard.assign( nullptr ); - m_pTail.store( nullptr, memory_model::memory_order_relaxed ); +// m_pTail.store( nullptr, memory_model::memory_order_relaxed ); } else pRet = guard.assign( &m_List.front()); @@ -427,7 +427,7 @@ namespace cds { namespace intrusive { } /// Inserts a new element at last segment of the stack - bool _push( value_type& val ) + bool push( value_type& val ) { // LSB is used as a flag in marked pointer assert( (reinterpret_cast( &val ) & 1) == 0 ); @@ -478,7 +478,7 @@ namespace cds { namespace intrusive { } } - value_type * _pop() + value_type * pop() { typename gc::Guard itemGuard; if ( do_pop( itemGuard )) { @@ -490,18 +490,6 @@ namespace cds { namespace intrusive { } - /// Synonym for \p _push(value_type&) member function - bool push( value_type& val ) - { - return _push( val ); - } - - /// Synonym for \p pop() member function - value_type * pop() - { - return _pop(); - } - /// Checks if the stack is empty bool empty() const { @@ -551,63 +539,61 @@ namespace cds { namespace intrusive { segment * pHeadSegment = m_SegmentList.head( segmentGuard ); permutation_generator gen( quasi_factor()); - while ( true ) { - if ( !pHeadSegment ) { - // Stack is empty - m_Stat.onPopEmpty(); - return false; - } - - bool bHadNullValue = false; - regular_cell item; - CDS_DEBUG_ONLY( size_t nLoopCount = 0 ); - do { - typename permutation_generator::integer_type i = gen; - CDS_DEBUG_ONLY( ++nLoopCount ); - - // Guard the item - // In segmented stack 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()); - - // Check if this cell is empty, which means an element - // can be pushed to this cell in the future - if ( !item.ptr()) - bHadNullValue = true; - else { - // If the item is not deleted yet - 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 )) - { - --m_ItemCounter; - m_Stat.onPop(); - - return true; + do{ + while ( true ) { + if ( !pHeadSegment ) { + // Stack is empty + m_Stat.onPopEmpty(); + return false; + } + bool bHadNullValue = false; + regular_cell item; + CDS_DEBUG_ONLY( size_t nLoopCount = 0 ); + do { + typename permutation_generator::integer_type i = gen; + CDS_DEBUG_ONLY( ++nLoopCount ); + + // Guard the item + // In segmented stack 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()); + + // Check if this cell is empty, which means an element + // can be pushed to this cell in the future + if ( !item.ptr()) + bHadNullValue = true; + else { + // If the item is not deleted yet + 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 )) + { + --m_ItemCounter; + m_Stat.onPop(); + + return true; + } + assert( item.bits()); + m_Stat.onPopContended(); } - assert( item.bits()); - m_Stat.onPopContended(); } - } - } while ( gen.next()); - - assert( nLoopCount == quasi_factor()); + } while ( gen.next()); - // scanning the entire segment without finding a candidate to pop - // If there was an empty cell, the stack is considered empty - if ( bHadNullValue ) { - m_Stat.onPopEmpty(); - return false; - } + assert( nLoopCount == quasi_factor()); - // All nodes have been poped, we can safely remove the first segment - pHeadSegment = m_SegmentList.remove_head( pHeadSegment, segmentGuard ); + // All nodes have been poped, we can safely remove the first segment + pHeadSegment = m_SegmentList.remove_head( pHeadSegment, segmentGuard ); - // Get new permutation - gen.reset(); + // Get new permutation + gen.reset(); + } } + while (!pHeadSegment); + // Stack is empty + m_Stat.onPopEmpty(); + return false; } //@endcond }; diff --git a/test/unit/stack/test_intrusive_segmented_stack.h b/test/unit/stack/test_intrusive_segmented_stack.h index 0e619b505..8608e7eba 100644 --- a/test/unit/stack/test_intrusive_segmented_stack.h +++ b/test/unit/stack/test_intrusive_segmented_stack.h @@ -89,7 +89,7 @@ namespace cds_test { void test( Stack& s, Data& val ) { typedef typename Stack::value_type value_type; - val.resize( 100 ); + val.resize( 10 ); for ( size_t i = 0; i < val.size(); ++i ) val[i].nValue = static_cast( i ); diff --git a/test/unit/stack/test_segmented_stack.h b/test/unit/stack/test_segmented_stack.h index 788398d32..d0278edcb 100644 --- a/test/unit/stack/test_segmented_stack.h +++ b/test/unit/stack/test_segmented_stack.h @@ -59,26 +59,18 @@ namespace cds_test { for ( size_t i = 0; i < nSize; ++i ) { it = -1; - ASSERT_TRUE( s.pop( it )); + ASSERT_TRUE( s.pop(it)); ASSERT_CONTAINER_SIZE( s, nSize - i - 1 ); - - int nSegment = int( i / s.quasi_factor()); - int nMin = nSegment * int( s.quasi_factor()); - int nMax = nMin + int( s.quasi_factor()) - 1; - EXPECT_LE( nMin, it ); - EXPECT_LE( it, nMax ); } ASSERT_TRUE( s.empty()); ASSERT_CONTAINER_SIZE( s, 0 ); - // clear for ( size_t i = 0; i < nSize; ++i ) { ASSERT_TRUE( s.push( static_cast(i))); } ASSERT_FALSE( s.empty()); ASSERT_CONTAINER_SIZE( s, nSize ); - s.clear(); ASSERT_TRUE( s.empty()); ASSERT_CONTAINER_SIZE( s, 0 ); From d2992c3fa6e474d7937c62a4ee71822d68581575 Mon Sep 17 00:00:00 2001 From: komarkom Date: Sun, 17 Dec 2017 15:34:20 +0300 Subject: [PATCH 6/9] update intrusive tests --- test/unit/stack/test_intrusive_segmented_stack.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/test/unit/stack/test_intrusive_segmented_stack.h b/test/unit/stack/test_intrusive_segmented_stack.h index 8608e7eba..caad40ce2 100644 --- a/test/unit/stack/test_intrusive_segmented_stack.h +++ b/test/unit/stack/test_intrusive_segmented_stack.h @@ -89,7 +89,7 @@ namespace cds_test { void test( Stack& s, Data& val ) { typedef typename Stack::value_type value_type; - val.resize( 10 ); + val.resize( 100 ); for ( size_t i = 0; i < val.size(); ++i ) val[i].nValue = static_cast( i ); @@ -112,11 +112,6 @@ namespace cds_test { ASSERT_TRUE( pVal != nullptr ); - int nSegment = int( nCount / s.quasi_factor()); - int nMin = nSegment * int( s.quasi_factor()); - int nMax = nMin + int( s.quasi_factor()) - 1; - EXPECT_TRUE( nMin <= pVal->nValue && pVal->nValue <= nMax ) << nMin << " <= " << pVal->nValue << " <= " << nMax; - ++nCount; EXPECT_CONTAINER_SIZE( s, val.size() - nCount ); } From b0e3de025cc045c6fe3459c4692d5cb338705f1a Mon Sep 17 00:00:00 2001 From: Komarov Daniil Date: Sun, 17 Dec 2017 15:49:44 +0300 Subject: [PATCH 7/9] fixed mistake --- test/stress/stack/stack_type.h | 1 - 1 file changed, 1 deletion(-) diff --git a/test/stress/stack/stack_type.h b/test/stress/stack/stack_type.h index ff5e3fcaa..d9d7ac3b1 100644 --- a/test/stress/stack/stack_type.h +++ b/test/stress/stack/stack_type.h @@ -371,7 +371,6 @@ namespace stack { typedef cds::container::SegmentedStack< cds::gc::DHP, T, traits_SegmentedStack_mutex > SegmentedStack_DHP_mutex; typedef cds::container::SegmentedStack< cds::gc::DHP, T, traits_SegmentedStack_mutex_padding > SegmentedStack_DHP_mutex_padding; typedef cds::container::SegmentedStack< cds::gc::DHP, T, traits_SegmentedStack_mutex_stat > SegmentedStack_DHP_mutex_stat; - }; // FCStack typedef cds::container::FCStack< T > FCStack_deque; From eb5d12436d0a342337f5bad85e8309fb286040af Mon Sep 17 00:00:00 2001 From: komarkom Date: Mon, 18 Dec 2017 19:39:22 +0300 Subject: [PATCH 8/9] fixed implementation --- cds/container/segmented_stack.h | 4 - cds/intrusive/segmented_stack.h | 1193 ++++++++--------- .../stack/intrusive_segmented_stack_dhp.cpp | 313 ++--- .../stack/intrusive_segmented_stack_hp.cpp | 313 ++--- test/unit/stack/segmented_stack_dhp.cpp | 225 ++-- test/unit/stack/segmented_stack_hp.cpp | 212 ++- .../stack/test_intrusive_segmented_stack.h | 356 ++--- test/unit/stack/test_segmented_stack.h | 178 +-- 8 files changed, 1357 insertions(+), 1437 deletions(-) mode change 100644 => 100755 cds/intrusive/segmented_stack.h mode change 100644 => 100755 test/unit/stack/intrusive_segmented_stack_dhp.cpp mode change 100644 => 100755 test/unit/stack/intrusive_segmented_stack_hp.cpp mode change 100644 => 100755 test/unit/stack/segmented_stack_dhp.cpp mode change 100644 => 100755 test/unit/stack/segmented_stack_hp.cpp mode change 100644 => 100755 test/unit/stack/test_intrusive_segmented_stack.h mode change 100644 => 100755 test/unit/stack/test_segmented_stack.h diff --git a/cds/container/segmented_stack.h b/cds/container/segmented_stack.h index 76cf47d1d..3494b887a 100644 --- a/cds/container/segmented_stack.h +++ b/cds/container/segmented_stack.h @@ -75,9 +75,6 @@ namespace cds { namespace container { /// Lock type used to maintain an internal list of allocated segments typedef cds::sync::spin lock_type; - - /// Random \ref cds::opt::permutation_generator "permutation generator" for sequence [0, quasi_factor) - typedef cds::opt::v::random2_permutation permutation_generator; }; template @@ -158,7 +155,6 @@ namespace cds { namespace container { typedef typename base_class::item_counter item_counter; ///< Item counting policy, see cds::opt::item_counter option setter typedef typename base_class::stat stat ; ///< Internal statistics policy typedef typename base_class::lock_type lock_type ; ///< Type of mutex for maintaining an internal list of allocated segments. - typedef typename base_class::permutation_generator permutation_generator; ///< Random permutation generator for sequence [0, quasi-factor) static const size_t c_nHazardPtrCount = base_class::c_nHazardPtrCount ; ///< Count of hazard pointer required for the algorithm diff --git a/cds/intrusive/segmented_stack.h b/cds/intrusive/segmented_stack.h old mode 100644 new mode 100755 index 01b975eac..cbdd8f9aa --- a/cds/intrusive/segmented_stack.h +++ b/cds/intrusive/segmented_stack.h @@ -1,606 +1,587 @@ -/* - 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_STACK_H -#define CDSLIB_INTRUSIVE_SEGMENTED_STACK_H - -#include -#include -#include -#include -#include -#include - -#include - -#if CDS_COMPILER == CDS_COMPILER_MSVC -# pragma warning( push ) -# pragma warning( disable: 4355 ) // warning C4355: 'this' : used in base member initializer list -#endif - -namespace cds { namespace intrusive { - - /// SegmentedStack -related declarations - namespace segmented_stack { - - /// SegmentedStack internal statistics. May be used for debugging or profiling - template - struct stat - { - typedef Counter counter_type; ///< Counter type - - counter_type m_nPush; ///< Push count - counter_type m_nPushPopulated; ///< Number of attempts to push to populated (non-empty) cell - counter_type m_nPushContended; ///< Number of failed CAS when pushing - counter_type m_nPop; ///< Pop count - counter_type m_nPopEmpty; ///< Number of poping from empty stack - counter_type m_nPopContended; ///< Number of failed CAS when popping - - counter_type m_nCreateSegmentReq; ///< Number of request to create new segment - counter_type m_nDeleteSegmentReq; ///< Number to request to delete segment - counter_type m_nSegmentCreated; ///< Number of created segments - counter_type m_nSegmentDeleted; ///< Number of deleted segments - - //@cond - void onPush() { ++m_nPush; } - void onPushPopulated() { ++m_nPushPopulated; } - void onPushContended() { ++m_nPushContended; } - void onPop() { ++m_nPop; } - void onPopEmpty() { ++m_nPopEmpty; } - void onPopContended() { ++m_nPopContended; } - void onCreateSegmentReq() { ++m_nCreateSegmentReq; } - void onDeleteSegmentReq() { ++m_nDeleteSegmentReq; } - void onSegmentCreated() { ++m_nSegmentCreated; } - void onSegmentDeleted() { ++m_nSegmentDeleted; } - //@endcond - }; - - /// Dummy SegmentedStack statistics, no overhead - struct empty_stat { - //@cond - void onPush() const {} - void onPushPopulated() const {} - void onPushContended() const {} - void onPop() const {} - void onPopEmpty() const {} - void onPopContended() const {} - void onCreateSegmentReq() const {} - void onDeleteSegmentReq() const {} - void onSegmentCreated() const {} - void onSegmentDeleted() const {} - //@endcond - }; - - /// SegmentedStack default traits - struct traits { - /// Element disposer that is called when the item to be pop. Default is opt::v::empty_disposer (no disposer) - typedef opt::v::empty_disposer disposer; - - /// Item counter, default is atomicity::item_counter - /** - The item counting is an essential part of segmented stack algorithm. - The \p empty() member function is based on checking size() == 0. - Therefore, dummy item counter like atomicity::empty_item_counter is not the proper counter. - */ - typedef atomicity::item_counter item_counter; - - /// Internal statistics, possible predefined types are \ref stat, \ref empty_stat (the default) - typedef segmented_stack::empty_stat stat; - - /// Memory model, default is opt::v::relaxed_ordering. See cds::opt::memory_model for the full list of possible types - typedef opt::v::relaxed_ordering memory_model; - - /// Alignment of critical data, default is cache line alignment. See cds::opt::alignment option specification - enum { alignment = opt::cache_line_alignment }; - - /// Padding of segment data, default is no special padding - /** - The segment is just an array of atomic data pointers, - so, the high load leads to false sharing and performance degradation. - A padding of segment data can eliminate false sharing issue. - On the other hand, the padding leads to increase segment size. - */ - enum { padding = opt::no_special_padding }; - - /// Segment allocator. Default is \ref CDS_DEFAULT_ALLOCATOR - typedef CDS_DEFAULT_ALLOCATOR allocator; - - /// Lock type used to maintain an internal list of allocated segments - typedef cds::sync::spin lock_type; - - /// Random \ref cds::opt::permutation_generator "permutation generator" for sequence [0, quasi_factor) - typedef cds::opt::v::random2_permutation permutation_generator; - }; - - template - struct make_traits { -# ifdef CDS_DOXYGEN_INVOKED - typedef implementation_defined type ; ///< Metafunction result -# else - typedef typename cds::opt::make_options< - typename cds::opt::find_type_traits< traits, Options... >::type - ,Options... - >::type type; -# endif - }; - } // namespace segmented_stack - - /// Segmented stack - /** @ingroup cds_intrusive_stack - - The stack is based on work - - [2014] Henzinger, Kirsch, Payer, Sezgin, Sokolova Quantitative Relaxation of Concurrent Data Structures - - Template parameters: - - \p GC - a garbage collector, possible types are cds::gc::HP, cds::gc::DHP - - \p T - the type of values stored in the stack - - \p Traits - stack type traits, default is \p segmented_stack::traits. - \p segmented_stack::make_traits metafunction can be used to construct the - type traits. - - */ - template - class SegmentedStack - { - public: - typedef GC gc; ///< Garbage collector - typedef T value_type; ///< type of the value stored in the stack - typedef Traits traits; ///< Stack traits - - typedef typename traits::disposer disposer ; ///< value disposer, called only in \p clear() when the element to be pop - typedef typename traits::allocator allocator; ///< Allocator maintaining the segments - typedef typename traits::memory_model memory_model; ///< Memory ordering. See cds::opt::memory_model option - typedef typename traits::item_counter item_counter; ///< Item counting policy, see cds::opt::item_counter option setter - typedef typename traits::stat stat; ///< Internal statistics policy - typedef typename traits::lock_type lock_type; ///< Type of mutex for maintaining an internal list of allocated segments. - typedef typename traits::permutation_generator permutation_generator; ///< Random permutation generator for sequence [0, quasi-factor) - - static const size_t c_nHazardPtrCount = 2 ; ///< Count of hazard pointer required for the algorithm - - protected: - //@cond - // Segment cell. LSB is used as deleted mark - typedef cds::details::marked_ptr< value_type, 1 > regular_cell; - typedef atomics::atomic< regular_cell > atomic_cell; - typedef typename cds::opt::details::apply_padding< atomic_cell, traits::padding >::type cell; - - // Segment - struct segment: public boost::intrusive::slist_base_hook<> - { - cell * cells; // Cell array of size \ref m_nQuasiFactor - size_t version; // version tag (ABA prevention tag) - // cell array is placed here in one continuous memory block - - // Initializes the segment - explicit segment( size_t nCellCount ) - // MSVC warning C4355: 'this': used in base member initializer list - : cells( reinterpret_cast< cell *>( this + 1 )) - , version( 0 ) - { - init( nCellCount ); - } - - segment() = delete; - - void init( size_t nCellCount ) - { - cell * pLastCell = cells + nCellCount; - for ( cell* pCell = cells; pCell < pLastCell; ++pCell ) - pCell->data.store( regular_cell(), atomics::memory_order_relaxed ); - atomics::atomic_thread_fence( memory_model::memory_order_release ); - } - }; - - typedef typename opt::details::alignment_setter< atomics::atomic, traits::alignment >::type aligned_segment_ptr; - //@endcond - - protected: - //@cond - class segment_list - { - typedef boost::intrusive::slist< segment, boost::intrusive::cache_last< true > > list_impl; - typedef std::unique_lock< lock_type > scoped_lock; - - aligned_segment_ptr m_pHead; - aligned_segment_ptr m_pTail; - - list_impl m_List; - mutable lock_type m_Lock; - size_t const m_nQuasiFactor; - stat& m_Stat; - - private: - struct segment_disposer - { - void operator()( segment * pSegment ) - { - assert( pSegment != nullptr ); - free_segment( pSegment ); - } - }; - - struct gc_segment_disposer - { - void operator()( segment * pSegment ) - { - assert( pSegment != nullptr ); - retire_segment( pSegment ); - } - }; - - public: - segment_list( size_t nQuasiFactor, stat& st ) - : m_pHead( nullptr ) - , m_pTail( nullptr ) - , m_nQuasiFactor( nQuasiFactor ) - , m_Stat( st ) - { - assert( cds::beans::is_power2( nQuasiFactor )); - } - - ~segment_list() - { - m_List.clear_and_dispose( gc_segment_disposer()); - } - - segment * head( typename gc::Guard& guard ) - { - return guard.protect( m_pHead ); - } - - segment * tail( typename gc::Guard& guard ) - { - return guard.protect( m_pTail ); - } - -# ifdef _DEBUG - bool populated( segment const& s ) const - { - // 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()) - return false; - } - return true; - } - bool exhausted( segment const& s ) const - { - // 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()) - return false; - } - return true; - } -# endif - - segment * create_head( segment * pHead, typename gc::Guard& guard ) - { - // pHead is guarded by GC - - m_Stat.onCreateSegmentReq(); - - scoped_lock l( m_Lock ); - - if ( !m_List.empty() && (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()); - } - -# ifdef _DEBUG - assert( m_List.empty() || populated( m_List.front())); -# endif - - segment * pNew = allocate_segment(); - m_Stat.onSegmentCreated(); - -// if ( m_List.empty()) -// m_pTail.store(pNew, memory_model::memory_order_release); - m_List.push_front( *pNew ); - m_pHead.store(pNew, memory_model::memory_order_release); - return guard.assign( pNew ); - } - - segment * remove_head( segment * pHead, typename gc::Guard& guard ) - { - // pHead is guarded by GC - m_Stat.onDeleteSegmentReq(); - - segment * pRet; - { - scoped_lock l( m_Lock ); - - 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 ); - } - - 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()); - } - -# ifdef _DEBUG - assert( exhausted( m_List.front())); -# endif - - m_List.pop_front(); - if ( m_List.empty()) { - pRet = guard.assign( nullptr ); -// m_pTail.store( nullptr, memory_model::memory_order_relaxed ); - } - else - pRet = guard.assign( &m_List.front()); - m_pHead.store( pRet, memory_model::memory_order_release ); - } - - retire_segment( pHead ); - m_Stat.onSegmentDeleted(); - - return pRet; - } - - size_t quasi_factor() const - { - return m_nQuasiFactor; - } - - private: - typedef cds::details::Allocator< segment, allocator > segment_allocator; - - static size_t get_version( segment * pSegment ) - { - return pSegment ? pSegment->version : 0; - } - - segment * allocate_segment() - { - return segment_allocator().NewBlock( sizeof(segment) + sizeof(cell) * m_nQuasiFactor, quasi_factor()); - } - - static void free_segment( segment * pSegment ) - { - segment_allocator().Delete( pSegment ); - } - - static void retire_segment( segment * pSegment ) - { - gc::template retire( pSegment ); - } - }; - //@endcond - - protected: - segment_list m_SegmentList; ///< List of segments - - item_counter m_ItemCounter; ///< Item counter - stat m_Stat; ///< Internal statistics - - public: - /// Initializes the empty stack - SegmentedStack( - size_t nQuasiFactor ///< Quasi factor. If it is not a power of 2 it is rounded up to nearest power of 2. Minimum is 2. - ) - : m_SegmentList( cds::beans::ceil2(nQuasiFactor), m_Stat ) - { - static_assert( (!std::is_same< item_counter, cds::atomicity::empty_item_counter >::value), - "cds::atomicity::empty_item_counter is not supported for SegmentedStack" - ); - assert( m_SegmentList.quasi_factor() > 1 ); - } - - /// Clears the stack and deletes all internal data - ~SegmentedStack() - { - clear(); - } - - /// Inserts a new element at last segment of the stack - bool push( value_type& val ) - { - // LSB is used as a flag in marked pointer - assert( (reinterpret_cast( &val ) & 1) == 0 ); - - typename gc::Guard segmentGuard; - segment * pHeadSegment = m_SegmentList.head( segmentGuard ); - if ( !pHeadSegment) { - // no segments, create the new one - pHeadSegment = m_SegmentList.create_head(pHeadSegment, segmentGuard ); - assert(pHeadSegment); - } - - permutation_generator gen( quasi_factor()); - - ++m_ItemCounter; - - while ( true ) { - CDS_DEBUG_ONLY( size_t nLoopCount = 0); - do { - typename permutation_generator::integer_type i = gen; - CDS_DEBUG_ONLY( ++nLoopCount ); - if (pHeadSegment->cells[i].data.load(memory_model::memory_order_relaxed).all()) { - // Cell is not empty, go next - m_Stat.onPushPopulated(); - } - else { - // Empty cell found, try to push here - regular_cell nullCell; - if (pHeadSegment->cells[i].data.compare_exchange_strong( nullCell, regular_cell( &val ), - memory_model::memory_order_release, atomics::memory_order_relaxed )) - { - // Ok to push item - m_Stat.onPush(); - return true; - } - assert( nullCell.ptr()); - m_Stat.onPushContended(); - } - } while ( gen.next()); - - assert( nLoopCount == quasi_factor()); - - // No available position, create a new segment - pHeadSegment = m_SegmentList.create_head(pHeadSegment, segmentGuard ); - - // Get new permutation - gen.reset(); - } - } - - value_type * pop() - { - typename gc::Guard itemGuard; - if ( do_pop( itemGuard )) { - value_type * pVal = itemGuard.template get(); - assert( pVal ); - return pVal; - } - return nullptr; - - } - - /// Checks if the stack is empty - bool empty() const - { - return size() == 0; - } - - /// Clear the stack - void clear() - { - clear_with( disposer()); - } - - template - void clear_with( Disposer ) - { - typename gc::Guard itemGuard; - while ( do_pop( itemGuard )) { - assert( itemGuard.template get()); - gc::template retire( itemGuard.template get()); - itemGuard.clear(); - } - } - - /// Returns stack's item count - size_t size() const - { - return m_ItemCounter.value(); - } - - /// Returns reference to internal statistics - const stat& statistics() const - { - return m_Stat; - } - - /// Returns quasi factor, a power-of-two number - size_t quasi_factor() const - { - return m_SegmentList.quasi_factor(); - } - - protected: - //@cond - bool do_pop( typename gc::Guard& itemGuard ) - { - typename gc::Guard segmentGuard; - segment * pHeadSegment = m_SegmentList.head( segmentGuard ); - - permutation_generator gen( quasi_factor()); - do{ - while ( true ) { - if ( !pHeadSegment ) { - // Stack is empty - m_Stat.onPopEmpty(); - return false; - } - bool bHadNullValue = false; - regular_cell item; - CDS_DEBUG_ONLY( size_t nLoopCount = 0 ); - do { - typename permutation_generator::integer_type i = gen; - CDS_DEBUG_ONLY( ++nLoopCount ); - - // Guard the item - // In segmented stack 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()); - - // Check if this cell is empty, which means an element - // can be pushed to this cell in the future - if ( !item.ptr()) - bHadNullValue = true; - else { - // If the item is not deleted yet - 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 )) - { - --m_ItemCounter; - m_Stat.onPop(); - - return true; - } - assert( item.bits()); - m_Stat.onPopContended(); - } - } - } while ( gen.next()); - - assert( nLoopCount == quasi_factor()); - - // All nodes have been poped, we can safely remove the first segment - pHeadSegment = m_SegmentList.remove_head( pHeadSegment, segmentGuard ); - - // Get new permutation - gen.reset(); - } - } - while (!pHeadSegment); - // Stack is empty - m_Stat.onPopEmpty(); - return false; - } - //@endcond - }; -}} // namespace cds::intrusive - -#if CDS_COMPILER == CDS_COMPILER_MSVC -# pragma warning( pop ) -#endif - -#endif // #ifndef CDSLIB_INTRUSIVE_SEGMENTED_STACK_H +/* + 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_STACK_H +#define CDSLIB_INTRUSIVE_SEGMENTED_STACK_H + +#include +#include +#include +#include +#include +#include + +#include + +#if CDS_COMPILER == CDS_COMPILER_MSVC +# pragma warning( push ) +# pragma warning( disable: 4355 ) // warning C4355: 'this' : used in base member initializer list +#endif + +namespace cds { namespace intrusive { + + /// SegmentedStack -related declarations + namespace segmented_stack { + + /// SegmentedStack internal statistics. May be used for debugging or profiling + template + struct stat + { + typedef Counter counter_type; ///< Counter type + + counter_type m_nPush; ///< Push count + counter_type m_nPushPopulated; ///< Number of attempts to push to populated (non-empty) cell + counter_type m_nPushContended; ///< Number of failed CAS when pushing + counter_type m_nPop; ///< Pop count + counter_type m_nPopEmpty; ///< Number of poping from empty stack + counter_type m_nPopContended; ///< Number of failed CAS when popping + + counter_type m_nCreateSegmentReq; ///< Number of request to create new segment + counter_type m_nDeleteSegmentReq; ///< Number to request to delete segment + counter_type m_nSegmentCreated; ///< Number of created segments + counter_type m_nSegmentDeleted; ///< Number of deleted segments + + //@cond + void onPush() { ++m_nPush; } + void onPushPopulated() { ++m_nPushPopulated; } + void onPushContended() { ++m_nPushContended; } + void onPop() { ++m_nPop; } + void onPopEmpty() { ++m_nPopEmpty; } + void onPopContended() { ++m_nPopContended; } + void onCreateSegmentReq() { ++m_nCreateSegmentReq; } + void onDeleteSegmentReq() { ++m_nDeleteSegmentReq; } + void onSegmentCreated() { ++m_nSegmentCreated; } + void onSegmentDeleted() { ++m_nSegmentDeleted; } + //@endcond + }; + + /// Dummy SegmentedStack statistics, no overhead + struct empty_stat { + //@cond + void onPush() const {} + void onPushPopulated() const {} + void onPushContended() const {} + void onPop() const {} + void onPopEmpty() const {} + void onPopContended() const {} + void onCreateSegmentReq() const {} + void onDeleteSegmentReq() const {} + void onSegmentCreated() const {} + void onSegmentDeleted() const {} + //@endcond + }; + + /// SegmentedStack default traits + struct traits { + /// Element disposer that is called when the item to be pop. Default is opt::v::empty_disposer (no disposer) + typedef opt::v::empty_disposer disposer; + + /// Item counter, default is atomicity::item_counter + /** + The item counting is an essential part of segmented stack algorithm. + The \p empty() member function is based on checking size() == 0. + Therefore, dummy item counter like atomicity::empty_item_counter is not the proper counter. + */ + typedef atomicity::item_counter item_counter; + + /// Internal statistics, possible predefined types are \ref stat, \ref empty_stat (the default) + typedef segmented_stack::empty_stat stat; + + /// Memory model, default is opt::v::relaxed_ordering. See cds::opt::memory_model for the full list of possible types + typedef opt::v::relaxed_ordering memory_model; + + /// Alignment of critical data, default is cache line alignment. See cds::opt::alignment option specification + enum { alignment = opt::cache_line_alignment }; + + /// Padding of segment data, default is no special padding + /** + The segment is just an array of atomic data pointers, + so, the high load leads to false sharing and performance degradation. + A padding of segment data can eliminate false sharing issue. + On the other hand, the padding leads to increase segment size. + */ + enum { padding = opt::no_special_padding }; + + /// Segment allocator. Default is \ref CDS_DEFAULT_ALLOCATOR + typedef CDS_DEFAULT_ALLOCATOR allocator; + + /// Lock type used to maintain an internal list of allocated segments + typedef cds::sync::spin lock_type; + }; + + template + struct make_traits { +# ifdef CDS_DOXYGEN_INVOKED + typedef implementation_defined type ; ///< Metafunction result +# else + typedef typename cds::opt::make_options< + typename cds::opt::find_type_traits< traits, Options... >::type + ,Options... + >::type type; +# endif + }; + } // namespace segmented_stack + + /// Segmented stack + /** @ingroup cds_intrusive_stack + + The stack is based on work + - [2014] Henzinger, Kirsch, Payer, Sezgin, Sokolova Quantitative Relaxation of Concurrent Data Structures + + Template parameters: + - \p GC - a garbage collector, possible types are cds::gc::HP, cds::gc::DHP + - \p T - the type of values stored in the stack + - \p Traits - stack type traits, default is \p segmented_stack::traits. + \p segmented_stack::make_traits metafunction can be used to construct the + type traits. + + */ + template + class SegmentedStack + { + public: + typedef GC gc; ///< Garbage collector + typedef T value_type; ///< type of the value stored in the stack + typedef Traits traits; ///< Stack traits + + typedef typename traits::disposer disposer ; ///< value disposer, called only in \p clear() when the element to be pop + typedef typename traits::allocator allocator; ///< Allocator maintaining the segments + typedef typename traits::memory_model memory_model; ///< Memory ordering. See cds::opt::memory_model option + typedef typename traits::item_counter item_counter; ///< Item counting policy, see cds::opt::item_counter option setter + typedef typename traits::stat stat; ///< Internal statistics policy + typedef typename traits::lock_type lock_type; ///< Type of mutex for maintaining an internal list of allocated segments. + + static const size_t c_nHazardPtrCount = 2 ; ///< Count of hazard pointer required for the algorithm + + protected: + //@cond + // Segment cell. LSB is used as deleted mark + typedef cds::details::marked_ptr< value_type, 1 > regular_cell; + typedef atomics::atomic< regular_cell > atomic_cell; + typedef typename cds::opt::details::apply_padding< atomic_cell, traits::padding >::type cell; + + // Segment + struct segment: public boost::intrusive::slist_base_hook<> + { + cell * cells; // Cell array of size \ref m_nQuasiFactor + size_t version; // version tag (ABA prevention tag) + // cell array is placed here in one continuous memory block + + // Initializes the segment + explicit segment( size_t nCellCount ) + // MSVC warning C4355: 'this': used in base member initializer list + : cells( reinterpret_cast< cell *>( this + 1 )) + , version( 0 ) + { + init( nCellCount ); + } + + segment() = delete; + + void init( size_t nCellCount ) + { + cell * pLastCell = cells + nCellCount; + for ( cell* pCell = cells; pCell < pLastCell; ++pCell ) + pCell->data.store( regular_cell(), atomics::memory_order_relaxed ); + atomics::atomic_thread_fence( memory_model::memory_order_release ); + } + }; + + typedef typename opt::details::alignment_setter< atomics::atomic, traits::alignment >::type aligned_segment_ptr; + //@endcond + + protected: + //@cond + class segment_list + { + typedef boost::intrusive::slist< segment, boost::intrusive::cache_last< true > > list_impl; + typedef std::unique_lock< lock_type > scoped_lock; + + aligned_segment_ptr m_pHead; + aligned_segment_ptr m_pTail; + + list_impl m_List; + mutable lock_type m_Lock; + size_t const m_nQuasiFactor; + stat& m_Stat; + + private: + struct segment_disposer + { + void operator()( segment * pSegment ) + { + assert( pSegment != nullptr ); + free_segment( pSegment ); + } + }; + + struct gc_segment_disposer + { + void operator()( segment * pSegment ) + { + assert( pSegment != nullptr ); + retire_segment( pSegment ); + } + }; + + public: + segment_list( size_t nQuasiFactor, stat& st ) + : m_pHead( nullptr ) + , m_pTail( nullptr ) + , m_nQuasiFactor( nQuasiFactor ) + , m_Stat( st ) + { + assert( cds::beans::is_power2( nQuasiFactor )); + } + + ~segment_list() + { + m_List.clear_and_dispose( gc_segment_disposer()); + } + + segment * head( typename gc::Guard& guard ) + { + return guard.protect( m_pHead ); + } + + segment * tail( typename gc::Guard& guard ) + { + return guard.protect( m_pTail ); + } + +# ifdef _DEBUG + bool populated( segment const& s ) const + { + // 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()) + return false; + } + return true; + } + bool exhausted( segment const& s ) const + { + // 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()) + return false; + } + return true; + } +# endif + + segment * create_head( segment * pHead, typename gc::Guard& guard ) + { + // pHead is guarded by GC + + m_Stat.onCreateSegmentReq(); + + scoped_lock l( m_Lock ); + + if ( !m_List.empty() && (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()); + } + +# ifdef _DEBUG + assert( m_List.empty() || populated( m_List.front())); +# endif + + segment * pNew = allocate_segment(); + m_Stat.onSegmentCreated(); + +// if ( m_List.empty()) +// m_pTail.store(pNew, memory_model::memory_order_release); + m_List.push_front( *pNew ); + m_pHead.store(pNew, memory_model::memory_order_release); + return guard.assign( pNew ); + } + + segment * remove_head( segment * pHead, typename gc::Guard& guard ) + { + // pHead is guarded by GC + m_Stat.onDeleteSegmentReq(); + + segment * pRet; + { + scoped_lock l( m_Lock ); + + 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 ); + } + + 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()); + } + +# ifdef _DEBUG + assert( exhausted( m_List.front())); +# endif + + m_List.pop_front(); + if ( m_List.empty()) { + pRet = guard.assign( nullptr ); +// m_pTail.store( nullptr, memory_model::memory_order_relaxed ); + } + else + pRet = guard.assign( &m_List.front()); + m_pHead.store( pRet, memory_model::memory_order_release ); + } + + retire_segment( pHead ); + m_Stat.onSegmentDeleted(); + + return pRet; + } + + size_t quasi_factor() const + { + return m_nQuasiFactor; + } + + private: + typedef cds::details::Allocator< segment, allocator > segment_allocator; + + static size_t get_version( segment * pSegment ) + { + return pSegment ? pSegment->version : 0; + } + + segment * allocate_segment() + { + return segment_allocator().NewBlock( sizeof(segment) + sizeof(cell) * m_nQuasiFactor, quasi_factor()); + } + + static void free_segment( segment * pSegment ) + { + segment_allocator().Delete( pSegment ); + } + + static void retire_segment( segment * pSegment ) + { + gc::template retire( pSegment ); + } + }; + //@endcond + + protected: + segment_list m_SegmentList; ///< List of segments + + item_counter m_ItemCounter; ///< Item counter + stat m_Stat; ///< Internal statistics + + public: + /// Initializes the empty stack + SegmentedStack( + size_t nQuasiFactor ///< Quasi factor. If it is not a power of 2 it is rounded up to nearest power of 2. Minimum is 2. + ) + : m_SegmentList( cds::beans::ceil2(nQuasiFactor), m_Stat ) + { + static_assert( (!std::is_same< item_counter, cds::atomicity::empty_item_counter >::value), + "cds::atomicity::empty_item_counter is not supported for SegmentedStack" + ); + assert( m_SegmentList.quasi_factor() > 1 ); + } + + /// Clears the stack and deletes all internal data + ~SegmentedStack() + { + clear(); + } + + /// Inserts a new element at last segment of the stack + bool push( value_type& val ) + { + // LSB is used as a flag in marked pointer + assert( (reinterpret_cast( &val ) & 1) == 0 ); + + typename gc::Guard segmentGuard; + segment * pHeadSegment = m_SegmentList.head( segmentGuard ); + if ( !pHeadSegment) { + // no segments, create the new one + pHeadSegment = m_SegmentList.create_head(pHeadSegment, segmentGuard ); + assert(pHeadSegment); + } + ++m_ItemCounter; + + while ( true ) { + CDS_DEBUG_ONLY( size_t nLoopCount = 0); + int i = quasi_factor(); + do { + CDS_DEBUG_ONLY( ++nLoopCount ); + if (pHeadSegment->cells[i].data.load(memory_model::memory_order_relaxed).all()) { + // Cell is not empty, go next + m_Stat.onPushPopulated(); + } + else { + // Empty cell found, try to push here + regular_cell nullCell; + if (pHeadSegment->cells[i].data.compare_exchange_strong( nullCell, regular_cell( &val ), + memory_model::memory_order_release, atomics::memory_order_relaxed )) + { + // Ok to push item + m_Stat.onPush(); + return true; + } + assert( nullCell.ptr()); + m_Stat.onPushContended(); + } + i--; + } while (i >= 0); + + // No available position, create a new segment + pHeadSegment = m_SegmentList.create_head(pHeadSegment, segmentGuard ); + } + } + + value_type * pop() + { + typename gc::Guard itemGuard; + if ( do_pop( itemGuard )) { + value_type * pVal = itemGuard.template get(); + assert( pVal ); + return pVal; + } + return nullptr; + + } + + /// Checks if the stack is empty + bool empty() const + { + return size() == 0; + } + + /// Clear the stack + void clear() + { + clear_with( disposer()); + } + + template + void clear_with( Disposer ) + { + typename gc::Guard itemGuard; + while ( do_pop( itemGuard )) { + assert( itemGuard.template get()); + gc::template retire( itemGuard.template get()); + itemGuard.clear(); + } + } + + /// Returns stack's item count + size_t size() const + { + return m_ItemCounter.value(); + } + + /// Returns reference to internal statistics + const stat& statistics() const + { + return m_Stat; + } + + /// Returns quasi factor, a power-of-two number + size_t quasi_factor() const + { + return m_SegmentList.quasi_factor(); + } + + protected: + //@cond + bool do_pop( typename gc::Guard& itemGuard ) + { + typename gc::Guard segmentGuard; + segment * pHeadSegment = m_SegmentList.head( segmentGuard ); + +// do{ + while ( true ) { + if ( !pHeadSegment ) { + // Stack is empty + m_Stat.onPopEmpty(); + return false; + } + regular_cell item; + CDS_DEBUG_ONLY( size_t nLoopCount = 0 ); + int i = quasi_factor(); + do { + CDS_DEBUG_ONLY( ++nLoopCount ); + + // Guard the item + // In segmented stack 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()); + + // Check if this cell is empty, which means an element + // can be pushed to this cell in the future + if ( !item.ptr()) + { + i--; + continue; + } + else { + // If the item is not deleted yet + 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 )) + { + --m_ItemCounter; + m_Stat.onPop(); + + return true; + } + assert( item.bits()); + m_Stat.onPopContended(); + } + } + i--; + } while ( i >= 0); + + // All nodes have been poped, we can safely remove the first segment + pHeadSegment = m_SegmentList.remove_head( pHeadSegment, segmentGuard ); + } + } + //@endcond + }; +}} // namespace cds::intrusive + +#if CDS_COMPILER == CDS_COMPILER_MSVC +# pragma warning( pop ) +#endif + +#endif // #ifndef CDSLIB_INTRUSIVE_SEGMENTED_STACK_H diff --git a/test/unit/stack/intrusive_segmented_stack_dhp.cpp b/test/unit/stack/intrusive_segmented_stack_dhp.cpp old mode 100644 new mode 100755 index a82872773..dc121594d --- a/test/unit/stack/intrusive_segmented_stack_dhp.cpp +++ b/test/unit/stack/intrusive_segmented_stack_dhp.cpp @@ -1,166 +1,147 @@ -/* - 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. -*/ - -#include "test_intrusive_segmented_stack.h" - -#include -#include -#include - -namespace { - namespace ci = cds::intrusive; - typedef cds::gc::DHP gc_type; - - class IntrusiveSegmentedStack_DHP : public cds_test::intrusive_segmented_stack - { - typedef cds_test::intrusive_segmented_stack base_class; - - protected: - static const size_t c_QuasiFactor = 15; - - void SetUp() - { - typedef ci::SegmentedStack< gc_type, item > stack_type; - - cds::gc::dhp::smr::construct( stack_type::c_nHazardPtrCount ); - cds::threading::Manager::attachThread(); - } - - void TearDown() - { - cds::threading::Manager::detachThread(); - cds::gc::dhp::smr::destruct(); - } - - template - void check_array( V& arr ) - { - for ( size_t i = 0; i < arr.size(); ++i ) { - EXPECT_EQ( arr[i].nDisposeCount, 2u ); - EXPECT_EQ( arr[i].nDispose2Count, 1u ); - } - } - }; - - TEST_F( IntrusiveSegmentedStack_DHP, defaulted ) - { - struct stack_traits : public cds::intrusive::segmented_stack::traits - { - typedef Disposer disposer; - }; - typedef cds::intrusive::SegmentedStack< gc_type, item, stack_traits > stack_type; - - std::vector arr; - { - stack_type s( c_QuasiFactor ); - test( s, arr ); - } - stack_type::gc::force_dispose(); - check_array( arr ); - } - - TEST_F( IntrusiveSegmentedStack_DHP, mutex ) - { - struct stack_traits : public - cds::intrusive::segmented_stack::make_traits < - cds::intrusive::opt::disposer< Disposer > - ,cds::opt::lock_type < std::mutex > - > ::type - {}; - typedef cds::intrusive::SegmentedStack< gc_type, item, stack_traits > stack_type; - - std::vector arr; - { - stack_type s( c_QuasiFactor ); - test( s, arr ); - } - stack_type::gc::force_dispose(); - check_array( arr ); - } - - TEST_F( IntrusiveSegmentedStack_DHP, shuffle ) - { - typedef cds::intrusive::SegmentedStack< gc_type, item, - cds::intrusive::segmented_stack::make_traits< - cds::intrusive::opt::disposer< Disposer > - ,cds::opt::item_counter< cds::atomicity::item_counter > - ,cds::opt::permutation_generator< cds::opt::v::random_shuffle_permutation<> > - >::type - > stack_type; - - std::vector arr; - { - stack_type s( c_QuasiFactor ); - test( s, arr ); - } - stack_type::gc::force_dispose(); - check_array( arr ); - } - - TEST_F( IntrusiveSegmentedStack_DHP, padding ) - { - struct stack_traits : public cds::intrusive::segmented_stack::traits - { - typedef Disposer disposer; - enum { padding = cds::opt::cache_line_padding }; - typedef ci::segmented_stack::stat<> stat; - }; - typedef cds::intrusive::SegmentedStack< gc_type, item, stack_traits > stack_type; - - std::vector arr; - { - stack_type s( c_QuasiFactor ); - test( s, arr ); - } - stack_type::gc::force_dispose(); - check_array( arr ); - } - - TEST_F( IntrusiveSegmentedStack_DHP, bigdata_padding ) - { - struct stack_traits : public cds::intrusive::segmented_stack::traits - { - typedef Disposer disposer; - enum { padding = cds::opt::cache_line_padding | cds::opt::padding_tiny_data_only }; - typedef cds::opt::v::sequential_consistent memory_model; - }; - typedef cds::intrusive::SegmentedStack< gc_type, big_item, stack_traits > stack_type; - - std::vector arr; - { - stack_type s( c_QuasiFactor ); - test( s, arr ); - } - stack_type::gc::force_dispose(); - check_array( arr ); - } - -} // namespace - +/* + 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. +*/ + +#include "test_intrusive_segmented_stack.h" + +#include +#include +#include + +namespace { + namespace ci = cds::intrusive; + typedef cds::gc::DHP gc_type; + + class IntrusiveSegmentedStack_DHP : public cds_test::intrusive_segmented_stack + { + typedef cds_test::intrusive_segmented_stack base_class; + + protected: + static const size_t c_QuasiFactor = 15; + + void SetUp() + { + typedef ci::SegmentedStack< gc_type, item > stack_type; + + cds::gc::dhp::smr::construct( stack_type::c_nHazardPtrCount ); + cds::threading::Manager::attachThread(); + } + + void TearDown() + { + cds::threading::Manager::detachThread(); + cds::gc::dhp::smr::destruct(); + } + + template + void check_array( V& arr ) + { + for ( size_t i = 0; i < arr.size(); ++i ) { + EXPECT_EQ( arr[i].nDisposeCount, 2u ); + EXPECT_EQ( arr[i].nDispose2Count, 1u ); + } + } + }; + + TEST_F( IntrusiveSegmentedStack_DHP, defaulted ) + { + struct stack_traits : public cds::intrusive::segmented_stack::traits + { + typedef Disposer disposer; + }; + typedef cds::intrusive::SegmentedStack< gc_type, item, stack_traits > stack_type; + + std::vector arr; + { + stack_type s( c_QuasiFactor ); + test( s, arr ); + } + stack_type::gc::force_dispose(); + check_array( arr ); + } + + TEST_F( IntrusiveSegmentedStack_DHP, mutex ) + { + struct stack_traits : public + cds::intrusive::segmented_stack::make_traits < + cds::intrusive::opt::disposer< Disposer > + ,cds::opt::lock_type < std::mutex > + > ::type + {}; + typedef cds::intrusive::SegmentedStack< gc_type, item, stack_traits > stack_type; + + std::vector arr; + { + stack_type s( c_QuasiFactor ); + test( s, arr ); + } + stack_type::gc::force_dispose(); + check_array( arr ); + } + + TEST_F( IntrusiveSegmentedStack_DHP, padding ) + { + struct stack_traits : public cds::intrusive::segmented_stack::traits + { + typedef Disposer disposer; + enum { padding = cds::opt::cache_line_padding }; + typedef ci::segmented_stack::stat<> stat; + }; + typedef cds::intrusive::SegmentedStack< gc_type, item, stack_traits > stack_type; + + std::vector arr; + { + stack_type s( c_QuasiFactor ); + test( s, arr ); + } + stack_type::gc::force_dispose(); + check_array( arr ); + } + + TEST_F( IntrusiveSegmentedStack_DHP, bigdata_padding ) + { + struct stack_traits : public cds::intrusive::segmented_stack::traits + { + typedef Disposer disposer; + enum { padding = cds::opt::cache_line_padding | cds::opt::padding_tiny_data_only }; + typedef cds::opt::v::sequential_consistent memory_model; + }; + typedef cds::intrusive::SegmentedStack< gc_type, big_item, stack_traits > stack_type; + + std::vector arr; + { + stack_type s( c_QuasiFactor ); + test( s, arr ); + } + stack_type::gc::force_dispose(); + check_array( arr ); + } + +} // namespace + diff --git a/test/unit/stack/intrusive_segmented_stack_hp.cpp b/test/unit/stack/intrusive_segmented_stack_hp.cpp old mode 100644 new mode 100755 index 1eeb37d54..90960538f --- a/test/unit/stack/intrusive_segmented_stack_hp.cpp +++ b/test/unit/stack/intrusive_segmented_stack_hp.cpp @@ -1,166 +1,147 @@ -/* - 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. -*/ - -#include "test_intrusive_segmented_stack.h" - -#include -#include -#include - -namespace { - namespace ci = cds::intrusive; - typedef cds::gc::HP gc_type; - - class IntrusiveSegmentedStack_HP : public cds_test::intrusive_segmented_stack - { - typedef cds_test::intrusive_segmented_stack base_class; - - protected: - static const size_t c_QuasiFactor = 15; - - void SetUp() - { - typedef ci::SegmentedStack< gc_type, item > stack_type; - - cds::gc::hp::GarbageCollector::Construct( stack_type::c_nHazardPtrCount, 1, 16 ); - cds::threading::Manager::attachThread(); - } - - void TearDown() - { - cds::threading::Manager::detachThread(); - cds::gc::hp::GarbageCollector::Destruct( true ); - } - - template - void check_array( V& arr ) - { - for ( size_t i = 0; i < arr.size(); ++i ) { - EXPECT_EQ( arr[i].nDisposeCount, 2u ); - EXPECT_EQ( arr[i].nDispose2Count, 1u ); - } - } - }; - - TEST_F( IntrusiveSegmentedStack_HP, defaulted ) - { - struct stack_traits : public cds::intrusive::segmented_stack::traits - { - typedef Disposer disposer; - }; - typedef cds::intrusive::SegmentedStack< gc_type, item, stack_traits > stack_type; - - std::vector arr; - { - stack_type s( c_QuasiFactor ); - test( s, arr ); - } - stack_type::gc::force_dispose(); - check_array( arr ); - } - - TEST_F( IntrusiveSegmentedStack_HP, mutex ) - { - struct stack_traits : public - cds::intrusive::segmented_stack::make_traits < - cds::intrusive::opt::disposer< Disposer > - ,cds::opt::lock_type < std::mutex > - > ::type - {}; - typedef cds::intrusive::SegmentedStack< gc_type, item, stack_traits > stack_type; - - std::vector arr; - { - stack_type s( c_QuasiFactor ); - test( s, arr ); - } - stack_type::gc::force_dispose(); - check_array( arr ); - } - - TEST_F( IntrusiveSegmentedStack_HP, shuffle ) - { - typedef cds::intrusive::SegmentedStack< gc_type, item, - cds::intrusive::segmented_stack::make_traits< - cds::intrusive::opt::disposer< Disposer > - ,cds::opt::item_counter< cds::atomicity::item_counter > - ,cds::opt::permutation_generator< cds::opt::v::random_shuffle_permutation<> > - >::type - > stack_type; - - std::vector arr; - { - stack_type s( c_QuasiFactor ); - test( s, arr ); - } - stack_type::gc::force_dispose(); - check_array( arr ); - } - - TEST_F( IntrusiveSegmentedStack_HP, padding ) - { - struct stack_traits : public cds::intrusive::segmented_stack::traits - { - typedef Disposer disposer; - enum { padding = cds::opt::cache_line_padding }; - typedef ci::segmented_stack::stat<> stat; - }; - typedef cds::intrusive::SegmentedStack< gc_type, item, stack_traits > stack_type; - - std::vector arr; - { - stack_type s( c_QuasiFactor ); - test( s, arr ); - } - stack_type::gc::force_dispose(); - check_array( arr ); - } - - TEST_F( IntrusiveSegmentedStack_HP, bigdata_padding ) - { - struct stack_traits : public cds::intrusive::segmented_stack::traits - { - typedef Disposer disposer; - enum { padding = cds::opt::cache_line_padding | cds::opt::padding_tiny_data_only }; - typedef cds::opt::v::sequential_consistent memory_model; - }; - typedef cds::intrusive::SegmentedStack< gc_type, big_item, stack_traits > stack_type; - - std::vector arr; - { - stack_type s( c_QuasiFactor ); - test( s, arr ); - } - stack_type::gc::force_dispose(); - check_array( arr ); - } - -} // namespace - +/* + 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. +*/ + +#include "test_intrusive_segmented_stack.h" + +#include +#include +#include + +namespace { + namespace ci = cds::intrusive; + typedef cds::gc::HP gc_type; + + class IntrusiveSegmentedStack_HP : public cds_test::intrusive_segmented_stack + { + typedef cds_test::intrusive_segmented_stack base_class; + + protected: + static const size_t c_QuasiFactor = 15; + + void SetUp() + { + typedef ci::SegmentedStack< gc_type, item > stack_type; + + cds::gc::hp::GarbageCollector::Construct( stack_type::c_nHazardPtrCount, 1, 16 ); + cds::threading::Manager::attachThread(); + } + + void TearDown() + { + cds::threading::Manager::detachThread(); + cds::gc::hp::GarbageCollector::Destruct( true ); + } + + template + void check_array( V& arr ) + { + for ( size_t i = 0; i < arr.size(); ++i ) { + EXPECT_EQ( arr[i].nDisposeCount, 2u ); + EXPECT_EQ( arr[i].nDispose2Count, 1u ); + } + } + }; + + TEST_F( IntrusiveSegmentedStack_HP, defaulted ) + { + struct stack_traits : public cds::intrusive::segmented_stack::traits + { + typedef Disposer disposer; + }; + typedef cds::intrusive::SegmentedStack< gc_type, item, stack_traits > stack_type; + + std::vector arr; + { + stack_type s( c_QuasiFactor ); + test( s, arr ); + } + stack_type::gc::force_dispose(); + check_array( arr ); + } + + TEST_F( IntrusiveSegmentedStack_HP, mutex ) + { + struct stack_traits : public + cds::intrusive::segmented_stack::make_traits < + cds::intrusive::opt::disposer< Disposer > + ,cds::opt::lock_type < std::mutex > + > ::type + {}; + typedef cds::intrusive::SegmentedStack< gc_type, item, stack_traits > stack_type; + + std::vector arr; + { + stack_type s( c_QuasiFactor ); + test( s, arr ); + } + stack_type::gc::force_dispose(); + check_array( arr ); + } + + TEST_F( IntrusiveSegmentedStack_HP, padding ) + { + struct stack_traits : public cds::intrusive::segmented_stack::traits + { + typedef Disposer disposer; + enum { padding = cds::opt::cache_line_padding }; + typedef ci::segmented_stack::stat<> stat; + }; + typedef cds::intrusive::SegmentedStack< gc_type, item, stack_traits > stack_type; + + std::vector arr; + { + stack_type s( c_QuasiFactor ); + test( s, arr ); + } + stack_type::gc::force_dispose(); + check_array( arr ); + } + + TEST_F( IntrusiveSegmentedStack_HP, bigdata_padding ) + { + struct stack_traits : public cds::intrusive::segmented_stack::traits + { + typedef Disposer disposer; + enum { padding = cds::opt::cache_line_padding | cds::opt::padding_tiny_data_only }; + typedef cds::opt::v::sequential_consistent memory_model; + }; + typedef cds::intrusive::SegmentedStack< gc_type, big_item, stack_traits > stack_type; + + std::vector arr; + { + stack_type s( c_QuasiFactor ); + test( s, arr ); + } + stack_type::gc::force_dispose(); + check_array( arr ); + } + +} // namespace + diff --git a/test/unit/stack/segmented_stack_dhp.cpp b/test/unit/stack/segmented_stack_dhp.cpp old mode 100644 new mode 100755 index 021c84c11..bcc10380d --- a/test/unit/stack/segmented_stack_dhp.cpp +++ b/test/unit/stack/segmented_stack_dhp.cpp @@ -1,114 +1,111 @@ -/* - 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. -*/ - -#include "test_segmented_stack.h" - -#include -#include - -namespace { - namespace cc = cds::container; - typedef cds::gc::DHP gc_type; - - - class SegmentedStack_DHP : public cds_test::segmented_stack - { - protected: - static const size_t c_QuasiFactor = 15; - void SetUp() - { - typedef cc::SegmentedStack< gc_type, int > stack_type; - - cds::gc::dhp::smr::construct( stack_type::c_nHazardPtrCount ); - cds::threading::Manager::attachThread(); - } - - void TearDown() - { - cds::threading::Manager::detachThread(); - cds::gc::dhp::smr::destruct(); - } - }; - - TEST_F( SegmentedStack_DHP, defaulted ) - { - typedef cds::container::SegmentedStack< gc_type, int > test_stack; - - test_stack s( c_QuasiFactor ); - ASSERT_EQ( s.quasi_factor(), cds::beans::ceil2( c_QuasiFactor )); - test(s); - } - - TEST_F( SegmentedStack_DHP, mutex ) - { - struct traits : public cds::container::segmented_stack::traits - { - typedef cds::atomicity::item_counter item_counter; - typedef cds::opt::v::random_shuffle_permutation<> permutation_generator; - }; - typedef cds::container::SegmentedStack< gc_type, int, traits > test_stack; - - test_stack s( c_QuasiFactor ); - ASSERT_EQ( s.quasi_factor(), cds::beans::ceil2( c_QuasiFactor )); - test( s ); - } - - TEST_F( SegmentedStack_DHP, shuffle ) - { - struct traits : public cds::container::segmented_stack::traits - { - typedef cds::atomicity::item_counter item_counter; - typedef cds::opt::v::random_shuffle_permutation<> permutation_generator; - }; - typedef cds::container::SegmentedStack< gc_type, int, traits > test_stack; - - test_stack s( c_QuasiFactor ); - ASSERT_EQ( s.quasi_factor(), cds::beans::ceil2( c_QuasiFactor )); - test( s ); - } - - TEST_F( SegmentedStack_DHP, stat ) - { - struct traits : public - cds::container::segmented_stack::make_traits < - cds::opt::item_counter< cds::atomicity::item_counter > - , cds::opt::permutation_generator< cds::opt::v::random_permutation<> > - , cds::opt::stat < cds::container::segmented_stack::stat<> > - > ::type - {}; - typedef cds::container::SegmentedStack< gc_type, int, traits > test_stack; - - test_stack s( c_QuasiFactor ); - ASSERT_EQ( s.quasi_factor(), cds::beans::ceil2( c_QuasiFactor )); - test( s ); - } - -} // namespace - +/* + 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. +*/ + +#include "test_segmented_stack.h" + +#include +#include + +namespace { + namespace cc = cds::container; + typedef cds::gc::DHP gc_type; + + + class SegmentedStack_DHP : public cds_test::segmented_stack + { + protected: + static const size_t c_QuasiFactor = 15; + void SetUp() + { + typedef cc::SegmentedStack< gc_type, int > stack_type; + + cds::gc::dhp::smr::construct( stack_type::c_nHazardPtrCount ); + cds::threading::Manager::attachThread(); + } + + void TearDown() + { + cds::threading::Manager::detachThread(); + cds::gc::dhp::smr::destruct(); + } + }; + + TEST_F( SegmentedStack_DHP, defaulted ) + { + typedef cds::container::SegmentedStack< gc_type, int > test_stack; + + test_stack s( c_QuasiFactor ); + ASSERT_EQ( s.quasi_factor(), cds::beans::ceil2( c_QuasiFactor )); + test(s); + } + + TEST_F( SegmentedStack_DHP, mutex ) + { + struct traits : public cds::container::segmented_stack::traits + { + typedef cds::atomicity::item_counter item_counter; + }; + typedef cds::container::SegmentedStack< gc_type, int, traits > test_stack; + + test_stack s( c_QuasiFactor ); + ASSERT_EQ( s.quasi_factor(), cds::beans::ceil2( c_QuasiFactor )); + test( s ); + } + + TEST_F( SegmentedStack_DHP, shuffle ) + { + struct traits : public cds::container::segmented_stack::traits + { + typedef cds::atomicity::item_counter item_counter; + }; + typedef cds::container::SegmentedStack< gc_type, int, traits > test_stack; + + test_stack s( c_QuasiFactor ); + ASSERT_EQ( s.quasi_factor(), cds::beans::ceil2( c_QuasiFactor )); + test( s ); + } + + TEST_F( SegmentedStack_DHP, stat ) + { + struct traits : public + cds::container::segmented_stack::make_traits < + cds::opt::item_counter< cds::atomicity::item_counter > + , cds::opt::stat < cds::container::segmented_stack::stat<> > + > ::type + {}; + typedef cds::container::SegmentedStack< gc_type, int, traits > test_stack; + + test_stack s( c_QuasiFactor ); + ASSERT_EQ( s.quasi_factor(), cds::beans::ceil2( c_QuasiFactor )); + test( s ); + } + +} // namespace + diff --git a/test/unit/stack/segmented_stack_hp.cpp b/test/unit/stack/segmented_stack_hp.cpp old mode 100644 new mode 100755 index 5d8d63850..f2c662bab --- a/test/unit/stack/segmented_stack_hp.cpp +++ b/test/unit/stack/segmented_stack_hp.cpp @@ -1,114 +1,98 @@ -/* - 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. -*/ - -#include "test_segmented_stack.h" - -#include -#include - -namespace { - namespace cc = cds::container; - typedef cds::gc::HP gc_type; - - - class SegmentedStack_HP : public cds_test::segmented_stack - { - protected: - static const size_t c_QuasiFactor = 15; - void SetUp() - { - typedef cc::SegmentedStack< gc_type, int > stack_type; - - cds::gc::hp::GarbageCollector::Construct( stack_type::c_nHazardPtrCount, 1, 16 ); - cds::threading::Manager::attachThread(); - } - - void TearDown() - { - cds::threading::Manager::detachThread(); - cds::gc::hp::GarbageCollector::Destruct( true ); - } - }; - - TEST_F( SegmentedStack_HP, defaulted ) - { - typedef cds::container::SegmentedStack< gc_type, int > test_stack; - - test_stack s( c_QuasiFactor ); - ASSERT_EQ( s.quasi_factor(), cds::beans::ceil2( c_QuasiFactor )); - test(s); - } - - TEST_F( SegmentedStack_HP, mutex ) - { - struct traits : public cds::container::segmented_stack::traits - { - typedef cds::atomicity::item_counter item_counter; - typedef cds::opt::v::random_shuffle_permutation<> permutation_generator; - }; - typedef cds::container::SegmentedStack< cds::gc::HP, int, traits > test_stack; - - test_stack s( c_QuasiFactor ); - ASSERT_EQ( s.quasi_factor(), cds::beans::ceil2( c_QuasiFactor )); - test( s ); - } - - TEST_F( SegmentedStack_HP, shuffle ) - { - struct traits : public cds::container::segmented_stack::traits - { - typedef cds::atomicity::item_counter item_counter; - typedef cds::opt::v::random_shuffle_permutation<> permutation_generator; - }; - typedef cds::container::SegmentedStack< cds::gc::HP, int, traits > test_stack; - - test_stack s( c_QuasiFactor ); - ASSERT_EQ( s.quasi_factor(), cds::beans::ceil2( c_QuasiFactor )); - test( s ); - } - - TEST_F( SegmentedStack_HP, stat ) - { - struct traits : public - cds::container::segmented_stack::make_traits < - cds::opt::item_counter< cds::atomicity::item_counter > - , cds::opt::permutation_generator< cds::opt::v::random_permutation<> > - , cds::opt::stat < cds::container::segmented_stack::stat<> > - > ::type - {}; - typedef cds::container::SegmentedStack< cds::gc::HP, int, traits > test_stack; - - test_stack s( c_QuasiFactor ); - ASSERT_EQ( s.quasi_factor(), cds::beans::ceil2( c_QuasiFactor )); - test( s ); - } - -} // namespace - +/* + 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. +*/ + +#include "test_segmented_stack.h" + +#include +#include + +namespace { + namespace cc = cds::container; + typedef cds::gc::HP gc_type; + + + class SegmentedStack_HP : public cds_test::segmented_stack + { + protected: + static const size_t c_QuasiFactor = 15; + void SetUp() + { + typedef cc::SegmentedStack< gc_type, int > stack_type; + + cds::gc::hp::GarbageCollector::Construct( stack_type::c_nHazardPtrCount, 1, 16 ); + cds::threading::Manager::attachThread(); + } + + void TearDown() + { + cds::threading::Manager::detachThread(); + cds::gc::hp::GarbageCollector::Destruct( true ); + } + }; + + TEST_F( SegmentedStack_HP, defaulted ) + { + typedef cds::container::SegmentedStack< gc_type, int > test_stack; + + test_stack s( c_QuasiFactor ); + ASSERT_EQ( s.quasi_factor(), cds::beans::ceil2( c_QuasiFactor )); + test(s); + } + + TEST_F( SegmentedStack_HP, mutex ) + { + struct traits : public cds::container::segmented_stack::traits + { + typedef cds::atomicity::item_counter item_counter; + }; + typedef cds::container::SegmentedStack< cds::gc::HP, int, traits > test_stack; + + test_stack s( c_QuasiFactor ); + ASSERT_EQ( s.quasi_factor(), cds::beans::ceil2( c_QuasiFactor )); + test( s ); + } + + TEST_F( SegmentedStack_HP, stat ) + { + struct traits : public + cds::container::segmented_stack::make_traits < + cds::opt::item_counter< cds::atomicity::item_counter > + , cds::opt::stat < cds::container::segmented_stack::stat<> > + > ::type + {}; + typedef cds::container::SegmentedStack< cds::gc::HP, int, traits > test_stack; + + test_stack s( c_QuasiFactor ); + ASSERT_EQ( s.quasi_factor(), cds::beans::ceil2( c_QuasiFactor )); + test( s ); + } + +} // namespace + diff --git a/test/unit/stack/test_intrusive_segmented_stack.h b/test/unit/stack/test_intrusive_segmented_stack.h old mode 100644 new mode 100755 index caad40ce2..6a5d42d5d --- a/test/unit/stack/test_intrusive_segmented_stack.h +++ b/test/unit/stack/test_intrusive_segmented_stack.h @@ -1,178 +1,178 @@ -/* - 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 CDSUNIT_STACK_TEST_INTRUSIVE_SEGMENTED_STACK_H -#define CDSUNIT_STACK_TEST_INTRUSIVE_SEGMENTED_STACK_H - -#include - -namespace cds_test { - - class intrusive_segmented_stack : public ::testing::Test - { - protected: - struct item { - int nValue; - - size_t nDisposeCount; - size_t nDispose2Count; - - item() - : nValue( 0 ) - , nDisposeCount( 0 ) - , nDispose2Count( 0 ) - {} - - item( int nVal ) - : nValue( nVal ) - , nDisposeCount( 0 ) - , nDispose2Count( 0 ) - {} - }; - - struct big_item : public item - { - big_item() - {} - - big_item( int nVal ) - : item( nVal ) - {} - - int arr[80]; - }; - - struct Disposer - { - void operator()( item * p ) - { - ++p->nDisposeCount; - } - }; - - struct Disposer2 - { - void operator()( item * p ) - { - ++p->nDispose2Count; - } - }; - - template - void test( Stack& s, Data& val ) - { - typedef typename Stack::value_type value_type; - val.resize( 100 ); - for ( size_t i = 0; i < val.size(); ++i ) - val[i].nValue = static_cast( i ); - - ASSERT_TRUE( s.empty()); - ASSERT_CONTAINER_SIZE( s, 0u ); - - // push - for ( size_t i = 0; i < val.size(); ++i ) { - ASSERT_TRUE( s.push( val[i] )); - - ASSERT_CONTAINER_SIZE( s, i + 1 ); - } - EXPECT_TRUE( !s.empty()); - - // pop - size_t nCount = 0; - while ( !s.empty()) { - value_type * pVal; - pVal = s.pop(); - - ASSERT_TRUE( pVal != nullptr ); - - ++nCount; - EXPECT_CONTAINER_SIZE( s, val.size() - nCount ); - } - EXPECT_EQ( nCount, val.size()); - EXPECT_TRUE( s.empty()); - EXPECT_CONTAINER_SIZE( s, 0u ); - - // pop from empty stack - ASSERT_TRUE( s.pop() == nullptr ); - EXPECT_TRUE( s.empty()); - EXPECT_CONTAINER_SIZE( s, 0u ); - - // check that Disposer has not been called - Stack::gc::force_dispose(); - for ( size_t i = 0; i < val.size(); ++i ) { - EXPECT_EQ( val[i].nDisposeCount, 0u ); - EXPECT_EQ( val[i].nDispose2Count, 0u ); - } - - // clear - for ( size_t i = 0; i < val.size(); ++i ) - EXPECT_TRUE( s.push( val[i] )); - EXPECT_CONTAINER_SIZE( s, val.size()); - EXPECT_TRUE( !s.empty()); - - s.clear(); - EXPECT_CONTAINER_SIZE( s, 0u ); - EXPECT_TRUE( s.empty()); - - // check if Disposer has been called - Stack::gc::force_dispose(); - for ( size_t i = 0; i < val.size(); ++i ) { - EXPECT_EQ( val[i].nDisposeCount, 1u ); - EXPECT_EQ( val[i].nDispose2Count, 0u ); - } - - // clear_with - for ( size_t i = 0; i < val.size(); ++i ) - EXPECT_TRUE( s.push( val[i] )); - EXPECT_CONTAINER_SIZE( s, val.size()); - EXPECT_TRUE( !s.empty()); - - s.clear_with( Disposer2()); - EXPECT_CONTAINER_SIZE( s, 0u ); - EXPECT_TRUE( s.empty()); - - // check if Disposer has been called - Stack::gc::force_dispose(); - for ( size_t i = 0; i < val.size(); ++i ) { - EXPECT_EQ( val[i].nDisposeCount, 1u ); - EXPECT_EQ( val[i].nDispose2Count, 1u ); - } - - // check clear on destruct - for ( size_t i = 0; i < val.size(); ++i ) - EXPECT_TRUE( s.push( val[i] )); - EXPECT_CONTAINER_SIZE( s, val.size()); - EXPECT_TRUE( !s.empty()); - } - }; - -} // namespace cds_test - -#endif // CDSUNIT_STACK_TEST_INTRUSIVE_SEGMENTED_STACK_H +/* + 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 CDSUNIT_STACK_TEST_INTRUSIVE_SEGMENTED_STACK_H +#define CDSUNIT_STACK_TEST_INTRUSIVE_SEGMENTED_STACK_H + +#include + +namespace cds_test { + + class intrusive_segmented_stack : public ::testing::Test + { + protected: + struct item { + int nValue; + + size_t nDisposeCount; + size_t nDispose2Count; + + item() + : nValue( 0 ) + , nDisposeCount( 0 ) + , nDispose2Count( 0 ) + {} + + item( int nVal ) + : nValue( nVal ) + , nDisposeCount( 0 ) + , nDispose2Count( 0 ) + {} + }; + + struct big_item : public item + { + big_item() + {} + + big_item( int nVal ) + : item( nVal ) + {} + + int arr[80]; + }; + + struct Disposer + { + void operator()( item * p ) + { + ++p->nDisposeCount; + } + }; + + struct Disposer2 + { + void operator()( item * p ) + { + ++p->nDispose2Count; + } + }; + + template + void test( Stack& s, Data& val ) + { + typedef typename Stack::value_type value_type; + val.resize( 100 ); + for ( size_t i = 0; i < val.size(); ++i ) + val[i].nValue = static_cast( i ); + + ASSERT_TRUE( s.empty()); + ASSERT_CONTAINER_SIZE( s, 0u ); + + // push + for ( size_t i = 0; i < val.size(); ++i ) { + ASSERT_TRUE( s.push( val[i] )); + + ASSERT_CONTAINER_SIZE( s, i + 1 ); + } + EXPECT_TRUE( !s.empty()); + + // pop + size_t nCount = 0; + while ( !s.empty()) { + value_type * pVal; + pVal = s.pop(); + + ASSERT_TRUE( pVal != nullptr ); + + ++nCount; + EXPECT_CONTAINER_SIZE( s, val.size() - nCount ); + } + EXPECT_EQ( nCount, val.size()); + EXPECT_TRUE( s.empty()); + EXPECT_CONTAINER_SIZE( s, 0u ); + + // pop from empty stack + ASSERT_TRUE( s.pop() == nullptr ); + EXPECT_TRUE( s.empty()); + EXPECT_CONTAINER_SIZE( s, 0u ); + + // check that Disposer has not been called + Stack::gc::force_dispose(); + for ( size_t i = 0; i < val.size(); ++i ) { + EXPECT_EQ( val[i].nDisposeCount, 0u ); + EXPECT_EQ( val[i].nDispose2Count, 0u ); + } + + // clear + for ( size_t i = 0; i < val.size(); ++i ) + EXPECT_TRUE( s.push( val[i] )); + EXPECT_CONTAINER_SIZE( s, val.size()); + EXPECT_TRUE( !s.empty()); + + s.clear(); + EXPECT_CONTAINER_SIZE( s, 0u ); + EXPECT_TRUE( s.empty()); + + // check if Disposer has been called + Stack::gc::force_dispose(); + for ( size_t i = 0; i < val.size(); ++i ) { + EXPECT_EQ( val[i].nDisposeCount, 1u ); + EXPECT_EQ( val[i].nDispose2Count, 0u ); + } + + // clear_with + for ( size_t i = 0; i < val.size(); ++i ) + EXPECT_TRUE( s.push( val[i] )); + EXPECT_CONTAINER_SIZE( s, val.size()); + EXPECT_TRUE( !s.empty()); + + s.clear_with( Disposer2()); + EXPECT_CONTAINER_SIZE( s, 0u ); + EXPECT_TRUE( s.empty()); + + // check if Disposer has been called + Stack::gc::force_dispose(); + for ( size_t i = 0; i < val.size(); ++i ) { + EXPECT_EQ( val[i].nDisposeCount, 1u ); + EXPECT_EQ( val[i].nDispose2Count, 1u ); + } + + // check clear on destruct + for ( size_t i = 0; i < val.size(); ++i ) + EXPECT_TRUE( s.push( val[i] )); + EXPECT_CONTAINER_SIZE( s, val.size()); + EXPECT_TRUE( !s.empty()); + } + }; + +} // namespace cds_test + +#endif // CDSUNIT_STACK_TEST_INTRUSIVE_SEGMENTED_STACK_H diff --git a/test/unit/stack/test_segmented_stack.h b/test/unit/stack/test_segmented_stack.h old mode 100644 new mode 100755 index d0278edcb..e2b9ca5ae --- a/test/unit/stack/test_segmented_stack.h +++ b/test/unit/stack/test_segmented_stack.h @@ -1,89 +1,89 @@ -/* - 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 CDSUNIT_STACK_TEST_SEGMENTED_STACK_H -#define CDSUNIT_STACK_TEST_SEGMENTED_STACK_H - -#include - -namespace cds_test { - - class segmented_stack : public ::testing::Test - { - protected: - template - void test( Stack& s ) - { - typedef typename Stack::value_type value_type; - value_type it; - - const size_t nSize = 100; - - ASSERT_TRUE( s.empty()); - ASSERT_CONTAINER_SIZE( s, 0 ); - // push/pop - for ( size_t i = 0; i < nSize; ++i ) { - it = static_cast(i); - ASSERT_TRUE( s.push( it )); - ASSERT_CONTAINER_SIZE( s, i + 1 ); - } - ASSERT_FALSE( s.empty()); - ASSERT_CONTAINER_SIZE( s, nSize ); - - for ( size_t i = 0; i < nSize; ++i ) { - it = -1; - ASSERT_TRUE( s.pop(it)); - ASSERT_CONTAINER_SIZE( s, nSize - i - 1 ); - } - ASSERT_TRUE( s.empty()); - ASSERT_CONTAINER_SIZE( s, 0 ); - - // clear - for ( size_t i = 0; i < nSize; ++i ) { - ASSERT_TRUE( s.push( static_cast(i))); - } - ASSERT_FALSE( s.empty()); - ASSERT_CONTAINER_SIZE( s, nSize ); - s.clear(); - ASSERT_TRUE( s.empty()); - ASSERT_CONTAINER_SIZE( s, 0 ); - - // pop from empty stack - it = nSize * 2; - ASSERT_FALSE( s.pop( it )); - ASSERT_EQ( it, static_cast( nSize * 2 )); - ASSERT_TRUE( s.empty()); - ASSERT_CONTAINER_SIZE( s, 0 ); - } - }; - -} // namespace cds_test - -#endif // CDSUNIT_STACK_TEST_SEGMENTED_STACK_H +/* + 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 CDSUNIT_STACK_TEST_SEGMENTED_STACK_H +#define CDSUNIT_STACK_TEST_SEGMENTED_STACK_H + +#include + +namespace cds_test { + + class segmented_stack : public ::testing::Test + { + protected: + template + void test( Stack& s ) + { + typedef typename Stack::value_type value_type; + value_type it; + + const size_t nSize = 100; + + ASSERT_TRUE( s.empty()); + ASSERT_CONTAINER_SIZE( s, 0 ); + // push/pop + for ( size_t i = 0; i < nSize; ++i ) { + it = static_cast(i); + ASSERT_TRUE( s.push( it )); + ASSERT_CONTAINER_SIZE( s, i + 1 ); + } + ASSERT_FALSE( s.empty()); + ASSERT_CONTAINER_SIZE( s, nSize ); + + for ( size_t i = 0; i < nSize; ++i ) { + it = -1; + ASSERT_TRUE( s.pop(it)); + ASSERT_CONTAINER_SIZE( s, nSize - i - 1 ); + } + ASSERT_TRUE( s.empty()); + ASSERT_CONTAINER_SIZE( s, 0 ); + + // clear + for ( size_t i = 0; i < nSize; ++i ) { + ASSERT_TRUE( s.push( static_cast(i))); + } + ASSERT_FALSE( s.empty()); + ASSERT_CONTAINER_SIZE( s, nSize ); + s.clear(); + ASSERT_TRUE( s.empty()); + ASSERT_CONTAINER_SIZE( s, 0 ); + + // pop from empty stack + it = nSize * 2; + ASSERT_FALSE( s.pop( it )); + ASSERT_EQ( it, static_cast( nSize * 2 )); + ASSERT_TRUE( s.empty()); + ASSERT_CONTAINER_SIZE( s, 0 ); + } + }; + +} // namespace cds_test + +#endif // CDSUNIT_STACK_TEST_SEGMENTED_STACK_H From c489b19ca1942204c20f09034d657946220ea4b9 Mon Sep 17 00:00:00 2001 From: komarkom Date: Tue, 19 Dec 2017 21:50:34 +0300 Subject: [PATCH 9/9] refactored --- cds/intrusive/segmented_stack.h | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/cds/intrusive/segmented_stack.h b/cds/intrusive/segmented_stack.h index cbdd8f9aa..9d490c422 100755 --- a/cds/intrusive/segmented_stack.h +++ b/cds/intrusive/segmented_stack.h @@ -439,7 +439,8 @@ namespace cds { namespace intrusive { while ( true ) { CDS_DEBUG_ONLY( size_t nLoopCount = 0); - int i = quasi_factor(); + size_t i = 0; + size_t qf = quasi_factor(); do { CDS_DEBUG_ONLY( ++nLoopCount ); if (pHeadSegment->cells[i].data.load(memory_model::memory_order_relaxed).all()) { @@ -456,11 +457,13 @@ namespace cds { namespace intrusive { m_Stat.onPush(); return true; } + else + continue; assert( nullCell.ptr()); m_Stat.onPushContended(); } - i--; - } while (i >= 0); + ++i; + } while (i <= qf); // No available position, create a new segment pHeadSegment = m_SegmentList.create_head(pHeadSegment, segmentGuard ); @@ -536,28 +539,29 @@ namespace cds { namespace intrusive { } regular_cell item; CDS_DEBUG_ONLY( size_t nLoopCount = 0 ); - int i = quasi_factor(); + size_t i = 0; + size_t qf = quasi_factor(); do { CDS_DEBUG_ONLY( ++nLoopCount ); // Guard the item // In segmented stack 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 ); + item = pHeadSegment->cells[qf-i].data.load( memory_model::memory_order_relaxed ); itemGuard.assign( item.ptr()); // Check if this cell is empty, which means an element // can be pushed to this cell in the future if ( !item.ptr()) { - i--; + ++i; continue; } else { // If the item is not deleted yet if ( !item.bits()) { // Try to mark the cell as deleted - if ( pHeadSegment->cells[i].data.compare_exchange_strong( item, item | 1, + if ( pHeadSegment->cells[qf-i].data.compare_exchange_strong( item, item | 1, memory_model::memory_order_acquire, atomics::memory_order_relaxed )) { --m_ItemCounter; @@ -565,12 +569,14 @@ namespace cds { namespace intrusive { return true; } + else + continue; assert( item.bits()); m_Stat.onPopContended(); } } - i--; - } while ( i >= 0); + ++i; + } while ( i <= qf); // All nodes have been poped, we can safely remove the first segment pHeadSegment = m_SegmentList.remove_head( pHeadSegment, segmentGuard );