ABA problem

The ABA problem is a well-known issue in concurrent programming, particularly in lock-free data structures such as stacks and queues. It occurs when a value at a memory location changes from A to B and back to A, making a thread’s subsequent check (like in a compare-and-set operation (CAS)) believe nothing has changed even though operations have been performed on the data.

What is the ABA Problem?

To illustrate, consider a scenario with a stack and two threads:

  • Thread 1 begins a pop operation on the stack, reads the top value (A), and gets preempted before it can complete the pop.
  • Thread 2 completes two operations while Thread 1 is preempted: it pops the original A and pushes a new value B, then pops B and pushes a new A (the same value but a different instance).
  • Thread 1 resumes and sees the value A on top of the stack. If it uses a simple compare-and-set based on value alone, it assumes the stack has not changed since no other thread would have modified the stack between its read and write.

This can lead to incorrect behaviour because Thread 1 missed the changes Thread 2 made, possibly leading to corrupted data structure states or other errors, depending on the stack’s use.

Solution: Double-Wide Compare-and-Set (CAVD)

The double-wide compare-and-set (CAS) operation extends the traditional CAS by incorporating a versioning mechanism, often using an additional “counter” (ticket) or “tag” alongside the actual value to prevent the ABA problem. Here’s how it works:

  1. Version Counter: Each node or value on the stack has an associated version counter that is incremented on each modification (push or pop).
  2. Double-Wide CAS: When performing a CAS operation, both the value and the version counter are compared and swapped atomically. This means that the operation checks both the data value and the version counter, and only if both match the expected values, the CAS operation proceeds.