This blog post leaves a lot to the imagination.
> Here’s the case that broke our stack:
Thread 1, in preparation for a Pop operation, reads the head of the stack.
Thread 1 writes the current head to its hazard pointer (using only release semantics,
which are weaker than sequentially consistent semantics).
Thread 1 reads the head of the stack again.
Thread 2 removes head from the stack, and passes it to the garbage collector thread
(using sequentially consistent memory semantics).
The garbage collector scans the hazard pointers,\ and (because the assignment was not
done with sequentially consistent memory semantics) is not guaranteed to see thread
1’s hazard pointer pointing to the node.
The garbage collector deletes the node
Thread 1 dereferences the node, and segfaults.
My interpretation is that with release semantics for the store, the 2nd read (load) in Thread 1 is actually allowed to be reordered before the release store to the hazard pointer. But they are not very explicit about it.
> So if thread 2 removing the pointer happens first, thread 1 will see a different value on its second read and not attempt to dereference it.
Thread 1 will see thread 2's remove even with release semantics for that store -- the store has a data dependency on the first load; they cannot be reordered.
> If thread 1 writes to its hazard pointer first, the garbage collector is guaranteed to see that value and not delete the node.
Yeah, this must be it. Thread 1 fails to notice the GC happened while it was writing its HP because its second load actually happened before the HP store.
Folly's hazard pointer implementation uses a release store to update the hazard pointer (here: reset_protection()), but uses some sort of SeqCst barrier between the store and the 2nd load (with acquire semantics): https://github.com/facebook/folly/blob/main/folly/synchroniz...