for exam, need to know all different techniques used to pre-empt the problem.
See https://jbesic.ch/slides/session_12.pdf#page=105 for examples
14.1 ABA problem
We will lock at the lock-free stack. However instead of assuming an infinite supply of memory, this time we’ll reuse nodes.
→ Node pool
14.1.1 Node Pool
We use a stack again, to hold the unused nodes. We then only swap the nodes between the “active” stack and the “inactive” stack (which stores currently non-used nodes).
- a node just
dequeuedis put into the node pool - to
enqueuewe take a node from the pool

if the node pool is empty → we create a new Node. Otherwise it’s the exact same as the previous stack implementation.
To then use this, we modify the stack code:

14.1.2 ABA Problem
If we use this in production, we’ll see an issue where values don’t match.
- for testing: push/pop 10k values
- check the sum pushed value == sum of popped value
This is the ABA Problem in practice.

we lose the B element, because the interrupted thread has a stale reference to the next of the head.
Defined more generally

14.2 Solving the ABA Problem
Easy solutions to the ABA Problem that are “cheating”
-
DCAS / Multi-Word CAS
- doesn’t exist on most platforms so we have no choice.
- DCAS solves the problem by providing the following operation in
pop- we read
headfrom top - then try
DCAS(refs=[top, head.next], old=[head, next], new=[next, next])- so if
top == headandhead.next = next→ swap
- so if
- if fails, re-get head
- we read
- Note: technically we only need double-compare + single swap
-
Garbage Collection (GC)
- relies on the existance of a GC
- even though the GC might give us the same address again → not possible to get ABA problem
- the interrupted thread still holds a reference to the
Anode, so it can’t get re-used
- the interrupted thread still holds a reference to the
- but no GC available in kernel etc.. (to slow as well)
14.2.3 Pointer Tagging
This does not cure the problem, but it delays it significantly.
Just makes it less probable:
- there are now “32 versions” of each pointer

14.2.4 Hazard Pointers
The issues stems from reusing a pointer that has been read by some thread X but not yet written by CAS.
Idea?
- before
Xreads mark it hazardous by entering it into one of the ( number of threads) slots of an array associated with the datastructure - when finished → remove from the array
→ beforeYtries to re-use → checks all entries of the hazard array
Each thread gets a location in the Hazardous Array → can set one pointer to hazardous.

We now have this operation to check if a pointer is hazardous.


only exit the initial do while after having set the head to a hazardous pointer.
re-check if we can put the pointer back into the pool → have to call isHazardous again.
How to protect the node pool? The node pool itself suffers from the ABA problem…
Solutions:
- Thread-local node pools
- no protection necessary
- does not help when push/pop are not well balanced
- they run out of nodes or have to many…
- Hazard Pointers on the global node pool
- expensive operation for node reuse
- equivalent to code above → node pool only returns a node only when it is not hazardous
- Hybrid of both
- node-local + global node pool
- we get new nodes from the global pool when imbalance
- or get rid of them by giving them to the global
- node-local + global node pool
(Exam relevant)
Pros
- actually solves the ABA Problem
Cons
- doesn’t improve performance in comparison to memory allocation + GC
The hazard-pointers are placed in thread-local storage.
When that is replaced by processor-local storage → scales better.
Note: on ARM architectures, the LL/SC operation prevents the ABA problem completely
- LL/SC pair of instructions(load linked, store conditional)
- loads a value
- store-conditional only updates it if no updates have occurred to that location since the load-link.
→ gives us lock-free, atomic, read-modify-write operation.
LL/SC is basically hazard pointers in hardware but better.
14.3 Lessons Learned
Lock-free programming: new kinds of problems in comparison to lock-based programming:
- atomic updates of several pointers / values impossible
- threads that help each other to guarantee global progress
- ABA problem (disappears with a garbage collector)
- beware of ABA on values (not pointers) → GC does NOT prevent those