I'm reading the book <c++ concurrency in action 2nd. Ed> by Anthony Williams Section 7.2.2 Stopping those pesky leaks: managing memory in lock-free data strcutures.
before this section, we implemented a lock_free_stack
template <typename T> class lock_free_stack {
private:
struct node {
std::shared_ptr<T> data;
node *next;
node(T const &data_) : data(std::make_shared<T>(data_)) {}
};
std::atomic<node *> head;
public:
void push(T const &data) {
node *const new_node = new node(data);
new_node->next = head.load();
while (!head.compare_exchange_weak(new_node->next, new_node));
}
std::shared_ptr<T> pop() {
node *old_head = head.load();
while (old_head && !head.compare_exchange_weak(old_head, old_head->next));
return old_head ? old_head->data : std::shared_ptr<T>();
}
};
in section 7.2.2, anthony said:
When you first looked at pop(), you opted to leak nodes in order to avoid the race condition where one thread deletes a node while another thread still holds a pointer to it that it’s about to dereference.
I don't understand, if two threads calling pop() concurrently, each thread will get different head, so old_head of each thread differes from each other, and they are free to delete old_head.
In my opinion, the no-memory-leaks pop() would be:
std::shared_ptr<T> pop() {
node *old_head = head.load();
while (old_head && !head.compare_exchange_weak(old_head, old_head->next));
std::shared_ptr<T> res = old_head ? old_head->data : std::shared_ptr<T>();
delete old_head;
return res;
}
How to understand what anthony says ?