Linearizability and progress: two different promises
Check a concurrent history against a sequential contract and distinguish blocking, obstruction-free, lock-free, and wait-free behavior.
On this page
The question for today
What you’ll understand
- Find a legal sequential explanation that respects non-overlapping operations.
- Distinguish linearizability safety from progress guarantees.
- Identify allocation, reclamation, and callbacks that widen a method's progress boundary.
A concurrent object needs a precise story about what its completed operations mean. Linearizability says they can be explained by a legal sequential history while respecting the real-time order of non-overlapping operations. Progress asks a different question: under which execution assumptions must operations finish? A queue can return correct FIFO results and still stop all callers behind a descheduled lock owner. Conversely, a rapidly returning implementation may violate FIFO. Review safety and liveness as separate claims.
A concurrent history must fit the sequential specification
Record invocation, response, arguments, return values, and operation identity. If one operation returns before another is invoked, the sequential explanation must preserve that precedence. Overlapping operations may be ordered either way if the result matches the object's sequential contract. For a FIFO queue, a dequeue must return the oldest previously enqueued item in the chosen order; it cannot invent a value. The history includes failed or empty outcomes because they constrain which state could legally have existed.
A linearization point is an abstract instant within an operation's invocation-response interval at which its effect is considered to occur. Some algorithms have an obvious successful CAS as that point. Others need a more involved proof, an operation-dependent point, or reasoning based on a later observation. Do not require every concurrent algorithm to share one fixed source line for every operation. Pending operations also need careful treatment when formally completing or discarding parts of a finite history.
Progress quantifies over executions, not averages
A blocking method can depend on a particular owner or waiter taking a step. Obstruction freedom guarantees completion when an operation eventually runs without interference for long enough. Lock freedom guarantees system-wide progress under its execution assumptions: threads continue taking steps and some operations complete, even though a particular caller may repeatedly lose. Wait freedom provides per-operation completion within a finite bound on that caller's own steps, potentially dependent on parameters such as participant count. None of these labels is a wall-clock latency service level by itself.
The progress scope must include what the advertised operation actually does. A lock-free pointer algorithm wrapped around a blocking allocator is not automatically a lock-free public queue operation. Reclamation may accumulate memory when a participant stalls; user-defined item construction may acquire locks or throw. State whether the proof assumes preallocated storage, lock-free atomic primitives, nonblocking callbacks, finite participants, or a specific scheduler condition. Those assumptions are part of the claim a caller needs to assess.
Choose the guarantee that matches the requirement
For a request-serving queue, a short mutex critical section may outperform a complex nonblocking design under the measured workload. For a runtime where one paused owner must not halt all other participants, a system-progress guarantee may be essential. Decide from failure and latency requirements rather than assuming lock-free means faster. Measure contention, tail latency, memory growth, and operation outcomes, while keeping those measurements distinct from the proof that excludes a class of bad executions.
The queue lab illustrates the difference between throughput and an individual latency promise. Begin a fixed-rate experiment with Reset, explicitly set arrival to two and service to three, predict draining from four jobs, then Play or Step. Compare a separate near-capacity run by choosing Reset again, explicitly setting arrival to 2.5 and service to three, predicting, then Play or Step. The separate M/M/1 residence estimate is two seconds rather than one under its assumptions. A control edit retains elapsed time and recomputes the fixed-rate model; it does not continue the old backlog with a new slope. Neither readout guarantees that a particular concurrent caller finishes, and the model has no CAS retries, locks, or scheduler. Use the animation to ask which metric a claim describes before inspecting the algorithm's actual progress argument.
Work it through
Linearize two overlapping enqueues
An initially empty FIFO records enqueue(A) from time zero to four milliseconds and enqueue(B) from one to two milliseconds. After both return, two non-overlapping dequeues return B then A. Is that history necessarily invalid?
The two enqueues overlap: enqueue(A) was invoked first but did not return before enqueue(B) began. Their invocation order alone does not force A before B in a linearization.
Choose enqueue(B) before enqueue(A), with candidate linearization instants 1.5 and three milliseconds respectively. Both instants lie inside the corresponding operation intervals and precede the later dequeues.
The sequential queue after those enqueues is [B,A], so dequeuing B then A respects FIFO. By contrast, if enqueue(A) had completed at 0.5 milliseconds before enqueue(B) began at one, the same dequeue order would violate the required precedence.
The overlapping history has a legal FIFO explanation. This safety conclusion says nothing about whether either enqueue has a bounded step count in every execution.
The living notebook
Explore the model
Interactive lab · queue
Throughput is not a per-caller guarantee
For each fixed-rate comparison use Reset → explicitly set both λ and μ → predict → Play or Step. Compare (λ,μ)=(2,3) with (2.5,3), resetting before the second run. A rate edit retains elapsed time and recomputes backlog from four initial jobs. The separate stable M/M/1 estimate does not prove caller progress, and the model contains no lock or CAS loop.
- Fluid backlog
- 4
- Net rate · λ − μ
- −1 /s
- M/M/1 mean residence
- 1 s
Fluid backlog starts at 4 and follows max(0, 4 + (λ − μ)t). The separate M/M/1 residence-time formula assumes Poisson arrivals, exponential service and λ < μ. Parameter changes pause playback and recompute the model at the current elapsed time. Reset restores both parameters and returns time to zero. Playback starts paused. Adjust either parameter, then choose Play or Step.
Look forAn aggregate capacity estimate cannot establish linearizability or a bound on one operation's own steps.
Pause & reason
Check your understanding
Your turn
Try it yourself
An algorithm's total work is 120 processor-milliseconds and its dependency span is 35 milliseconds. What ideal lower bound applies on four processors? Would a measured 40-millisecond run prove its queue wait-free?
Take a moment to make a prediction or work it out on paper. Open a hint when you need one.
Give me a hint
Take the larger of work divided by processor count and span, then distinguish one observation from an execution-wide bound.
Compare with the solution
Work divided by four is 30 milliseconds, so the span dominates and the ideal lower bound is 35 milliseconds. A 40-millisecond run is compatible with that bound, but proves neither optimality nor wait freedom. A wait-free claim needs an algorithmic bound on each operation's own steps under documented assumptions. One timing sample does not cover adversarial interleavings, preemption, allocator behavior, or all participating threads. It also cannot establish FIFO safety without checking operation outcomes against a specification.
Keep the idea
Take it with you
- Linearizability preserves the real-time precedence of non-overlapping operations while permitting legal orders for overlap.
- Lock freedom guarantees system progress; wait freedom concerns each operation's own bounded steps.
- Progress claims must include public-operation dependencies, not only a central CAS loop.
Read further
Original sources and deeper explanations to follow at your own pace.
Optional progress, saved in this browser when available. Revisit any lesson at any time.
Lesson not marked complete.