advanced24 min studyLesson 156 in Engineering

Lock-free queues and ring buffers

Separate slot ownership, payload publication, and progress guarantees when designing concurrent FIFO structures.

On this page

What you’ll understand

  • Trace ownership through a bounded single-producer, single-consumer ring.
  • Identify publication and linearization events in two different queue designs.
  • Explain why atomic counters alone do not establish lock-free progress.

A queue connects independently paced parts of a system: an inference worker hands completed responses to a network thread, or an ingestion thread supplies decoded records to a trainer. The difficult question is not merely where the next value belongs. It is when another thread may read that value, when its storage may be reused, and what happens if an operation pauses halfway through. We will distinguish a small single-producer, single-consumer ring from a linked multiple-producer queue; their proofs depend on different ownership and progress arguments.

Prove slot ownership before optimizing instructions

For an illustrative ring with capacity C, let w count published writes and r count completed reads. Treat these as unbounded logical integers while proving the invariant: occupancy stays between zero and C. Logical item i occupies slot i modulo C. Exactly one producer advances w and exactly one consumer advances r, so neither side needs a compare-and-swap to compete with another writer of its own counter. A bounded machine implementation still needs an explicit counter-wrap argument; mathematical integers do not automatically justify overflowing signed arithmetic.

The producer initializes a slot before publishing an updated write counter with release ordering. The consumer uses an appropriate acquire observation of that publication before reading the payload. In the reverse direction, the consumer finishes reading before releasing the updated read counter; the producer acquires that information before overwriting reusable storage. These two handoffs protect ordinary payload accesses. A stale observation can temporarily report full or empty, but must never authorize an unsafe read or overwrite. Padding counters can reduce false sharing without changing the proof.

q=w−r,0≤q≤C,slot⁡(i)=i mod Cq=w-r,\qquad 0\le q\le C,\qquad \operatorname{slot}(i)=i\bmod C
Here w, r, q, and C count items; logical counters are nonnegative integers and C is a positive integer. Counter wrap requires a separate implementation argument.↔ Scroll wide equations horizontally.

Linked queues need helping and safe reclamation

A Michael–Scott-style linked queue begins with a dummy node. An enqueue prepares a node privately, then tries to link it after the observed last node. The successful link is the enqueue publication and linearization event in that algorithm. Updating the tail hint can happen later: another thread can notice the lag and help advance it. This separation matters when the original enqueuer is descheduled after linking; useful work need not wait for that particular thread to resume.

A successful nonempty dequeue advances the head to the next node; that next node becomes the new dummy. The removed old dummy cannot simply be freed because another operation may still hold its address. A reclamation protocol supplies the missing lifetime guarantee. Read the original pseudocode with its allocator assumptions rather than transplanting a few CAS operations into arbitrary allocation code. The abstract FIFO proof, the language memory-model proof, and the reclamation proof are separate obligations, even when a library packages all three.

Capacity and contention are different constraints

A bounded ring needs a defined full-queue policy: reject, retry, shed work, or block through an external mechanism. Reserving a slot with fetch-add does not by itself publish initialized data. In a multiple-producer design, a paused producer may leave a reservation hole that prevents consumers from taking later items. Whether the complete algorithm is lock-free depends on how it handles such holes, allocation, and stalled participants; the mere presence of atomic instructions proves no progress property.

Use the queue lab to separate load imbalance from synchronization correctness. Compare independent fixed-rate experiments: choose Reset, explicitly set both arrival and service rates, predict the slope, then Play or Step. Use arrival four and service three for growth; for draining choose Reset again, explicitly set arrival two and service three, predict, then Play or Step. Editing a control pauses but retains elapsed time and recomputes from the initial backlog; it is not a live rate change preserving queue contents. Its initial backlog is four items and its fluid approximation has no ring capacity, reservations, threads, or CAS failures. The separately displayed M/M/1 residence time additionally assumes Poisson arrivals, exponential service, and a stable single server. Neither picture validates a concurrent queue implementation; use it to ask whether a correct queue will still grow under the offered workload.

B(t)=max⁡ ⁣(0,4+(λ−μ)t)B(t)=\max\!\left(0,4+(\lambda-\mu)t\right)
For this illustrative fluid model, B is measured in items, t in seconds, and nonnegative arrival and service rates in items per second; the model has no finite-capacity overflow policy.↔ Scroll wide equations horizontally.

Trace an eight-slot handoff

Assume an SPSC ring with eight usable slots, unbounded logical counters, r=13 and w=18. The producer publishes three items, then the consumer finishes four reads. Which slots are written and what occupancy remains?

  1. Initially there are 18−13=5 unread items, so exactly three of the eight slots are reusable. The producer must not publish a fourth additional item before observing a completed read.

  2. The new logical items are 18, 19, and 20, which use physical slots 2, 3, and 4 because their indices are reduced modulo eight. After their safe publication, w=21 and occupancy is eight.

  3. The consumer reads logical items 13 through 16 before publishing r=17. The producer can now observe four reusable slots; the remaining occupancy is 21−17=4.

The result

The ring ends half full. Slot indices wrap while logical occupancy does not; acquire/release handoffs are still required around these arithmetic facts.

Explore the model

Interactive lab

Interactive lab · queue

Load imbalance is not a CAS trace

Compare fixed-rate runs using Reset → explicitly set both arrival and service → predict → Play or Step. Try (4,3) for growth and Reset plus (2,3) for draining. Controls retain elapsed time when edited and recompute from four initial items; they do not change live queue rates while preserving backlog. They are not producer counts or memory-order choices.

Load imbalance is not a CAS traceFluid backlog starts at 4 and follows max(0, 4 + (λ − μ)t). The separate M/M/1 residence-time formula assumes Poisson arrivals, exponential service and λ < μ. Current readings: Fluid backlog: 4; Net rate · λ − μ: −1 /s; M/M/1 mean residence: 1 s. All parameters are adjustable with the labeled controls below.02.557.51001.534.56Elapsed time · sFluid backlogλ < μ
Fluid backlogCurrent time
Fluid backlog
4
Net rate · λ − μ
−1 /s
M/M/1 mean residence
1 s
t = 0 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 forA stable offered load and a correct concurrent queue answer different questions. This lab models only backlog and a separately qualified M/M/1 residence time.

Check your understanding

In the linked queue described here, an enqueuer links its node and then pauses before updating the tail hint. What permits continued progress?

Try it yourself

In the same eight-slot model, r=25 and w=31. Can the producer publish three new items without an intervening read? Identify the first two physical slots.

Take a moment to make a prediction or work it out on paper. Open a hint when you need one.

Give me a hint

Calculate free capacity before reducing logical indices modulo eight. A wrapped slot index is not evidence that storage is reusable.

Compare with the solution

Occupancy is six, so only two slots are free. Logical items 31 and 32 use slots 7 and 0. After publishing them, w=33 and occupancy is eight; publishing item 33 must wait for or fail on the full condition according to the API. The counter protocol must also ensure payload publication and reuse ordering.

Take it with you

  • Prove exclusive slot ownership and both publication directions.
  • Helping can decouple completion from a stalled queue participant.
  • Atomic instructions, finite capacity, and safe reclamation need separate arguments.

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.