Вот та реализация lock-free стека которая падает у меня.
Кстати если закомментировать строчки выделеные жирным, то падать перестаёт.
Ну и разумеется для воспроизведения падения, необходимо что бы pop вызывали несколько потоков.
template<typename T>
class lf_stack_pop_dummy_guard
{
public:
lf_stack_pop_dummy_guard() :
head_(0), threads_in_pop_(0), to_be_deleted_(0)
{}
lf_stack_pop_dummy_guard(const lf_stack_pop_dummy_guard&) = delete;
lf_stack_pop_dummy_guard& operator=(const lf_stack_pop_dummy_guard&) = delete;
~lf_stack_pop_dummy_guard()
{
node* head = head_.load(std::memory_order_relaxed);
while(head)
{
node* old_head = head;
head = head->next_;
delete old_head;
}
}
void push(const T& val)
{
node* const new_node = new node(val);
new_node->next_ = head_.load();
while(!head_.compare_exchange_weak(new_node->next_, new_node));
}
std::shared_ptr<T> pop()
{
++threads_in_pop_;
node* old_head = head_.load();
while(old_head && !head_.compare_exchange_strong(old_head, old_head->next_));
std::shared_ptr<T> res;
if (old_head)
res.swap(old_head->data_);
node* nodes_to_delete = to_be_deleted_.load();
if (!--threads_in_pop_)
{
if (to_be_deleted_.compare_exchange_strong(nodes_to_delete, 0))
delete_nodes(nodes_to_delete);
delete old_head;
}
else if (old_head)
{
old_head->next_ = nodes_to_delete;
while(!to_be_deleted_.compare_exchange_weak(old_head->next_, old_head));
}
return res;
}
private:
struct node
{
node(const T& data) : data_(new T(data)), next_(0) {}
node() = delete;
node(const node&) = delete;
node& operator=(const node&) = delete;
std::shared_ptr<T> data_;
node* next_;
};
static void delete_nodes(node* head)
{
while(head)
{
node* next = head->next_;
delete head;
head = next;
}
}
std::atomic<node*> head_;
std::atomic<unsigned> threads_in_pop_;
std::atomic<node*> to_be_deleted_;
};