interviewpath
  • 0day streak
  • 10total XP
  • Level 1
0/6

Unit 3 · OS & concurrency · Lesson 8 of 11

Linearizability, concurrent data structures and memory reclamation

Linearizability is the standard correctness condition for concurrent data structures, and lock-free designs add two hard problems: ABA and safe memory reclamation. This specialty depth is expected in low-level, database-internals and systems-research interviews.
About 4 hFocus mode

Step 1: Learn the ideas

  • Histories, real-time order and linearizability
  • Linearization points and correctness arguments
  • Sequential consistency and serializability compared
  • Progress guarantees: blocking, obstruction-free, lock-free, wait-free
  • Treiber stack and Michael-Scott queue
  • The ABA problem and tagged pointers
  • Reference counting for reclamation
  • Hazard pointers
  • Epoch-based reclamation
  • Read-copy update (RCU) and grace periods
  • Testing linearizability and model checking

Why it matters

"Is this concurrent structure correct?" needs a definition before it has an answer, and linearizability is the one most libraries, databases and key-value stores use. Lock-free code adds a second problem that garbage-collected languages hide: when may a removed node be freed while other threads might still be reading it? Getting this wrong produces use-after-free bugs that surface rarely, and usually only under load.

Core ideas

Linearizability. Every operation appears to take effect atomically at one instant, its linearization point, somewhere between invocation and response, and the resulting order is a legal sequential history. Operations that do not overlap must keep their real-time order. The condition is local: a system built from linearizable objects is linearizable, so you can reason one object at a time. Sequential consistency drops the real-time requirement; serializability concerns multi-object transactions and also ignores real time.

Finding linearization points. In a lock-based structure, any instant inside the critical section works. In a Treiber stack, push and pop take effect at their successful compare-and-swap on top. In the Michael-Scott queue, enqueue takes effect at the CAS that links the new node after the last node, and dequeue at the CAS that swings head, or at the read that finds the queue empty. When other threads may complete your operation for you (helping), the point can depend on later events, which is where proofs get hard.

Progress. Lock-free means some thread always completes within a finite number of steps, so a descheduled thread cannot block the others; wait-free means every thread completes within a bounded number of its own steps. Neither implies higher throughput: CAS retry loops under contention can be slower than a good lock.

ABA. CAS checks that a value is unchanged, not that nothing happened. Thread 1 reads top = A and A.next = B, then stalls. Other threads pop A, pop B and push A again (a reused node). Thread 1's CAS(top, A, B) succeeds and installs B, which is no longer in the stack. Fixes: pair the pointer with a version counter (double-width CAS), use LL/SC, or make sure a node is not reused while anyone can still see it, which is the reclamation problem.

Safe memory reclamation.

  • Reference counting is simple, but every traversal writes shared counters, which creates contention.
  • Hazard pointers: a reader publishes the pointer it is about to dereference, then re-validates it; a reclaimer frees only retired nodes that no hazard pointer names. Unreclaimed memory stays bounded, but each protected access costs a store and a fence.
  • Epoch-based reclamation: threads announce the global epoch when entering a critical region, and a retired node is freed once every active thread has moved past the epoch in which it was retired. Reads are nearly free, but one stalled thread blocks all reclamation.
  • RCU: readers take no locks and, in the classic kernel implementation, write no shared memory; writers publish a new version with release semantics, wait for a grace period in which all pre-existing readers finish, then free the old version. It suits read-mostly data such as routing tables and configuration.

C++26 adds standard library support for both hazard pointers and RCU.

Common misconceptions

  • "Lock-free means faster." Its benefit is progress despite preemption or failure; throughput depends on contention.
  • "Linearizability is serializability." One is about single operations on objects in real time, the other about transactions over many objects.
  • "If the CAS succeeded, nothing changed." That is precisely the ABA trap.
  • "Garbage collection solves all of this." It removes use-after-free and pointer reuse, not the need for correct linearization points and memory ordering.
  • "Hazard pointers and epochs are interchangeable." They trade read-side cost against bounded memory and robustness to stalled threads.

How to approach it

Implement a Treiber stack, mark its linearization points in comments, construct the ABA interleaving with a deliberately reused node, and fix it twice. Check short recorded histories by hand or with a checker; stress tests alone miss rare interleavings. In interviews, define linearizability crisply, place linearization points, and explain one reclamation scheme with its tradeoff.

Suggested order

  1. 1Read Herlihy and Wing Sections 1–3 for the definition of linearizability, its locality property and the comparison with other conditions.
  2. 2Read the Michael and Scott PODC 1996 paper and mark the linearization point of each operation in its pseudocode.
  3. 3Read perfbook Ch. 9 (Deferred Processing): reference counting, hazard pointers (9.3) and RCU (9.5).
  4. 4Read What is RCU, Fundamentally? and Lock-freedom without garbage collection to compare RCU with epoch-based reclamation.
  5. 5Read perfbook Ch. 14 (Advanced Synchronization), section 14.2, for progress guarantees.
  6. 6Implement a Treiber stack, reproduce ABA with node reuse, and fix it with a tagged pointer and with epoch-based reclamation.

Step 2: Check off what you can do

Tap each one you can genuinely do or explain. Each earns 10 XP.

Step 3: Go deeper

  • Linearizability: A Correctness Condition for Concurrent ObjectsStart here

    Maurice Herlihy's page (Brown University)

    Primary source for the definition, real-time order and locality; Sections 1–3 are enough for interviews.

    Open(opens in a new tab)Link responded, 5 Oct 2026
  • Is Parallel Programming Hard, And, If So, What Can You Do About It?Start here

    kernel.org (author-maintained free book)

    Section: Ch. 9: Deferred Processing

    Reference counting, hazard pointers, sequence locks and RCU compared by an expert practitioner, with code.

    Open(opens in a new tab)Link responded, 5 Oct 2026
  • Simple, Fast, and Practical Non-Blocking and Blocking Concurrent Queue Algorithms

    Michael L. Scott's papers page (University of Rochester)

    Classic lock-free queue for practicing linearization points and counted pointers against ABA.

    Open(opens in a new tab)Link responded, 5 Oct 2026
  • Lock-freedom without garbage collection

    Aaron Turon's blog

    Intuitive explanation of epoch-based reclamation and what it costs.

    Open(opens in a new tab)Link responded, 5 Oct 2026
  • What is RCU, Fundamentally?

    LWN.net

    Short conceptual introduction to RCU’s publish, wait-for-readers and multiple-versions ideas.

    Open(opens in a new tab)Link responded, 5 Oct 2026

Step 4: Practice

There is no standard graded lab for this topic, so build it yourself in C++ or Rust: (1) implement a Treiber stack with compare-and-swap; (2) in a test harness, free and immediately reuse a popped node to force ABA and show the corruption; (3) fix it with a version-tagged pointer and, separately, with epoch-based reclamation (crossbeam-epoch in Rust, or a minimal hand-written epoch scheme); (4) record operation histories from a multithreaded stress test and check a few short ones for linearizability by hand. Run everything under ThreadSanitizer or AddressSanitizer. Optionally reimplement the Michael-Scott queue from the paper.

No external problems are linked; follow the guidance above.

Record attempts on each problem’s page (+15 XP, +10 more when solved on your own).

Step 5: Test yourself

Answer each one out loud or on paper before you finish the lesson.

  1. Question 1A write completes before a read begins, yet the read returns the old value. Is that history linearizable for a register?
  2. Question 2Why is linearizability called a local property, and why does that matter in practice?
  3. Question 3Where does a Michael-Scott dequeue take effect when the queue is empty?
  4. Question 4Why does a stalled thread hurt epoch-based reclamation but not hazard pointers?
  5. Question 5What does an RCU writer wait for before freeing the old version?
  6. Question 6Why is a lock-free algorithm not necessarily faster than a lock-based one?

You’re done when you can

  • Define linearizability precisely and contrast it with sequential consistency and serializability in under three minutes.
  • Mark the linearization points of both operations of a Treiber stack and of the Michael-Scott queue.
  • Reproduce the ABA problem in code and fix it in at least one way.
  • Compare hazard pointers, epoch-based reclamation and RCU in a table covering read cost, memory bound and stalled-thread behavior.

Finish the lesson

How confident do you feel?

Lesson complete!

+50 XP

Reviews will come up on the Practice tab so it sticks.

Your notes and study time

Log study time