The ABA problem is a well-known issue in CAS-based lock-free data structures.
C++ supports std::atomic for arbitrary data types, but it is not always lock-free.
You can check it with below.
1 | |
The output will look like:
1 | |
In fact, CAS-possible data size is limited to the HW support. However, most of HW supports only memory size CAS operation. If it is 32-bit computing unit, it usually supports 32-bit CAS. If it is 64-bit computing unit, it usually supports 64-bit CAS. Therefore, most of CAS based data structures use a pointer to work. Below lock-free queue uses this.
1 | |
However, this code has a subtle problem (you can reproduce it yourself). Since the CAS checks only the pointer value to determine whether it has changed, it cannot detect when the same address is reallocated for a new object. The following scenario demonstrates the issue.
Assume there is one CAS-based lock-free stack $Q$ and two threads $T_1$ and $T_2$. $Q$ initially contains $A$ at the top and $B$ below it. Thread $T_1$ reads $A$ as the top and $B$ as the next element, then goes to sleep. Thread $T_2$ pops both $A$ and $B$, then allocates a new node that happens to reuse $A$’s address and pushes it. When $T_1$ wakes up, its CAS succeeds because the top pointer still matches $A$’s address. As a result, the new top points to $B$, which has already been freed — a dangling pointer. The data structure is now in a corrupted state.