Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Williams lock free stack (3304 - Сандин, Аксёнов, Итальянцев, Бенитез) #135

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
252 changes: 252 additions & 0 deletions cds/container/williams_lock_free_stack.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
#ifndef CDSLIB_CONTAINER_WILLIAMS_STACK_H
#define CDSLIB_CONTAINER_WILLIAMS_STACK_H

#include <memory>
#include <cds/container/details/base.h>

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 <typename... Options>
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 <typename T, typename Traits = williams_stack::traits>
class WilliamsStack
{
public:

template <typename T2, typename Traits2>
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<value_type>(data_) ),
internal_count(0)
{}

template <typename... Args>
node_type( Args&&... args ) : node_type( value_type( std::forward<Args>( 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 <typename... Args>
static node_type * alloc_node_move( Args&&... args )
{
return node_allocator().MoveNew( std::forward<Args>( 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 <typename... Args>
bool emplace(Args&&... args)
{
value_type val(std::forward<Args>(args)...);
return push(val);
}

bool pop(value_type& val)
{
return pop_with([&val](value_type& src) { val = src; });
}

// pop
template <typename Func>
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<value_type> 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
3 changes: 2 additions & 1 deletion projects/Win/vc141/cds.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1140,6 +1140,7 @@
<ClInclude Include="..\..\..\cds\container\striped_set\std_list.h" />
<ClInclude Include="..\..\..\cds\container\striped_set\std_set.h" />
<ClInclude Include="..\..\..\cds\container\striped_set\std_vector.h" />
<ClInclude Include="..\..\..\cds\container\williams_lock_free_stack.h" />
<ClInclude Include="..\..\..\cds\container\weak_ringbuffer.h" />
<ClInclude Include="..\..\..\cds\details\binary_functor_wrapper.h" />
<ClInclude Include="..\..\..\cds\details\bit_reverse_counter.h" />
Expand Down Expand Up @@ -1374,4 +1375,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>
5 changes: 4 additions & 1 deletion projects/Win/vc141/cds.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -1257,6 +1257,9 @@
</ClInclude>
<ClInclude Include="..\..\..\cds\details\tls_holder.h">
<Filter>Header Files\cds\details</Filter>
</ClInclude>
<ClInclude Include="..\..\..\cds\container\williams_lock_free_stack.h">
<Filter>Header Files\cds\container</Filter>
</ClInclude>
</ItemGroup>
</Project>
</Project>
1 change: 1 addition & 0 deletions test/stress/stack/push.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,5 +180,6 @@ namespace {
CDSSTRESS_FCStack( stack_push )
CDSSTRESS_FCDeque( stack_push )
CDSSTRESS_StdStack( stack_push )
CDSSTRESS_WilliamsStack( stack_push )

} // namespace
1 change: 1 addition & 0 deletions test/stress/stack/push_pop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
17 changes: 17 additions & 0 deletions test/stress/stack/stack_type.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <cds/container/treiber_stack.h>
#include <cds/container/fcstack.h>
#include <cds/container/fcdeque.h>
#include <cds/container/williams_lock_free_stack.h>

#include <cds/gc/hp.h>
#include <cds/gc/dhp.h>
Expand Down Expand Up @@ -344,6 +345,18 @@ namespace stack {
typedef cds::container::FCStack< T, std::stack<T, std::list<T> >, traits_FCStack_elimination > FCStack_list_elimination;
typedef cds::container::FCStack< T, std::stack<T, std::list<T> >, 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<cds::atomicity::empty_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<
Expand Down Expand Up @@ -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