diff --git a/cds/container/speculative_pairing_queue.h b/cds/container/speculative_pairing_queue.h new file mode 100644 index 000000000..365b0a143 --- /dev/null +++ b/cds/container/speculative_pairing_queue.h @@ -0,0 +1,403 @@ +// Copyright (c) 2006-2018 Maxim Khizhinsky +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef CDSLIB_CONTAINER_SPECULATIVE_PAIRING_QUEUE_H +#define CDSLIB_CONTAINER_SPECULATIVE_PAIRING_QUEUE_H + +#include +#include +#include + +namespace cds { namespace container { + + /// MSQueue related definitions + /** @ingroup cds_nonintrusive_helper + */ + namespace speculative_pairing_queue { + /// Internal statistics + template ::counter_type > + using stat = cds::intrusive::speculative_pairing_queue::stat< Counter >; + + /// Dummy internal statistics + typedef cds::intrusive::speculative_pairing_queue::empty_stat empty_stat; + + /// MSQueue default type traits + struct traits + { + /// Node allocator + typedef CDS_DEFAULT_ALLOCATOR allocator; + + /// Back-off strategy + typedef cds::backoff::empty back_off; + + /// Item counting feature; by default, disabled. Use \p cds::atomicity::item_counter to enable item counting + typedef atomicity::empty_item_counter item_counter; + + /// Internal statistics (by default, disabled) + /** + Possible option value are: \p msqueue::stat, \p msqueue::empty_stat (the default), + user-provided class that supports \p %msqueue::stat interface. + */ + typedef speculative_pairing_queue::empty_stat stat; + + /// C++ memory ordering model + /** + Can be \p opt::v::relaxed_ordering (relaxed memory model, the default) + or \p opt::v::sequential_consistent (sequentially consisnent memory model). + */ + typedef opt::v::relaxed_ordering memory_model; + + /// Padding for internal critical atomic data. Default is \p opt::cache_line_padding + enum { padding = opt::cache_line_padding }; + }; + + /// Metafunction converting option list to \p msqueue::traits + /** + Supported \p Options are: + - \p opt::allocator - allocator (like \p std::allocator) used for allocating queue nodes. Default is \ref CDS_DEFAULT_ALLOCATOR + - \p opt::back_off - back-off strategy used, default is \p cds::backoff::empty. + - \p opt::item_counter - the type of item counting feature. Default is \p cds::atomicity::empty_item_counter (item counting disabled) + To enable item counting use \p cds::atomicity::item_counter + - \p opt::stat - the type to gather internal statistics. + Possible statistics types are: \p msqueue::stat, \p msqueue::empty_stat, user-provided class that supports \p %msqueue::stat interface. + Default is \p %msqueue::empty_stat. + - \p opt::padding - padding for internal critical atomic data. Default is \p opt::cache_line_padding + - \p opt::memory_model - C++ memory ordering model. Can be \p opt::v::relaxed_ordering (relaxed memory model, the default) + or \p opt::v::sequential_consistent (sequentially consisnent memory model). + + Example: declare \p %MSQueue with item counting and internal statistics + \code + typedef cds::container::MSQueue< cds::gc::HP, Foo, + typename cds::container::msqueue::make_traits< + cds::opt::item_counter< cds::atomicity::item_counter >, + cds::opt::stat< cds::container::msqueue::stat<> > + >::type + > myQueue; + \endcode + */ + 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 msqueue + + //@cond + namespace details { + template + struct make_spqueue + { + typedef GC gc; + typedef T value_type; + typedef Traits traits; + + struct node_type : public intrusive::sp_queue::node< gc > + { + value_type m_value; + + node_type( value_type const& val ) + : m_value( val ) + {} + + template + node_type( Args&&... args ) + : m_value( std::forward( args )... ) + {} + }; + + typedef typename traits::allocator::template rebind::other allocator_type; + typedef cds::details::Allocator< node_type, allocator_type > cxx_allocator; + + struct node_deallocator + { + void operator ()( node_type * pNode ) + { + cxx_allocator().Delete( pNode ); + } + }; + + struct intrusive_traits : public traits + { + typedef cds::intrusive::speculative_pairing_queue::base_hook< cds::opt::gc > hook; + typedef node_deallocator disposer; + static constexpr const cds::intrusive::opt::link_check_type link_checker = cds::intrusive::speculative_pairing_queue::traits::link_checker; + }; + + typedef intrusive::SPQueue< gc, node_type, intrusive_traits > type; + }; + } + //@endcond + + /// Michael & Scott lock-free queue + /** @ingroup cds_nonintrusive_queue + It is non-intrusive version of Michael & Scott's queue algorithm based on intrusive implementation + \p cds::intrusive::MSQueue. + + Template arguments: + - \p GC - garbage collector type: \p gc::HP, \p gc::DHP + - \p T is a type stored in the queue. + - \p Traits - queue traits, default is \p msqueue::traits. You can use \p msqueue::make_traits + metafunction to make your traits or just derive your traits from \p %msqueue::traits: + \code + struct myTraits: public cds::container::msqueue::traits { + typedef cds::intrusive::msqueue::stat<> stat; + typedef cds::atomicity::item_counter item_counter; + }; + typedef cds::container::MSQueue< cds::gc::HP, Foo, myTraits > myQueue; + + // Equivalent make_traits example: + typedef cds::container::MSQueue< cds::gc::HP, Foo, + typename cds::container::msqueue::make_traits< + cds::opt::stat< cds::container::msqueue::stat<> >, + cds::opt::item_counter< cds::atomicity::item_counter > + >::type + > myQueue; + \endcode + */ + template + class SPQueue: +#ifdef CDS_DOXYGEN_INVOKED + private intrusive::SPQueue< GC, cds::intrusive::speculative_pairing_queue::node< T >, Traits > +#else + private details::make_spqueue< GC, T, Traits >::type +#endif + { + //@cond + typedef details::make_spqueue< GC, T, Traits > maker; + typedef typename maker::type base_class; + //@endcond + + public: + /// Rebind template arguments + template + struct rebind { + typedef SPQueue< GC2, T2, Traits2> other ; ///< Rebinding result + }; + + public: + typedef T value_type; ///< Value type stored in the queue + typedef Traits traits; ///< Queue traits + + typedef typename base_class::gc gc; ///< Garbage collector used + typedef typename base_class::back_off back_off; ///< Back-off strategy used + typedef typename maker::allocator_type allocator_type; ///< Allocator type used for allocate/deallocate the nodes + typedef typename base_class::item_counter item_counter; ///< Item counting policy used + typedef typename base_class::stat stat; ///< Internal statistics policy used + typedef typename base_class::memory_model memory_model; ///< Memory ordering. See cds::opt::memory_model option + + static constexpr const size_t c_nHazardPtrCount = base_class::c_nHazardPtrCount; ///< Count of hazard pointer required for the algorithm + + protected: + //@cond + typedef typename maker::node_type node_type; ///< queue node type (derived from \p intrusive::msqueue::node) + + typedef typename maker::cxx_allocator cxx_allocator; + typedef typename maker::node_deallocator node_deallocator; // deallocate node + typedef typename base_class::node_traits node_traits; + //@endcond + + protected: + ///@cond + static node_type * alloc_node() + { + return cxx_allocator().New(); + } + static node_type * alloc_node( value_type const& val ) + { + return cxx_allocator().New( val ); + } + template + static node_type * alloc_node_move( Args&&... args ) + { + return cxx_allocator().MoveNew( std::forward( args )... ); + } + static void free_node( node_type * p ) + { + node_deallocator()( p ); + } + + struct node_disposer { + void operator()( node_type * pNode ) + { + free_node( pNode ); + } + }; + typedef std::unique_ptr< node_type, node_disposer > scoped_node_ptr; + //@endcond + + public: + /// Initializes empty queue + SPQueue() + {} + + /// Destructor clears the queue + ~SPQueue() + {} + + /// Enqueues \p val value into the queue. + /** + The function makes queue node in dynamic memory calling copy constructor for \p val + and then it calls \p intrusive::MSQueue::enqueue. + Returns \p true if success, \p false otherwise. + */ + bool enqueue( value_type const& val ) + { + scoped_node_ptr p( alloc_node(val)); + if ( base_class::enqueue( *p )) { + p.release(); + return true; + } + return false; + } + + /// Enqueues \p val in the queue, move semantics + bool enqueue( value_type&& val ) + { + scoped_node_ptr p( alloc_node_move( std::move( val ))); + if ( base_class::enqueue( *p )) { + p.release(); + return true; + } + return false; + } + + /// Enqueues data to the queue using a functor + /** + \p Func is a functor called to create node. + The functor \p f takes one argument - a reference to a new node of type \ref value_type : + \code + cds::container::MSQueue< cds::gc::HP, Foo > myQueue; + Bar bar; + myQueue.enqueue_with( [&bar]( Foo& dest ) { dest = bar; } ); + \endcode + */ + template + bool enqueue_with( Func f ) + { + scoped_node_ptr p( alloc_node()); + f( p->m_value ); + if ( base_class::enqueue( *p )) { + p.release(); + return true; + } + return false; + } + + /// Enqueues data of type \ref value_type constructed from std::forward(args)... + template + bool emplace( Args&&... args ) + { + scoped_node_ptr p( alloc_node_move( std::forward( args )... )); + if ( base_class::enqueue( *p )) { + p.release(); + return true; + } + return false; + } + + /// Synonym for \p enqueue() function + bool push( value_type const& val ) + { + return enqueue( val ); + } + + /// Synonym for \p enqueue() function + bool push( value_type&& val ) + { + return enqueue( std::move( val )); + } + + /// Synonym for \p enqueue_with() function + template + bool push_with( Func f ) + { + return enqueue_with( f ); + } + + /// Dequeues a value from the queue + /** + If queue is not empty, the function returns \p true, \p dest contains copy of + dequeued value. The assignment operator for type \ref value_type is invoked. + If queue is empty, the function returns \p false, \p dest is unchanged. + */ + bool dequeue( value_type& dest ) + { + return dequeue_with( [&dest]( value_type& src ) { dest = std::move( src );}); + } + + /// Dequeues a value using a functor + /** + \p Func is a functor called to copy dequeued value. + The functor takes one argument - a reference to removed node: + \code + cds:container::MSQueue< cds::gc::HP, Foo > myQueue; + Bar bar; + myQueue.dequeue_with( [&bar]( Foo& src ) { bar = std::move( src );}); + \endcode + The functor is called only if the queue is not empty. + */ + template + bool dequeue_with( Func f ) + { + node_type* p = base_class::dequeue(); + if ( p) { + f( p->m_value ); + return true; + } + return false; + } + + /// Synonym for \p dequeue() function + bool pop( value_type& dest ) + { + return dequeue( dest ); + } + + /// Synonym for \p dequeue_with() function + template + bool pop_with( Func f ) + { + return dequeue_with( f ); + } + + /// Clear the queue + /** + The function repeatedly calls \ref dequeue until it returns \p nullptr. + */ + void clear() + { + base_class::clear(); + } + + /// Checks if the queue is empty + bool empty() const + { + return base_class::empty(); + } + + /// Returns queue's item count (see \ref intrusive::MSQueue::size for explanation) + /** \copydetails cds::intrusive::MSQueue::size() + */ + size_t size() const + { + return base_class::size(); + } + + /// Returns reference to internal statistics + const stat& statistics() const + { + return base_class::statistics(); + } + }; + +}} // namespace cds::container + +#endif // #ifndef CDSLIB_CONTAINER_MSQUEUE_H diff --git a/cds/intrusive/details/sp_queue_base.h b/cds/intrusive/details/sp_queue_base.h new file mode 100644 index 000000000..ac6229e11 --- /dev/null +++ b/cds/intrusive/details/sp_queue_base.h @@ -0,0 +1,179 @@ +// Copyright (c) 2006-2018 Maxim Khizhinsky +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef CDSLIB_INTRUSIVE_DETAILS_SP_QUEUE_BASE_H +#define CDSLIB_INTRUSIVE_DETAILS_SP_QUEUE_BASE_H + +#include +#include +#include +#include + +namespace cds { namespace intrusive { + + /// Definitions common for single-linked data structures + /** @ingroup cds_intrusive_helper + */ + namespace sp_queue { + + /// Container's node + /** + Template parameters: + - GC - garbage collector used + - Tag - a tag used to distinguish between different implementation + */ + + template + struct node { + typedef GC gc ; ///< Garbage collector + typedef Tag tag ; ///< tag + + typedef typename gc::template atomic_ref atomic_node_ptr; ///< atomic pointer + typedef typename gc::template atomic_type atomic_int; + + /// Rebind node for other template parameters + template + struct rebind { + typedef node other ; ///< Rebinding result + }; + + atomic_node_ptr m_pNext ; ///< pointer to the next node in the container + + atomic_int m_nVer; + bool m_removed = false; + + node() noexcept + { + m_pNext.store( nullptr, atomics::memory_order_release ); + m_nVer.store(0, atomics::memory_order_release); + } + + }; + + + //@cond + struct default_hook { + typedef cds::gc::default_gc gc; + typedef opt::none tag; + }; + //@endcond + + //@cond + template < typename HookType, typename... Options> + struct hook + { + typedef typename opt::make_options< default_hook, Options...>::type options; + typedef typename options::gc gc; + typedef typename options::tag tag; + typedef node node_type; + typedef HookType hook_type; + }; + //@endcond + + /// Base hook + /** + \p Options are: + - opt::gc - garbage collector used. + - opt::tag - tag + */ + template < typename... Options > + struct base_hook: public hook< opt::base_hook_tag, Options... > + {}; + + /// Member hook + /** + \p MemberOffset defines offset in bytes of \ref node member into your structure. + Use \p offsetof macro to define \p MemberOffset + + \p Options are: + - opt::gc - garbage collector used. + - opt::tag - tag + */ + template < size_t MemberOffset, typename... Options > + struct member_hook: public hook< opt::member_hook_tag, Options... > + { + //@cond + static const size_t c_nMemberOffset = MemberOffset; + //@endcond + }; + + /// Traits hook + /** + \p NodeTraits defines type traits for node. + See \ref node_traits for \p NodeTraits interface description + + \p Options are: + - opt::gc - garbage collector used. + - opt::tag - tag + */ + template + struct traits_hook: public hook< opt::traits_hook_tag, Options... > + { + //@cond + typedef NodeTraits node_traits; + //@endcond + }; + + /// Check link + template + struct link_checker { + //@cond + typedef Node node_type; + //@endcond + + /// Checks if the link field of node \p pNode is \p nullptr + /** + An asserting is generated if \p pNode link field is not \p nullptr + */ + static void is_empty( const node_type * pNode ) + { + assert( pNode->m_pNext.load( atomics::memory_order_relaxed ) == nullptr ); + CDS_UNUSED( pNode ); + } + }; + + //@cond + template + struct link_checker_selector; + + template + struct link_checker_selector< GC, Node, opt::never_check_link > + { + typedef intrusive::opt::v::empty_link_checker type; + }; + + template + struct link_checker_selector< GC, Node, opt::debug_check_link > + { +# ifdef _DEBUG + typedef link_checker type; +# else + typedef intrusive::opt::v::empty_link_checker type; +# endif + }; + + template + struct link_checker_selector< GC, Node, opt::always_check_link > + { + typedef link_checker type; + }; + //@endcond + + /// Metafunction for selecting appropriate link checking policy + template < typename Node, opt::link_check_type LinkType > + struct get_link_checker + { + //@cond + typedef typename link_checker_selector< typename Node::gc, Node, LinkType>::type type; + //@endcond + }; + + } // namespace single_link + +}} // namespace cds::intrusive + + + +#endif // #ifndef CDSLIB_INTRUSIVE_DETAILS_SINGLE_LINK_STRUCT_H diff --git a/cds/intrusive/speculative_pairing_queue.h b/cds/intrusive/speculative_pairing_queue.h new file mode 100644 index 000000000..f347597df --- /dev/null +++ b/cds/intrusive/speculative_pairing_queue.h @@ -0,0 +1,572 @@ +// Copyright (c) 2006-2018 Maxim Khizhinsky +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef CDSLIB_INTRUSIVE_SPECULATIVE_PAIRING_QUEUE_H +#define CDSLIB_INTRUSIVE_SPECULATIVE_PAIRING_QUEUE_H + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace cds { namespace intrusive { + + /// MSPriorityQueue related definitions + /** @ingroup cds_intrusive_helper + */ + namespace speculative_pairing_queue { + //@cond + /// Slot type + template < class GC, typename Tag = opt::none> + using node = cds::intrusive::sp_queue::node< GC, Tag >; + + template < typename... Options > + using base_hook = cds::intrusive::sp_queue::base_hook< Options...>; + + template + using traits_hook = cds::intrusive::sp_queue::traits_hook< NodeTraits, Options... >; + + template < size_t MemberOffset, typename... Options > + using member_hook = cds::intrusive::sp_queue::member_hook< MemberOffset, Options... >; + + /// MSPriorityQueue statistics + template + struct stat { + typedef Counter counter_type ; ///< Event counter type + + counter_type m_nEnqueCount; ///< Count of success enque operation + counter_type m_nQueueCreatingCount; ///< Count of ccd reating Queue on enque + counter_type m_nRepeatEnqueCount; ///< Count of repeat iteration + + counter_type m_nDequeCount; ///< Count of success deque operation + counter_type m_nReturnEmptyInvalid; ///< Count of EMPTY returning because of invalid queue + counter_type m_nClosingQueue; ///< Count of closing queue(made it invalid) + + //@cond + void onEnqueSuccess() { ++m_nEnqueCount ;} + void onDequeSuccess() { ++m_nDequeCount ;} + + void onQueueCreate() { ++m_nQueueCreatingCount ;} + void onRepeatEnque() { ++m_nRepeatEnqueCount ;} + + void onReturnEmpty() { ++m_nReturnEmptyInvalid ;} + void onCloseQueue() { ++m_nClosingQueue ;} + //@endcond + + }; + + /// MSPriorityQueue empty statistics + struct empty_stat { + //@cond + void onEnqueSuccess() const {} + void onDequeSuccess() const {} + + void onQueueCreate() const {} + void onRepeatEnque() const {} + + void onReturnEmpty() const {} + void onCloseQueue() const {} + + void reset() {} + empty_stat& operator +=(empty_stat const&) + { + return *this; + } + //@endcond + }; + + /// MSQueue default traits + struct traits + { + typedef speculative_pairing_queue::base_hook<> hook; + /// Back-off strategy + typedef cds::backoff::empty back_off; + + /// The functor used for dispose removed items. Default is \p opt::v::empty_disposer. This option is used for dequeuing + typedef opt::v::empty_disposer disposer; + + /// Item counting feature; by default, disabled. Use \p cds::atomicity::item_counter to enable item counting + typedef atomicity::empty_item_counter item_counter; + + /// Internal statistics (by default, disabled) + /** + Possible option value are: \p msqueue::stat, \p msqueue::empty_stat (the default), + user-provided class that supports \p %msqueue::stat interface. + */ + typedef speculative_pairing_queue::empty_stat stat; + + /// C++ memory ordering model + /** + Can be \p opt::v::relaxed_ordering (relaxed memory model, the default) + or \p opt::v::sequential_consistent (sequentially consisnent memory model). + */ + typedef opt::v::sequential_consistent memory_model; + + /// Link checking, see \p cds::opt::link_checker + static constexpr const opt::link_check_type link_checker = opt::debug_check_link; + + /// Padding for internal critical atomic data. Default is \p opt::cache_line_padding + enum { padding = opt::cache_line_padding }; + }; + + /// Metafunction converting option list to traits + /** + \p Options: + - \p opt::buffer - the buffer type for heap array. Possible type are: \p opt::v::initialized_static_buffer, \p opt::v::initialized_dynamic_buffer. + Default is \p %opt::v::initialized_dynamic_buffer. + You may specify any type of value for the buffer since at instantiation time + the \p buffer::rebind member metafunction is called to change the type of values stored in the buffer. + - \p opt::compare - priority compare functor. No default functor is provided. + If the option is not specified, the \p opt::less is used. + - \p opt::less - specifies binary predicate used for priority compare. Default is \p std::less. + - \p opt::lock_type - lock type. Default is \p cds::sync::spin + - \p opt::back_off - back-off strategy. Default is \p cds::backoff::yield + - \p opt::stat - internal statistics. Available types: \p mspriority_queue::stat, \p mspriority_queue::empty_stat (the default, no overhead) + */ + 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 mspriority_queue + + /// Michael & Scott array-based lock-based concurrent priority queue heap + /** @ingroup cds_intrusive_priority_queue + Source: + - [2013] Henzinger,Payer,Sezgin " + Replacing competition with cooperation to achieve scalable lock-free FIFO queues" + + Template parameters: + - \p T - type to be stored in the queue. The priority is a part of \p T type. + - \p Traits - type traits. See \p speculative_pairing_queue::traits for explanation. + It is possible to declare option-based queue with \p cds::container::speculative_pairing_queue::make_traits + metafunction instead of \p Traits template argument. + */ + template + class SPQueue + { + public: + typedef GC gc ; ///< Garbage collector + typedef T value_type ; ///< Value type stored in the queue + typedef Traits traits ; ///< Traits template parameter + + typedef typename traits::hook hook; + typedef typename hook::node_type node_type; ///< node type + typedef typename get_node_traits< value_type, node_type, hook>::type node_traits; ///< node traits + typedef typename sp_queue::get_link_checker< node_type, traits::link_checker >::type link_checker; ///< link checker + typedef typename traits::back_off back_off; ///< back-off strategy + typedef typename traits::item_counter item_counter; ///< Item counter class + typedef typename traits::stat stat; ///< Internal statistics + typedef typename traits::memory_model memory_model; ///< Memory ordering. See \p cds::opt::memory_model option + typedef typename traits::disposer disposer; ///< disposer used + + /// Rebind template arguments + template + struct rebind { + typedef SPQueue< GC2, T2, Traits2 > other; ///< Rebinding result + }; + + static constexpr const size_t c_nHazardPtrCount = 5; ///< Count of hazard pointer required for the algorithm + protected: + typedef typename node_type::atomic_node_ptr atomic_node_ptr; + static_assert((std::is_same::value),"GC and node_type::gc must be the same"); + + + //@cond + /// Slot type + typedef struct SlotType { + atomic_node_ptr m_pHead; + atomic_node_ptr m_pLast; + atomic_node_ptr m_pRemoved; + + SlotType(){ + m_pHead.store(nullptr, memory_model::memory_order_release); + m_pLast.store(nullptr, memory_model::memory_order_release); + m_pRemoved.store(nullptr, memory_model::memory_order_release); + } + } Slot; + //@endcond + const static size_t C_SIZE = 10; ///< size + int probStale = 0; //< propability of dispose stale nodes + //@cond + /// Queue type + typedef struct QueueType { + typedef typename gc::template atomic_type atomic_int; + typedef typename gc::template atomic_type atomic_bool; + + atomic_bool m_Invalid; + atomic_int m_Cntdeq; + atomic_int m_Tail; + Slot m_pair[10]; + + QueueType(){ + m_Invalid.store(false, memory_model::memory_order_release); + m_Tail.store(0, memory_model::memory_order_release); + m_Cntdeq.store(0, memory_model::memory_order_release); + } + + QueueType(int tail){ + m_Invalid.store(false, memory_model::memory_order_release); + m_Tail.store(tail, memory_model::memory_order_release); + m_Cntdeq.store(0, memory_model::memory_order_release); + } + } Queue; + //@endcond + + typedef typename gc::template atomic_ref atomic_queue_ptr; + + item_counter m_ItemCounter ; ///< Item counter + stat m_Stat ; ///< internal statistics accumulator + atomic_queue_ptr m_Queue ; ///< Global queue + + static node_type* PICKET; + + + + static void clear_links( node_type * pNode ) + { + pNode->m_pNext.store( nullptr, memory_model::memory_order_release ); + pNode->m_nVer.store(0, memory_model::memory_order_release); + pNode->m_removed = false; + } + + struct disposer_node_thunk { + void operator()( value_type * p ) const + { + assert( p != nullptr ); + SPQueue::clear_links( node_traits::to_node_ptr( p )); + disposer()(p); + } + }; + + static void dispose_node( node_type * p ) + { + //retire all except PICKET + if (p != PICKET) + { + gc::template retire( node_traits::to_value_ptr( p )); + } + } + + + static void dispose_queue( Queue* queue) + { + + struct disposer_thunk { + void operator()( Queue* queue ) const + { + assert(queue != nullptr); + + node_type* current_node; + for (int i = 0; i < C_SIZE; i++) + { + current_node = queue->m_pair[i].m_pHead.load(memory_model::memory_order_acquire); + while (current_node != nullptr) + { + node_type* next_node = current_node->m_pNext.load(memory_model::memory_order_acquire); + if (current_node != PICKET) + disposer_node_thunk()(node_traits::to_value_ptr( current_node )); + current_node = next_node; + } + } + //there must be queue disposer, buuuuuut... + delete queue; + } + }; + + gc::template retire(queue); + } + + public: + /// Constructs empty speculative pairing queue + /** + */ + void changeProbStale(int prob) + { + probStale = prob; + } + + SPQueue() + { + m_Queue.store(new Queue, memory_model::memory_order_release); + PICKET->m_nVer.store(-1, memory_model::memory_order_release); + } + + /// Clears priority queue and destructs the object + ~SPQueue() + { + clear(); + //delete PICKET; + } + + /// Inserts a item into priority queue + /** + + */ + bool enqueue( value_type& val ) + { + node_type* DUMMY = nullptr; + Queue* pQueue; + typename gc::template Guard queue_guard; + typename gc::template GuardArray<3> guards; + size_t tail; + + while (true){ + tail = 0; + pQueue = queue_guard.protect(m_Queue); + if (pQueue->m_Invalid.load(memory_model::memory_order_acquire)) { + Queue* pNewQueue = createNewQueue(val); + if (m_Queue.compare_exchange_strong(pQueue, pNewQueue, memory_model::memory_order_seq_cst,memory_model::memory_order_seq_cst)) { + dispose_queue(pQueue); + m_Stat.onQueueCreate(); + ++m_ItemCounter; + return true; + } else { + delete pNewQueue; + } + + m_Stat.onRepeatEnque(); + continue; + } + + tail = pQueue->m_Tail.load(memory_model::memory_order_seq_cst); + + size_t idx = tail % C_SIZE; + + guards.protect(0, pQueue->m_pair[idx].m_pLast); + guards.protect(1, pQueue->m_pair[idx].m_pHead); + + node_type* pNode = guards.protect(2, pQueue->m_pair[idx].m_pLast, [](node_type * p) -> value_type * {return node_traits::to_value_ptr(p);}); + + if (tail == idx) { + if (pNode == nullptr) { + + node_type* pNewNode = node_traits::to_node_ptr( val ); + pNewNode->m_nVer.store(tail, memory_model::memory_order_release); + + node_type* DUMMY = nullptr; + if (pQueue->m_pair[idx].m_pHead.compare_exchange_strong(DUMMY, pNewNode, memory_model::memory_order_seq_cst, memory_model::memory_order_seq_cst)) { + pQueue->m_pair[idx].m_pLast.store(pNewNode, memory_model::memory_order_release); + break; + } + else { + if (pQueue->m_pair[idx].m_pHead.load(memory_model::memory_order_acquire) == PICKET) + pQueue->m_Invalid.store(true, memory_model::memory_order_release); + else + pQueue->m_Tail.compare_exchange_weak(tail, + tail + 1, + memory_model::memory_order_seq_cst, + memory_model::memory_order_seq_cst); + m_Stat.onRepeatEnque(); + continue; + } + } + else { + if (pNode == PICKET) + pQueue->m_Invalid.store(true, memory_model::memory_order_release); + else + pQueue->m_Tail.compare_exchange_weak(tail, + tail + 1, + memory_model::memory_order_seq_cst, + memory_model::memory_order_seq_cst); + + m_Stat.onRepeatEnque(); + continue; + } + } + + if (pNode == nullptr) + { + pNode = guards.protect(2, pQueue->m_pair[idx].m_pHead, [](node_type * p) -> value_type * {return node_traits::to_value_ptr(p);}); + } + + if (pNode == PICKET) { + Queue* pNewQueue = createNewQueue(val); + if (m_Queue.compare_exchange_strong(pQueue, pNewQueue,memory_model::memory_order_seq_cst,memory_model::memory_order_acquire)) + { + dispose_queue(pQueue); + m_Stat.onQueueCreate(); + ++m_ItemCounter; + return true; + } else { + delete pNewQueue; + } + + m_Stat.onRepeatEnque(); + continue; + } + + while (pNode->m_pNext.load(memory_model::memory_order_acquire) != nullptr + && pNode->m_nVer.load(memory_model::memory_order_acquire) < tail) + pNode = guards.protect(2, pNode->m_pNext, [](node_type * p) -> value_type * {return node_traits::to_value_ptr(p);}); + + if (pNode->m_nVer.load(memory_model::memory_order_acquire) >= tail) { + pQueue->m_Tail.compare_exchange_strong(tail, tail + 1,memory_model::memory_order_seq_cst,memory_model::memory_order_seq_cst); + m_Stat.onRepeatEnque(); + continue; + } + + if (pNode != PICKET) { + node_type* pNewNode = node_traits::to_node_ptr(val); + pNewNode->m_nVer.store(tail, memory_model::memory_order_release); + node_type* DUMMY = nullptr; + if (pNode->m_pNext.compare_exchange_strong(DUMMY, pNewNode,memory_model::memory_order_seq_cst,memory_model::memory_order_seq_cst)) { + pQueue->m_pair[idx].m_pLast.store(pNewNode, memory_model::memory_order_release); + break; + } + } + else { + pQueue->m_Invalid.store(true, memory_model::memory_order_release); + } + } + pQueue->m_Tail.compare_exchange_weak(tail, tail + 1,memory_model::memory_order_seq_cst,memory_model::memory_order_seq_cst); + ++m_ItemCounter; + m_Stat.onEnqueSuccess(); + return true; + } + + /// Extracts item with high priority + /** + + */ + value_type* dequeue() + { + node_type* DUMMY = nullptr; + typename gc::template Guard queue_guard; + typename gc::template GuardArray<3> guards; + + Queue* pQueue = queue_guard.protect(m_Queue); + if (pQueue->m_Invalid.load(memory_model::memory_order_acquire)) { + m_Stat.onReturnEmpty(); + return nullptr; + } + + + size_t ticket = pQueue->m_Cntdeq++; + + size_t idx = ticket % C_SIZE; + guards.protect(0, pQueue->m_pair[idx].m_pRemoved); + guards.protect(1, pQueue->m_pair[idx].m_pHead); + + if (ticket >= pQueue->m_Tail.load(memory_model::memory_order_acquire) && ticket == idx) {//Error in article. may be must be >= + node_type* DUMMY = nullptr; + if (pQueue->m_pair[idx].m_pHead.compare_exchange_strong(DUMMY, PICKET, memory_model::memory_order_seq_cst,memory_model::memory_order_acquire)) { + CloseQueue(pQueue, idx); + m_Stat.onCloseQueue(); + return nullptr; + } + } + + node_type* pNode = guards.protect(2, pQueue->m_pair[idx].m_pRemoved, [](node_type * p) -> value_type * {return node_traits::to_value_ptr(p);}); + if (pNode == nullptr || pNode == PICKET) + pNode = guards.protect(2, pQueue->m_pair[idx].m_pHead, [](node_type * p) -> value_type * {return node_traits::to_value_ptr(p);}); + + if (pNode == PICKET) { + CloseQueue(pQueue, idx); + m_Stat.onCloseQueue(); + return nullptr; + } + + if (pNode->m_nVer.load(memory_model::memory_order_acquire) > ticket) + pNode = guards.protect(2, pQueue->m_pair[idx].m_pHead, [](node_type * p) -> value_type * {return node_traits::to_value_ptr(p);}); + + while (pNode->m_nVer.load(memory_model::memory_order_acquire) < ticket) { + if (pNode->m_pNext.load(memory_model::memory_order_acquire) == nullptr) { + node_type* DUMMY = nullptr; + if (pNode->m_pNext.compare_exchange_strong(DUMMY, PICKET, memory_model::memory_order_seq_cst, memory_model::memory_order_acquire)) { + CloseQueue(pQueue, idx); + m_Stat.onCloseQueue(); + return nullptr; + } + } + pNode = guards.protect(2, pNode->m_pNext, [](node_type * p) -> value_type * {return node_traits::to_value_ptr(p);}); + if (pNode == PICKET) { + CloseQueue(pQueue, idx); + m_Stat.onCloseQueue(); + return nullptr; + } + } + + value_type* x = node_traits::to_value_ptr(pNode); + pQueue->m_pair[idx].m_pRemoved.store(pNode, memory_model::memory_order_release); + --m_ItemCounter; + m_Stat.onDequeSuccess(); + return x; + } + + bool push(value_type& val){ + return enqueue(val); + } + + value_type* pop(){ + return dequeue(); + } + /// Clears the queue (not atomic) + /** + + */ + void clear() + { + while ( dequeue() != nullptr); + dispose_queue(m_Queue.load(memory_model::memory_order_acquire)); + m_Queue.store(new Queue(), memory_model::memory_order_release); + } + + /// Checks is the priority queue is empty + bool empty() const + { + Queue* pQueue = m_Queue.load(memory_model::memory_order_acquire); + return pQueue->m_Tail.load(memory_model::memory_order_acquire) <= + pQueue->m_Cntdeq.load(memory_model::memory_order_acquire); + } + + /// Returns current size of priority queue + size_t size() const + { + return m_ItemCounter.value(); + } + + /// Returns const reference to internal statistics + stat const& statistics() const + { + return m_Stat; + } + + protected: + Queue* createNewQueue(value_type& x) { + Queue* queue = new Queue(1); + + node_type* pNewNode = node_traits::to_node_ptr(x); + pNewNode->m_nVer.store(0, memory_model::memory_order_release); + queue->m_pair[0].m_pHead.store(pNewNode, memory_model::memory_order_release); + queue->m_pair[0].m_pLast.store(pNewNode, memory_model::memory_order_release); + + return queue; + } + + void CloseQueue(Queue* q, int idx) { + q->m_Invalid.store(true, memory_model::memory_order_release); + q->m_pair[idx].m_pRemoved.store(PICKET, memory_model::memory_order_release); + } + }; + + template + typename SPQueue::node_type* SPQueue::PICKET = new SPQueue::node_type(); + }} // namespace cds::intrusive + +#endif // #ifndef CDSLIB_INTRUSIVE_MSPRIORITY_QUEUE_H diff --git a/test/stress/queue/CMakeLists.txt b/test/stress/queue/CMakeLists.txt index a074aceed..35bcc1325 100644 --- a/test/stress/queue/CMakeLists.txt +++ b/test/stress/queue/CMakeLists.txt @@ -10,7 +10,7 @@ set(CDSSTRESS_QUEUE_POP_SOURCES ) add_executable(${CDSSTRESS_QUEUE_POP} ${CDSSTRESS_QUEUE_POP_SOURCES}) target_link_libraries(${CDSSTRESS_QUEUE_POP} ${CDS_TEST_LIBRARIES} ${CDSSTRESS_FRAMEWORK_LIBRARY}) -add_test(NAME ${CDSSTRESS_QUEUE_POP} COMMAND ${CDSSTRESS_QUEUE_POP} WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) +add_test(NAME ${CDSSTRESS_QUEUE_POP} COMMAND ${CDSSTRESS_QUEUE_POP} WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) # stress-queue-push set(CDSSTRESS_QUEUE_PUSH stress-queue-push) @@ -20,18 +20,18 @@ set(CDSSTRESS_QUEUE_PUSH_SOURCES ) add_executable(${CDSSTRESS_QUEUE_PUSH} ${CDSSTRESS_QUEUE_PUSH_SOURCES}) target_link_libraries(${CDSSTRESS_QUEUE_PUSH} ${CDS_TEST_LIBRARIES} ${CDSSTRESS_FRAMEWORK_LIBRARY}) -add_test(NAME ${CDSSTRESS_QUEUE_PUSH} COMMAND ${CDSSTRESS_QUEUE_PUSH} WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) +add_test(NAME ${CDSSTRESS_QUEUE_PUSH} COMMAND ${CDSSTRESS_QUEUE_PUSH} WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) # stress-queue-push-pop set(CDSSTRESS_QUEUE_PUSHPOP stress-queue-push-pop) set(CDSSTRESS_QUEUE_PUSHPOP_SOURCES ../main.cpp push_pop.cpp - intrusive_push_pop.cpp + intrusive_push_pop.cpp ) add_executable(${CDSSTRESS_QUEUE_PUSHPOP} ${CDSSTRESS_QUEUE_PUSHPOP_SOURCES}) target_link_libraries(${CDSSTRESS_QUEUE_PUSHPOP} ${CDS_TEST_LIBRARIES} ${CDSSTRESS_FRAMEWORK_LIBRARY}) -add_test(NAME ${CDSSTRESS_QUEUE_PUSHPOP} COMMAND ${CDSSTRESS_QUEUE_PUSHPOP} WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) +add_test(NAME ${CDSSTRESS_QUEUE_PUSHPOP} COMMAND ${CDSSTRESS_QUEUE_PUSHPOP} WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) # stress-queue-random set(CDSSTRESS_QUEUE_RANDOM stress-queue-random) @@ -41,7 +41,7 @@ set(CDSSTRESS_QUEUE_RANDOM_SOURCES ) add_executable(${CDSSTRESS_QUEUE_RANDOM} ${CDSSTRESS_QUEUE_RANDOM_SOURCES}) target_link_libraries(${CDSSTRESS_QUEUE_RANDOM} ${CDS_TEST_LIBRARIES} ${CDSSTRESS_FRAMEWORK_LIBRARY}) -add_test(NAME ${CDSSTRESS_QUEUE_RANDOM} COMMAND ${CDSSTRESS_QUEUE_RANDOM} WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) +add_test(NAME ${CDSSTRESS_QUEUE_RANDOM} COMMAND ${CDSSTRESS_QUEUE_RANDOM} WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) # stress-queue-bounded set(CDSSTRESS_QUEUE_BOUNDED stress-queue-bounded) @@ -51,7 +51,7 @@ set(CDSSTRESS_QUEUE_BOUNDED_SOURCES ) add_executable(${CDSSTRESS_QUEUE_BOUNDED} ${CDSSTRESS_QUEUE_BOUNDED_SOURCES}) target_link_libraries(${CDSSTRESS_QUEUE_BOUNDED} ${CDS_TEST_LIBRARIES} ${CDSSTRESS_FRAMEWORK_LIBRARY}) -add_test(NAME ${CDSSTRESS_QUEUE_BOUNDED} COMMAND ${CDSSTRESS_QUEUE_BOUNDED} WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) +add_test(NAME ${CDSSTRESS_QUEUE_BOUNDED} COMMAND ${CDSSTRESS_QUEUE_BOUNDED} WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) # stress-spsc-queue set(CDSSTRESS_SPSC_QUEUE stress-spsc-queue) @@ -64,7 +64,6 @@ add_executable(${CDSSTRESS_SPSC_QUEUE} ${CDSSTRESS_SPSC_QUEUE_SOURCES}) target_link_libraries(${CDSSTRESS_SPSC_QUEUE} ${CDS_TEST_LIBRARIES} ${CDSSTRESS_FRAMEWORK_LIBRARY}) add_test(NAME ${CDSSTRESS_SPSC_QUEUE} COMMAND ${CDSSTRESS_SPSC_QUEUE} WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}) - # stress-queue add_custom_target( stress-queue DEPENDS diff --git a/test/stress/queue/intrusive_push_pop.cpp b/test/stress/queue/intrusive_push_pop.cpp index a6af373b2..b3c6976e9 100644 --- a/test/stress/queue/intrusive_push_pop.cpp +++ b/test/stress/queue/intrusive_push_pop.cpp @@ -314,7 +314,6 @@ namespace { } } pool.add( new Consumer( pool, q ), s_nReaderThreadCount ); - std::chrono::milliseconds duration = pool.run(); propout() << std::make_pair( "duration", duration ); @@ -331,6 +330,7 @@ namespace { analyze( q, nLeftOffset, nRightOffset ); + propout() << q.statistics(); } }; @@ -348,6 +348,16 @@ namespace { queue_type::gc::force_dispose(); \ } + + CDSSTRESS_QUEUE_F( SPQueue_HP, cds::intrusive::speculative_pairing_queue::node ) + CDSSTRESS_QUEUE_F( SPQueue_HP_ic, cds::intrusive::speculative_pairing_queue::node ) + CDSSTRESS_QUEUE_F( SPQueue_HP_stat, cds::intrusive::speculative_pairing_queue::node ) + CDSSTRESS_QUEUE_F( SPQueue_DHP, cds::intrusive::speculative_pairing_queue::node ) + CDSSTRESS_QUEUE_F( SPQueue_DHP_ic, cds::intrusive::speculative_pairing_queue::node ) + CDSSTRESS_QUEUE_F( SPQueue_DHP_stat, cds::intrusive::speculative_pairing_queue::node ) + + + CDSSTRESS_QUEUE_F( MSQueue_HP, cds::intrusive::msqueue::node ) CDSSTRESS_QUEUE_F( MSQueue_HP_ic, cds::intrusive::msqueue::node ) CDSSTRESS_QUEUE_F( MSQueue_HP_stat, cds::intrusive::msqueue::node ) @@ -375,6 +385,7 @@ namespace { CDSSTRESS_QUEUE_F( BasketQueue_DHP, cds::intrusive::basket_queue::node ) CDSSTRESS_QUEUE_F( BasketQueue_DHP_ic, cds::intrusive::basket_queue::node ) CDSSTRESS_QUEUE_F( BasketQueue_DHP_stat, cds::intrusive::basket_queue::node ) + #undef CDSSTRESS_QUEUE_F @@ -399,6 +410,7 @@ namespace { CDSSTRESS_QUEUE_F(FCQueue_list_wait_sm_stat, boost::intrusive::list_base_hook<> ) CDSSTRESS_QUEUE_F(FCQueue_list_wait_mm, boost::intrusive::list_base_hook<> ) CDSSTRESS_QUEUE_F(FCQueue_list_wait_mm_stat, boost::intrusive::list_base_hook<> ) + #undef CDSSTRESS_QUEUE_F @@ -413,6 +425,7 @@ namespace { CDSSTRESS_QUEUE_F( VyukovMPMCCycleQueue_dyn ) CDSSTRESS_QUEUE_F( VyukovMPMCCycleQueue_dyn_ic ) + #undef CDSSTRESS_QUEUE_F diff --git a/test/stress/queue/intrusive_queue_type.h b/test/stress/queue/intrusive_queue_type.h index f6ef2e64c..76cff40c6 100644 --- a/test/stress/queue/intrusive_queue_type.h +++ b/test/stress/queue/intrusive_queue_type.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -136,7 +137,7 @@ namespace queue { }; typedef cds::intrusive::MSQueue< cds::gc::DHP, T, traits_MSQueue_DHP_ic > MSQueue_DHP_ic; typedef cds::intrusive::MoirQueue< cds::gc::DHP, T, traits_MSQueue_DHP_ic > MoirQueue_DHP_ic; - + // MSQueue + stat struct traits_MSQueue_HP_stat : public cds::intrusive::msqueue::traits { @@ -155,6 +156,62 @@ namespace queue { typedef cds::intrusive::MoirQueue< cds::gc::DHP, T, traits_MSQueue_DHP_stat > MoirQueue_DHP_stat; + struct traits_SPQueue_HP : public cds::intrusive::speculative_pairing_queue::traits + { + typedef cds::intrusive::sp_queue::base_hook< cds::opt::gc< cds::gc::HP > > hook; + }; + typedef cds::intrusive::SPQueue< cds::gc::HP, T, traits_SPQueue_HP > SPQueue_HP; + + struct traits_SPQueue_HP_seqcst : public cds::intrusive::speculative_pairing_queue::traits + { + typedef cds::intrusive::sp_queue::base_hook< cds::opt::gc< cds::gc::HP > > hook; + typedef cds::opt::v::sequential_consistent memory_model; + }; + typedef cds::intrusive::SPQueue< cds::gc::HP, T, traits_SPQueue_HP_seqcst > SPQueue_HP_seqcst; + + struct traits_SPQueue_DHP : public cds::intrusive::speculative_pairing_queue::traits + { + typedef cds::intrusive::sp_queue::base_hook< cds::opt::gc< cds::gc::DHP > > hook; + }; + typedef cds::intrusive::SPQueue< cds::gc::DHP, T, traits_SPQueue_DHP > SPQueue_DHP; + + struct traits_SPQueue_DHP_seqcst : public cds::intrusive::speculative_pairing_queue::traits + { + typedef cds::intrusive::sp_queue::base_hook< cds::opt::gc< cds::gc::DHP > > hook; + typedef cds::opt::v::sequential_consistent memory_model; + }; + typedef cds::intrusive::SPQueue< cds::gc::DHP, T, traits_SPQueue_DHP_seqcst > SPQueue_DHP_seqcst; + + // SPQueue + item counter + struct traits_SPQueue_HP_ic : public cds::intrusive::speculative_pairing_queue::traits + { + typedef cds::intrusive::sp_queue::base_hook< cds::opt::gc< cds::gc::HP > > hook; + typedef cds::atomicity::item_counter item_counter; + }; + typedef cds::intrusive::SPQueue< cds::gc::HP, T, traits_SPQueue_HP_ic > SPQueue_HP_ic; + + struct traits_SPQueue_DHP_ic : public cds::intrusive::speculative_pairing_queue::traits + { + typedef cds::intrusive::sp_queue::base_hook< cds::opt::gc< cds::gc::DHP > > hook; + typedef cds::atomicity::item_counter item_counter; + }; + typedef cds::intrusive::SPQueue< cds::gc::DHP, T, traits_SPQueue_DHP_ic > SPQueue_DHP_ic; + + // SPQueue + stat + struct traits_SPQueue_HP_stat : public cds::intrusive::speculative_pairing_queue::traits + { + typedef cds::intrusive::sp_queue::base_hook< cds::opt::gc< cds::gc::HP > > hook; + typedef cds::intrusive::speculative_pairing_queue::stat<> stat; + }; + typedef cds::intrusive::SPQueue< cds::gc::HP, T, traits_SPQueue_HP_stat > SPQueue_HP_stat; + + struct traits_SPQueue_DHP_stat : public cds::intrusive::speculative_pairing_queue::traits + { + typedef cds::intrusive::sp_queue::base_hook< cds::opt::gc< cds::gc::DHP > > hook; + typedef cds::intrusive::speculative_pairing_queue::stat<> stat; + }; + typedef cds::intrusive::SPQueue< cds::gc::DHP, T, traits_SPQueue_DHP_stat > SPQueue_DHP_stat; + // OptimisticQueue struct traits_OptimisticQueue_HP : public cds::intrusive::optimistic_queue::traits { diff --git a/test/stress/queue/pop.cpp b/test/stress/queue/pop.cpp index 5dbb72cd5..287ccbb4a 100644 --- a/test/stress/queue/pop.cpp +++ b/test/stress/queue/pop.cpp @@ -125,8 +125,11 @@ namespace { pool.add( new Consumer( pool, q ), s_nThreadCount ); - for ( size_t i = 0; i < s_nQueueSize; ++i ) + for ( size_t i = 0; i < s_nQueueSize; ++i ) { q.push( i ); + } + + propout() << std::make_pair( "thread_count", s_nThreadCount ) << std::make_pair( "push_count", s_nQueueSize ); @@ -141,7 +144,9 @@ namespace { } }; + CDSSTRESS_MSQueue( queue_pop ) + CDSSTRESS_SPQueue( queue_pop ) CDSSTRESS_MoirQueue( queue_pop ) CDSSTRESS_BasketQueue( queue_pop ) CDSSTRESS_OptimsticQueue( queue_pop ) @@ -159,8 +164,10 @@ namespace { test( queue ); \ } + CDSSTRESS_VyukovQueue( queue_pop ) + #undef CDSSTRESS_Queue_F @@ -216,6 +223,7 @@ namespace { CDSSTRESS_SegmentedQueue( segmented_queue_pop ) + #ifdef CDSTEST_GTEST_INSTANTIATE_TEST_CASE_P_HAS_4TH_ARG static std::string get_test_parameter_name( testing::TestParamInfo const& p ) { diff --git a/test/stress/queue/print_stat.h b/test/stress/queue/print_stat.h index 960abdac5..8e1d8fc29 100644 --- a/test/stress/queue/print_stat.h +++ b/test/stress/queue/print_stat.h @@ -51,6 +51,23 @@ namespace cds_test { return o; } + template + static inline property_stream& operator <<( property_stream& o, cds::intrusive::speculative_pairing_queue::stat const& s ) + { + return o + << CDSSTRESS_STAT_OUT( s, m_nEnqueCount ) + << CDSSTRESS_STAT_OUT( s, m_nQueueCreatingCount ) + << CDSSTRESS_STAT_OUT( s, m_nRepeatEnqueCount ) + << CDSSTRESS_STAT_OUT( s, m_nDequeCount ) + << CDSSTRESS_STAT_OUT( s, m_nReturnEmptyInvalid ) + << CDSSTRESS_STAT_OUT( s, m_nClosingQueue ); + } + + static inline property_stream& operator <<( property_stream& o, cds::intrusive::speculative_pairing_queue::empty_stat const& ) + { + return o; + } + template static inline property_stream& operator <<( property_stream& o, cds::intrusive::optimistic_queue::stat const& s ) { diff --git a/test/stress/queue/push.cpp b/test/stress/queue/push.cpp index c2e80bba2..854a3f706 100644 --- a/test/stress/queue/push.cpp +++ b/test/stress/queue/push.cpp @@ -57,8 +57,9 @@ namespace { virtual void test() { for ( size_t nItem = m_nStartItem; nItem < m_nEndItem; ++nItem ) { - if ( !m_Queue.push( nItem )) + if ( !m_Queue.push( nItem )) { ++m_nPushError; + } } } @@ -89,6 +90,7 @@ namespace { template void test( Queue& q ) { + try { cds_test::thread_pool& pool = get_pool(); pool.add( new Producer( pool, q ), s_nThreadCount ); @@ -113,6 +115,11 @@ namespace { analyze( q ); propout() << q.statistics(); + } + catch (std::exception& e) + { + EXPECT_TRUE(false) << "Exception catched : " << e.what(); + } } template @@ -132,6 +139,7 @@ namespace { size_t nPopped = 0; value_type val; + while ( q.pop( val )) { nPopped++; ++arr[ val.nNo ]; @@ -144,7 +152,9 @@ namespace { } }; + CDSSTRESS_MSQueue( queue_push ) + CDSSTRESS_SPQueue( queue_push ) CDSSTRESS_MoirQueue( queue_push ) CDSSTRESS_BasketQueue( queue_push ) CDSSTRESS_OptimsticQueue( queue_push ) @@ -153,6 +163,7 @@ namespace { CDSSTRESS_RWQueue( queue_push ) CDSSTRESS_StdQueue( queue_push ) + #undef CDSSTRESS_Queue_F #define CDSSTRESS_Queue_F( test_fixture, type_name ) \ TEST_F( test_fixture, type_name ) \ diff --git a/test/stress/queue/push_pop.cpp b/test/stress/queue/push_pop.cpp index 4803f5238..d1ed31769 100644 --- a/test/stress/queue/push_pop.cpp +++ b/test/stress/queue/push_pop.cpp @@ -321,6 +321,7 @@ namespace { using simple_queue_push_pop = queue_push_pop<>; CDSSTRESS_MSQueue( simple_queue_push_pop ) + CDSSTRESS_SPQueue( simple_queue_push_pop ) CDSSTRESS_MoirQueue( simple_queue_push_pop ) CDSSTRESS_BasketQueue( simple_queue_push_pop ) CDSSTRESS_OptimsticQueue( simple_queue_push_pop ) @@ -398,6 +399,7 @@ namespace { CDSSTRESS_SegmentedQueue( segmented_queue_push_pop ) + #ifdef CDSTEST_GTEST_INSTANTIATE_TEST_CASE_P_HAS_4TH_ARG static std::string get_test_parameter_name( testing::TestParamInfo const& p ) { diff --git a/test/stress/queue/queue_type.h b/test/stress/queue/queue_type.h index 4dd0f454b..c146a9fd9 100644 --- a/test/stress/queue/queue_type.h +++ b/test/stress/queue/queue_type.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -195,6 +196,35 @@ namespace fc_details{ typedef cds::container::MoirQueue< cds::gc::HP, Value, traits_MSQueue_stat > MoirQueue_HP_stat; typedef cds::container::MoirQueue< cds::gc::DHP, Value, traits_MSQueue_stat > MoirQueue_DHP_stat; + // SPQueue + typedef cds::container::SPQueue SPQueue_HP; + typedef cds::container::SPQueue SPQueue_DHP; + + struct traits_SPQueue_seqcst : public + cds::container::speculative_pairing_queue::make_traits < + cds::opt::memory_model < cds::opt::v::sequential_consistent > + > ::type + {}; + typedef cds::container::SPQueue< cds::gc::HP, Value, traits_SPQueue_seqcst > SPQueue_HP_seqcst; + typedef cds::container::SPQueue< cds::gc::DHP, Value, traits_SPQueue_seqcst > SPQueue_DHP_seqcst; + + // SPQueue + item counter + struct traits_SPQueue_ic : public + cds::container::speculative_pairing_queue::make_traits < + cds::opt::item_counter < cds::atomicity::item_counter > + >::type + {}; + typedef cds::container::SPQueue< cds::gc::HP, Value, traits_SPQueue_ic > SPQueue_HP_ic; + typedef cds::container::SPQueue< cds::gc::DHP, Value, traits_SPQueue_ic > SPQueue_DHP_ic; + + // SPQueue + stat + struct traits_SPQueue_stat: public + cds::container::speculative_pairing_queue::make_traits < + cds::opt::stat< cds::container::speculative_pairing_queue::stat<> > + >::type + {}; + typedef cds::container::SPQueue< cds::gc::HP, Value, traits_SPQueue_stat > SPQueue_HP_stat; + typedef cds::container::SPQueue< cds::gc::DHP, Value, traits_SPQueue_stat > SPQueue_DHP_stat; // OptimisticQueue typedef cds::container::OptimisticQueue< cds::gc::HP, Value > OptimisticQueue_HP; @@ -753,6 +783,7 @@ namespace cds_test { #else # define CDSSTRESS_MSQueue_1( test_fixture ) +# define CDSSTRESS_SPQueue_1( test_fixture ) # define CDSSTRESS_MoirQueue_1( test_fixture ) # define CDSSTRESS_OptimsticQueue_1( test_fixture ) # define CDSSTRESS_BasketQueue_1( test_fixture ) @@ -771,6 +802,13 @@ namespace cds_test { CDSSTRESS_Queue_F( test_fixture, MSQueue_DHP_stat ) \ CDSSTRESS_MSQueue_1( test_fixture ) +#define CDSSTRESS_SPQueue( test_fixture ) \ + CDSSTRESS_Queue_F( test_fixture, SPQueue_HP ) \ + CDSSTRESS_Queue_F( test_fixture, SPQueue_HP_stat ) \ + CDSSTRESS_Queue_F( test_fixture, SPQueue_DHP ) \ + CDSSTRESS_Queue_F( test_fixture, SPQueue_DHP_stat ) \ + CDSSTRESS_SPQueue_1( test_fixture ) + #define CDSSTRESS_MoirQueue( test_fixture ) \ CDSSTRESS_Queue_F( test_fixture, MoirQueue_HP ) \ CDSSTRESS_Queue_F( test_fixture, MoirQueue_HP_stat ) \ diff --git a/test/stress/queue/random.cpp b/test/stress/queue/random.cpp index 357737d9e..ac49edf20 100644 --- a/test/stress/queue/random.cpp +++ b/test/stress/queue/random.cpp @@ -209,6 +209,7 @@ namespace { }; CDSSTRESS_MSQueue( queue_random ) + CDSSTRESS_SPQueue( queue_random ) CDSSTRESS_MoirQueue( queue_random ) CDSSTRESS_BasketQueue( queue_random ) CDSSTRESS_OptimsticQueue( queue_random ) diff --git a/test/unit/queue/CMakeLists.txt b/test/unit/queue/CMakeLists.txt index aea3e81f4..d144682fd 100644 --- a/test/unit/queue/CMakeLists.txt +++ b/test/unit/queue/CMakeLists.txt @@ -28,6 +28,10 @@ set(CDSGTEST_QUEUE_SOURCES intrusive_segmented_queue_hp.cpp intrusive_segmented_queue_dhp.cpp intrusive_vyukov_queue.cpp + intrusive_speculative_pairing_queue_hp.cpp + intrusive_speculative_pairing_queue_dhp.cpp + speculative_pairing_queue_hp.cpp + speculative_pairing_queue_dhp.cpp ) include_directories( @@ -37,4 +41,4 @@ include_directories( add_executable(${PACKAGE_NAME} ${CDSGTEST_QUEUE_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}) diff --git a/test/unit/queue/intrusive_speculative_pairing_queue_dhp.cpp b/test/unit/queue/intrusive_speculative_pairing_queue_dhp.cpp new file mode 100644 index 000000000..4df095f97 --- /dev/null +++ b/test/unit/queue/intrusive_speculative_pairing_queue_dhp.cpp @@ -0,0 +1,160 @@ +// Copyright (c) 2006-2018 Maxim Khizhinsky +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include "test_intrusive_speculative_pairing_queue.h" + +#include +#include +#include + +namespace { + namespace ci = cds::intrusive; + typedef cds::gc::DHP gc_type; + + + class IntrusiveSPQueue_DHP : public cds_test::intrusive_speculative_pairing_queue + { + typedef cds_test::intrusive_speculative_pairing_queue base_class; + + protected: + typedef typename base_class::base_hook_item< ci::speculative_pairing_queue::node> base_item_type; + typedef typename base_class::member_hook_item< ci::speculative_pairing_queue::node> member_item_type; + + void SetUp() + { + typedef ci::SPQueue< gc_type, base_item_type, + typename ci::speculative_pairing_queue::make_traits< + ci::opt::hook< ci::speculative_pairing_queue::base_hook< ci::opt::gc>> + >::type + > queue_type; + + cds::gc::dhp::smr::construct( queue_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 ) { + ASSERT_EQ( arr[i].nDisposeCount, 3 ); + } + } + }; + + TEST_F( IntrusiveSPQueue_DHP, base_hook ) + { + typedef cds::intrusive::SPQueue< gc_type, base_item_type, + typename ci::speculative_pairing_queue::make_traits< + ci::opt::disposer< mock_disposer > + ,ci::opt::hook< ci::speculative_pairing_queue::base_hook< ci::opt::gc>> + >::type + > test_queue; + + std::vector arr; + arr.resize(100); + { + test_queue q; + test(q, arr); + } + gc_type::scan(); + check_array( arr ); + } + + TEST_F( IntrusiveSPQueue_DHP, base_item_counting ) + { + typedef cds::intrusive::SPQueue< gc_type, base_item_type, + typename ci::speculative_pairing_queue::make_traits< + ci::opt::disposer< mock_disposer > + , cds::opt::item_counter< cds::atomicity::item_counter > + , ci::opt::hook< ci::speculative_pairing_queue::base_hook< ci::opt::gc>> + >::type + > test_queue; + + std::vector arr; + arr.resize(100); + { + test_queue q; + test(q, arr); + } + gc_type::scan(); + check_array( arr ); + } + + TEST_F( IntrusiveSPQueue_DHP, base_stat ) + { + struct traits : public ci::speculative_pairing_queue::traits + { + typedef ci::speculative_pairing_queue::base_hook< ci::opt::gc> hook; + typedef mock_disposer disposer; + typedef cds::atomicity::item_counter item_counter; + typedef ci::speculative_pairing_queue::stat<> stat; + typedef cds::opt::v::sequential_consistent memory_model; + }; + typedef cds::intrusive::SPQueue< gc_type, base_item_type, traits > test_queue; + + std::vector arr; + arr.resize(100); + { + test_queue q; + test(q, arr); + } + gc_type::scan(); + check_array( arr ); + } + + TEST_F( IntrusiveSPQueue_DHP, member_hook ) + { + typedef cds::intrusive::SPQueue< gc_type, member_item_type, + typename ci::speculative_pairing_queue::make_traits< + ci::opt::disposer< mock_disposer > + ,ci::opt::hook< ci::speculative_pairing_queue::member_hook< + offsetof( member_item_type, hMember ), + ci::opt::gc + >> + >::type + > test_queue; + + std::vector arr; + arr.resize( 100 ); + { + test_queue q; + test( q, arr ); + } + gc_type::scan(); + check_array( arr ); + } + + TEST_F( IntrusiveSPQueue_DHP, member_hook_stat ) + { + struct traits : public ci::speculative_pairing_queue::traits + { + typedef ci::speculative_pairing_queue::member_hook< + offsetof( member_item_type, hMember ), + ci::opt::gc + > hook; + typedef mock_disposer disposer; + typedef cds::atomicity::item_counter item_counter; + typedef ci::speculative_pairing_queue::stat<> stat; + typedef cds::opt::v::sequential_consistent memory_model; + }; + typedef cds::intrusive::SPQueue< gc_type, member_item_type, traits > test_queue; + + std::vector arr; + arr.resize( 100 ); + { + test_queue q; + test( q, arr ); + } + gc_type::scan(); + check_array( arr ); + } + +} // namespace diff --git a/test/unit/queue/intrusive_speculative_pairing_queue_hp.cpp b/test/unit/queue/intrusive_speculative_pairing_queue_hp.cpp new file mode 100644 index 000000000..5839ff074 --- /dev/null +++ b/test/unit/queue/intrusive_speculative_pairing_queue_hp.cpp @@ -0,0 +1,175 @@ +// Copyright (c) 2006-2018 Maxim Khizhinsky +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include "test_intrusive_speculative_pairing_queue.h" + +#include +#include +#include + +namespace { + namespace ci = cds::intrusive; + typedef cds::gc::HP gc_type; + + + class IntrusiveSPQueue_HP : public cds_test::intrusive_speculative_pairing_queue + { + typedef cds_test::intrusive_speculative_pairing_queue base_class; + + protected: + typedef typename base_class::base_hook_item< ci::speculative_pairing_queue::node> base_item_type; + typedef typename base_class::member_hook_item< ci::speculative_pairing_queue::node> member_item_type; + + void SetUp() + { + typedef ci::SPQueue< gc_type, base_item_type > queue_type; + + cds::gc::hp::GarbageCollector::Construct( queue_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 ) { + ASSERT_EQ( arr[i].nDisposeCount, 3 ) << "i=" << i; + } + } + }; + + TEST_F( IntrusiveSPQueue_HP, defaulted ) + { + typedef cds::intrusive::SPQueue< gc_type, base_item_type, + typename ci::speculative_pairing_queue::make_traits< + ci::opt::disposer< mock_disposer > + >::type + > test_queue; + + + std::vector arr; + arr.resize(100); + { + test_queue q; + test(q, arr); + } + gc_type::scan(); + check_array( arr ); + } + + TEST_F( IntrusiveSPQueue_HP, base_hook ) + { + typedef cds::intrusive::SPQueue< gc_type, base_item_type, + typename ci::speculative_pairing_queue::make_traits< + ci::opt::disposer< mock_disposer > + ,ci::opt::hook< ci::speculative_pairing_queue::base_hook< ci::opt::gc>> + >::type + > test_queue; + + std::vector arr; + arr.resize(100); + { + test_queue q; + test(q, arr); + } + gc_type::scan(); + check_array( arr ); + } + + TEST_F( IntrusiveSPQueue_HP, base_item_counting ) + { + typedef cds::intrusive::SPQueue< gc_type, base_item_type, + typename ci::speculative_pairing_queue::make_traits< + ci::opt::disposer< mock_disposer > + , cds::opt::item_counter< cds::atomicity::item_counter > + , ci::opt::hook< ci::speculative_pairing_queue::base_hook< ci::opt::gc>> + >::type + > test_queue; + + std::vector arr; + arr.resize(100); + { + test_queue q; + test(q, arr); + } + gc_type::scan(); + check_array( arr ); + } + + TEST_F( IntrusiveSPQueue_HP, base_stat ) + { + struct traits : public ci::speculative_pairing_queue::traits + { + typedef mock_disposer disposer; + typedef cds::atomicity::item_counter item_counter; + typedef ci::speculative_pairing_queue::stat<> stat; + typedef cds::opt::v::sequential_consistent memory_model; + }; + typedef cds::intrusive::SPQueue< gc_type, base_item_type, traits > test_queue; + + std::vector arr; + arr.resize(100); + { + test_queue q; + test(q, arr); + } + gc_type::scan(); + check_array( arr ); + } + + TEST_F( IntrusiveSPQueue_HP, member_hook ) + { + typedef cds::intrusive::SPQueue< gc_type, member_item_type, + typename ci::speculative_pairing_queue::make_traits< + ci::opt::disposer< mock_disposer > + ,ci::opt::hook< ci::speculative_pairing_queue::member_hook< + offsetof( member_item_type, hMember ), + ci::opt::gc + >> + >::type + > test_queue; + + std::vector arr; + arr.resize( 100 ); + { + test_queue q; + test( q, arr ); + } + gc_type::scan(); + check_array( arr ); + } + + TEST_F( IntrusiveSPQueue_HP, member_hook_stat ) + { + struct traits : public ci::speculative_pairing_queue::traits + { + typedef ci::speculative_pairing_queue::member_hook< + offsetof( member_item_type, hMember ), + ci::opt::gc + > hook; + typedef mock_disposer disposer; + typedef cds::atomicity::item_counter item_counter; + typedef ci::speculative_pairing_queue::stat<> stat; + typedef cds::opt::v::sequential_consistent memory_model; + }; + typedef cds::intrusive::SPQueue< gc_type, member_item_type, traits > test_queue; + + std::vector arr; + arr.resize( 100 ); + { + test_queue q; + test( q, arr ); + } + gc_type::scan(); + check_array( arr ); + } + +} // namespace + diff --git a/test/unit/queue/speculative_pairing_queue_dhp.cpp b/test/unit/queue/speculative_pairing_queue_dhp.cpp new file mode 100644 index 000000000..d1f47d04a --- /dev/null +++ b/test/unit/queue/speculative_pairing_queue_dhp.cpp @@ -0,0 +1,115 @@ +// Copyright (c) 2006-2018 Maxim Khizhinsky +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include "test_generic_queue.h" + +#include +#include + +namespace { + namespace cc = cds::container; + typedef cds::gc::DHP gc_type; + + + class SPQueue_DHP : public cds_test::generic_queue + { + protected: + void SetUp() + { + typedef cc::SPQueue< gc_type, int > queue_type; + + cds::gc::dhp::smr::construct( queue_type::c_nHazardPtrCount ); + cds::threading::Manager::attachThread(); + } + + void TearDown() + { + cds::threading::Manager::detachThread(); + cds::gc::dhp::smr::destruct(); + } + }; + + TEST_F( SPQueue_DHP, defaulted ) + { + typedef cds::container::SPQueue< gc_type, int > test_queue; + + test_queue q; + test(q); + } + + TEST_F( SPQueue_DHP, item_counting ) + { + typedef cds::container::SPQueue < gc_type, int, + typename cds::container::speculative_pairing_queue::make_traits < + cds::opt::item_counter < cds::atomicity::item_counter > + > ::type + > test_queue; + + test_queue q; + test( q ); + } + + TEST_F( SPQueue_DHP, relaxed ) + { + typedef cds::container::SPQueue < gc_type, int, + typename cds::container::speculative_pairing_queue::make_traits < + cds::opt::item_counter< cds::atomicity::item_counter > + , cds::opt::memory_model < cds::opt::v::relaxed_ordering > + > ::type + > test_queue; + + test_queue q; + test( q ); + } + + TEST_F( SPQueue_DHP, aligned ) + { + typedef cds::container::SPQueue < gc_type, int, + typename cds::container::speculative_pairing_queue::make_traits < + cds::opt::memory_model< cds::opt::v::relaxed_ordering> + , cds::opt::padding < 32 > + >::type + > test_queue; + + test_queue q; + test( q ); + } + + TEST_F( SPQueue_DHP, seq_cst ) + { + struct traits : public cc::speculative_pairing_queue::traits + { + typedef cds::opt::v::sequential_consistent memory_model; + typedef cds::atomicity::item_counter item_counter; + enum { padding = 64 }; + }; + typedef cds::container::SPQueue < gc_type, int, traits > test_queue; + + test_queue q; + test( q ); + } + + TEST_F( SPQueue_DHP, move ) + { + typedef cds::container::SPQueue< gc_type, std::string > test_queue; + + test_queue q; + test_string( q ); + } + + TEST_F( SPQueue_DHP, move_item_counting ) + { + struct traits : public cc::speculative_pairing_queue::traits + { + typedef cds::atomicity::item_counter item_counter; + }; + typedef cds::container::SPQueue< gc_type, std::string, traits > test_queue; + + test_queue q; + test_string( q ); + } + +} // namespace + diff --git a/test/unit/queue/speculative_pairing_queue_hp.cpp b/test/unit/queue/speculative_pairing_queue_hp.cpp new file mode 100644 index 000000000..fb9d6d64b --- /dev/null +++ b/test/unit/queue/speculative_pairing_queue_hp.cpp @@ -0,0 +1,115 @@ +// Copyright (c) 2006-2018 Maxim Khizhinsky +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include "test_generic_queue.h" + +#include +#include + +namespace { + namespace cc = cds::container; + typedef cds::gc::HP gc_type; + + + class SPQueue_HP : public cds_test::generic_queue + { + protected: + void SetUp() + { + typedef cc::SPQueue< gc_type, int > queue_type; + + cds::gc::hp::GarbageCollector::Construct( queue_type::c_nHazardPtrCount, 1, 16 ); + cds::threading::Manager::attachThread(); + } + + void TearDown() + { + cds::threading::Manager::detachThread(); + cds::gc::hp::GarbageCollector::Destruct( true ); + } + }; + + TEST_F( SPQueue_HP, defaulted ) + { + typedef cds::container::SPQueue< gc_type, int > test_queue; + + test_queue q; + test(q); + } + + TEST_F( SPQueue_HP, item_counting ) + { + typedef cds::container::SPQueue < gc_type, int, + typename cds::container::speculative_pairing_queue::make_traits < + cds::opt::item_counter < cds::atomicity::item_counter > + > ::type + > test_queue; + + test_queue q; + test( q ); + } + + TEST_F( SPQueue_HP, relaxed ) + { + typedef cds::container::SPQueue < gc_type, int, + typename cds::container::speculative_pairing_queue::make_traits < + cds::opt::item_counter< cds::atomicity::item_counter > + , cds::opt::memory_model < cds::opt::v::relaxed_ordering > + > ::type + > test_queue; + + test_queue q; + test( q ); + } + + TEST_F( SPQueue_HP, aligned ) + { + typedef cds::container::SPQueue < gc_type, int, + typename cds::container::speculative_pairing_queue::make_traits < + cds::opt::memory_model< cds::opt::v::relaxed_ordering> + , cds::opt::padding < 32 > + >::type + > test_queue; + + test_queue q; + test( q ); + } + + TEST_F( SPQueue_HP, seq_cst ) + { + struct traits : public cc::speculative_pairing_queue::traits + { + typedef cds::opt::v::sequential_consistent memory_model; + typedef cds::atomicity::item_counter item_counter; + enum { padding = 64 }; + }; + typedef cds::container::SPQueue < gc_type, int, traits > test_queue; + + test_queue q; + test( q ); + } + + TEST_F( SPQueue_HP, move ) + { + typedef cds::container::SPQueue< gc_type, std::string > test_queue; + + test_queue q; + test_string( q ); + } + + TEST_F( SPQueue_HP, move_item_counting ) + { + struct traits : public cc::speculative_pairing_queue::traits + { + typedef cds::atomicity::item_counter item_counter; + }; + typedef cds::container::SPQueue< gc_type, std::string, traits > test_queue; + + test_queue q; + test_string( q ); + } + +} // namespace + diff --git a/test/unit/queue/test_intrusive_speculative_pairing_queue.h b/test/unit/queue/test_intrusive_speculative_pairing_queue.h new file mode 100644 index 000000000..e056a8a23 --- /dev/null +++ b/test/unit/queue/test_intrusive_speculative_pairing_queue.h @@ -0,0 +1,163 @@ +// Copyright (c) 2006-2018 Maxim Khizhinsky +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef CDSUNIT_QUEUE_TEST_INTRUSIVE_SPECULATIVE_PAIRING_QUEUE_H +#define CDSUNIT_QUEUE_TEST_INTRUSIVE_SPECULATIVE_PAIRING_QUEUE_H + +#include + +namespace cds_test { + + class intrusive_speculative_pairing_queue : public ::testing::Test + { + protected: + template + struct base_hook_item : public Base + { + int nVal; + int nDisposeCount; + + base_hook_item() + : nDisposeCount( 0 ) + {} + + base_hook_item( base_hook_item const& s) + : nVal( s.nVal ) + , nDisposeCount( s.nDisposeCount ) + {} + }; + + template + struct member_hook_item + { + int nVal; + int nDisposeCount; + Member hMember; + + member_hook_item() + : nDisposeCount( 0 ) + {} + + member_hook_item( member_hook_item const& s ) + : nVal( s.nVal ) + , nDisposeCount( s.nDisposeCount ) + {} + }; + + struct mock_disposer + { + template + void operator ()( T * p ) + { + ++p->nDisposeCount; + } + }; + + template + void test( Queue& q, Data& arr ) + { + typedef typename Queue::value_type value_type; + size_t nSize = arr.size(); + + value_type * pv; + for ( size_t i = 0; i < nSize; ++i ) + arr[i].nVal = static_cast(i); + + pv = q.pop(); + ASSERT_TRUE( pv == nullptr ); + ASSERT_TRUE( q.empty()); + ASSERT_CONTAINER_SIZE( q, 0 ); + + pv = q.dequeue(); + ASSERT_TRUE( pv == nullptr ); + ASSERT_TRUE( q.empty()); + ASSERT_CONTAINER_SIZE( q, 0 ); + + // push/pop test + //------------------------------------ + for ( size_t i = 0; i < nSize; ++i ) { + if ( i & 1 ) + q.push( arr[i] ); + else + q.enqueue( arr[i] ); + ASSERT_FALSE( q.empty()); + ASSERT_CONTAINER_SIZE( q, i + 1 ); + } + + for ( size_t i = 0; i < nSize; ++i ) { + ASSERT_FALSE( q.empty()); + ASSERT_CONTAINER_SIZE( q, nSize - i ); + if ( i & 1 ) + pv = q.pop(); + else + pv = q.dequeue(); + ASSERT_FALSE( pv == nullptr ); + ASSERT_EQ( pv->nVal, static_cast(i)); + } + ASSERT_TRUE( q.empty()); + ASSERT_CONTAINER_SIZE( q, 0 ); + + //dispose queue and create new + q.clear(); + Queue::gc::scan(); + for ( size_t i = 0; i < nSize; ++i ) { + ASSERT_EQ( arr[i].nDisposeCount, 1 ) << "i=" << i; + } + //------------------------------------ + //end push/pop test + + //clear test + //------------------------------------ + for ( size_t i = 0; i < nSize; ++i ) + { + q.push( arr[i] ); + ASSERT_CONTAINER_SIZE( q, i+1); + } + + ASSERT_FALSE( q.empty()); + ASSERT_CONTAINER_SIZE( q, nSize); + q.clear(); + ASSERT_CONTAINER_SIZE( q, 0 ); + ASSERT_TRUE( q.empty()); + + Queue::gc::scan(); + for ( size_t i = 0; i < nSize; ++i ) { + ASSERT_EQ( arr[i].nDisposeCount, 2 ) << "i=" << i; + } + //------------------------------------ + //end clear test + + //clear stale nodes test + //------------------------------------ + for ( size_t i = 0; i < nSize; ++i ) + { + q.push( arr[i] ); + ASSERT_CONTAINER_SIZE( q, i+1); + } + ASSERT_FALSE( q.empty()); + ASSERT_CONTAINER_SIZE( q, nSize); + + for ( size_t i = 0; i < nSize; ++i ) { + ASSERT_FALSE( q.empty()); + ASSERT_CONTAINER_SIZE( q, nSize - i ); + if ( i & 1 ) + pv = q.pop(); + else + pv = q.dequeue(); + ASSERT_FALSE( pv == nullptr ); + ASSERT_EQ( pv->nVal, static_cast(i)); + } + //clear queue + q.clear(); + Queue::gc::scan(); + for ( size_t i = 0; i < nSize; ++i ) { + ASSERT_EQ( arr[i].nDisposeCount, 3 ) << "i=" << i; + } + } + }; + +} // namespace cds_test + +#endif // CDSUNIT_QUEUE_TEST_INTRUSIVE_MSQUEUE_H