diff --git a/cds/container/williams_lock_free_stack.h b/cds/container/williams_lock_free_stack.h new file mode 100644 index 000000000..182d24a35 --- /dev/null +++ b/cds/container/williams_lock_free_stack.h @@ -0,0 +1,252 @@ +#ifndef CDSLIB_CONTAINER_WILLIAMS_STACK_H +#define CDSLIB_CONTAINER_WILLIAMS_STACK_H + +#include +#include + +namespace cds +{ + namespace container + { + namespace williams_stack + { + /// Williams_Stack dummy statistics, no overhead + struct empty_stat: public cds::algo::flat_combining::empty_stat + { + //@cond + void onPush() {} + void onPushMove() {} + void onPop(bool) {} + void onCollide() {} + //@endcond + }; + + struct traits + { + typedef CDS_DEFAULT_ALLOCATOR allocator; + typedef atomicity::empty_item_counter item_counter; + typedef empty_stat stat; + }; + + 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 williams_stack + + template + class WilliamsStack + { + public: + + template + struct rebind + { + typedef WilliamsStack< T2, Traits2 > other; + }; + + typedef T value_type; + typedef Traits traits; + + typedef typename traits::item_counter item_counter; + typedef typename traits::stat stat; + + protected: + struct node_type; + + struct counted_node_ptr + { + int external_count; + node_type * ptr; + }; + + struct node_type + { + std::shared_ptr< value_type > data; + atomics::atomic< int > internal_count; + counted_node_ptr next; + + node_type( const value_type& data_ ) + : data( std::make_shared(data_) ), + internal_count(0) + {} + + template + node_type( Args&&... args ) : node_type( value_type( std::forward( args )... ) ) + {} + + node_type() : node_type( (value_type*) nullptr ) + {} + }; + + atomics::atomic< counted_node_ptr > head; + item_counter itemCounter; + stat m_stat; + + public: + typedef typename traits::allocator::template rebind< node_type >::other allocator_type; + + protected: + typedef cds::details::Allocator< node_type, allocator_type > node_allocator; + + static node_type * alloc_node() + { + return node_allocator().New(); + } + + static node_type * alloc_node( value_type const& val ) + { + return node_allocator().New( val ); + } + + template + static node_type * alloc_node_move( Args&&... args ) + { + return node_allocator().MoveNew( std::forward( args )... ); + } + + static void free_node( node_type * p ) + { + if (p) node_allocator().Delete( p ); + } + + void increase_head_count(counted_node_ptr& old_counter ) + { + counted_node_ptr new_counter; + + do + { + new_counter = old_counter; + ++new_counter.external_count; + } while (!head.compare_exchange_strong( old_counter, new_counter, + std::memory_order_acquire, + std::memory_order_relaxed)); + + old_counter.external_count = new_counter.external_count; + } + + public: + WilliamsStack() + { + counted_node_ptr _head; + _head.external_count = 0; + _head.ptr = nullptr; + head.store(_head, atomics::memory_order_relaxed); + } + + ~WilliamsStack() + { + clear(); + free_node( head.load().ptr ); + } + + WilliamsStack( const WilliamsStack& other ) = delete; + WilliamsStack& operator=( const WilliamsStack& other ) = delete; + + // push + bool push( value_type const& val ) + { + counted_node_ptr new_node; + new_node.ptr = alloc_node(val); + new_node.external_count = 1; + + new_node.ptr->next = head.load(std::memory_order_relaxed); + + while (!head.compare_exchange_weak( new_node.ptr->next, new_node, + std::memory_order_release, + std::memory_order_relaxed)) + {} + + ++itemCounter; + return true; + } + + template + bool emplace(Args&&... args) + { + value_type val(std::forward(args)...); + return push(val); + } + + bool pop(value_type& val) + { + return pop_with([&val](value_type& src) { val = src; }); + } + + // pop + template + bool pop_with(Func f) + { + counted_node_ptr old_head = head.load(atomics::memory_order_relaxed); + while (true) + { + increase_head_count(old_head); + node_type * const ptr = old_head.ptr; + if (!ptr) + { + return false; + } + + counted_node_ptr next = ptr->next; + if (head.compare_exchange_strong(old_head, next, + std::memory_order_relaxed)) + { + std::shared_ptr res; + res.swap(ptr->data); + + int const count_increase = old_head.external_count - 2; + + if (ptr->internal_count.fetch_add(count_increase, std::memory_order_release) == -count_increase) + { + delete ptr; + } + + --itemCounter; + f(*res); + return true; + } + else + { + if (ptr->internal_count.fetch_add(-1, std::memory_order_relaxed) == 1) + { + ptr->internal_count.load(std::memory_order_acquire); + delete ptr; + } + } + } + } + + void clear() + { + value_type v; + while( pop( v ) ); + } + + bool empty() const + { + return head.load().ptr == nullptr; + } + + size_t size() const + { + return itemCounter.value(); + } + + stat const& statistics() const + { + return m_stat; + } + }; + + } +} // namespace cds::container + +#endif // #ifndef CDSLIB_CONTAINER_WILLIAMS_STACK_H diff --git a/projects/Win/vc141/cds.vcxproj b/projects/Win/vc141/cds.vcxproj index 9b904397c..112002393 100644 --- a/projects/Win/vc141/cds.vcxproj +++ b/projects/Win/vc141/cds.vcxproj @@ -1140,6 +1140,7 @@ + @@ -1374,4 +1375,4 @@ - \ No newline at end of file + diff --git a/projects/Win/vc141/cds.vcxproj.filters b/projects/Win/vc141/cds.vcxproj.filters index d9c34bb92..f3266ccb7 100644 --- a/projects/Win/vc141/cds.vcxproj.filters +++ b/projects/Win/vc141/cds.vcxproj.filters @@ -1257,6 +1257,9 @@ Header Files\cds\details + + + Header Files\cds\container - \ No newline at end of file + diff --git a/test/stress/stack/push.cpp b/test/stress/stack/push.cpp index ae582aae7..bdcf756e8 100644 --- a/test/stress/stack/push.cpp +++ b/test/stress/stack/push.cpp @@ -180,5 +180,6 @@ namespace { CDSSTRESS_FCStack( stack_push ) CDSSTRESS_FCDeque( stack_push ) CDSSTRESS_StdStack( stack_push ) + CDSSTRESS_WilliamsStack( stack_push ) } // namespace diff --git a/test/stress/stack/push_pop.cpp b/test/stress/stack/push_pop.cpp index 74d8b8968..e2e82b0d1 100644 --- a/test/stress/stack/push_pop.cpp +++ b/test/stress/stack/push_pop.cpp @@ -257,5 +257,6 @@ namespace { CDSSTRESS_FCStack( stack_push_pop ) CDSSTRESS_FCDeque( stack_push_pop ) CDSSTRESS_StdStack( stack_push_pop ) + CDSSTRESS_WilliamsStack( stack_push_pop ) } // namespace diff --git a/test/stress/stack/stack_type.h b/test/stress/stack/stack_type.h index 4e58955ba..4ce2d9a7b 100644 --- a/test/stress/stack/stack_type.h +++ b/test/stress/stack/stack_type.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -344,6 +345,18 @@ namespace stack { typedef cds::container::FCStack< T, std::stack >, traits_FCStack_elimination > FCStack_list_elimination; typedef cds::container::FCStack< T, std::stack >, traits_FCStack_elimination_stat > FCStack_list_elimination_stat; + + // WilliamsStack + typedef cds::container::WilliamsStack< T > WilliamsStack_default; + + struct traits_WilliamsStack_item_counter : + public cds::container::williams_stack::make_traits< + cds::opt::item_counter + >::type + {}; + + typedef cds::container::WilliamsStack< T, traits_WilliamsStack_item_counter> WilliamsStack_item_counter; + // FCDeque struct traits_FCDeque_stat: public cds::container::fcdeque::make_traits< @@ -540,5 +553,9 @@ namespace cds_test { CDSSTRESS_Stack_F( test_fixture, StdStack_List_Mutex ) \ CDSSTRESS_Stack_F( test_fixture, StdStack_List_Spin ) +#define CDSSTRESS_WilliamsStack( test_fixture ) \ + CDSSTRESS_Stack_F( test_fixture, WilliamsStack_default ) \ + CDSSTRESS_Stack_F( test_fixture, WilliamsStack_item_counter ) + #endif // #ifndef CDSSTRESS_STACK_TYPES_H