From answers to my previous question:Pointers in c++ after delete
it's become clear, that using the values of pointers, that point to "deleted" memory (in particular, copying them) lead to undef behaviour in c++11, and implementation-defined behaviour in c++14.
Antomy Williams in his book "C++ concurrency in action" suggests the next lock-free stack with reference counting:
#include <atomic>#include <memory>template<typename T>class lock_free_stack{private: struct node; struct counted_node_ptr { int external_count; node* ptr; }; struct node { std::shared_ptr<T> data; std::atomic<int> internal_count; counted_node_ptr next; node(T const& data_): data(std::make_shared<T>(data_)), internal_count(0) {} }; std::atomic<counted_node_ptr> head; 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: ~lock_free_stack() { while(pop()); } void push(T const& data) { counted_node_ptr new_node; new_node.ptr=new node(data); 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)); } std::shared_ptr<T> pop() { counted_node_ptr old_head= head.load(std::memory_order_relaxed); for(;;) { increase_head_count(old_head); node* const ptr=old_head.ptr; if(!ptr) { return std::shared_ptr<T>(); } if(head.compare_exchange_strong( old_head,ptr->next,std::memory_order_relaxed)) { std::shared_ptr<T> 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; } return res; } else if(ptr->internal_count.fetch_add( -1,std::memory_order_relaxed)==1) { ptr->internal_count.load(std::memory_order_acquire); delete ptr; } } }};
I'm worried about function
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;}
It seems, that new_counter=old_counter; can be executed, when old_counter.ptr has been deleted by another thread.
So, could somebody confirm or refuse, that this stack implementation is strictly incorrect in c++11?