In the book "C++ Concurrency in Action" by Anthony Williams, when describing the listing "7.9 A lock-free stack using a lock-free std::shared_ptr<>", the author states that we have to:
to clear the next pointer from the popped node in order to avoid the potential for deeply nested destruction of nodes when the last std::shared_ptr referencing a given node is destroyed
template <typename T> class lock_free_stack { private: struct node { std::shared_ptr<T> data; std::shared_ptr<node> next; node(T const &data_) : data(std::make_shared<T>(data_)){} }; std::shared_ptr<node> head; public: void push(T const &data) { std::shared_ptr<node> const new_node = std::make_shared<node>(data); new_node->next = std::atomic_load(&head); while (!std::atomic_compare_exchange_weak(&head,&new_node->next, new_node)); } std::shared_ptr<T> pop() { std::shared_ptr<node> old_head = std::atomic_load(&head); while (old_head && !std::atomic_compare_exchange_weak(&head, &old_head, std::atomic_load(&old_head->next))); if (old_head) { std::atomic_store(&old_head->next, std::shared_ptr<node>()); return old_head->data; } return std::shared_ptr<T>(); } ~lock_free_stack() { while (pop()) ; } };
The line he's referring to is this one:
std::atomic_store(&old_head->next, std::shared_ptr<node>());
I don't see why that line is needed since the node pointed by old_head->next
is also pointed by std::shared_ptr<node> head;
or another thread executing pop()
, therefore there will be still one shared pointer pointing to the node so the node won't be deleted.
Am I missing something, or is my view correct?