Table of Contents

Lock convoy

A lock convoy is a specific pathological pattern of Lock contention in which threads end up serialized behind a lock even though the lock itself is held only briefly each time. The name comes from the analogy of a traffic convoy: once cars bunch up behind a slow one, they stay bunched even after the slow car is gone, because each following car spends time reacting to the one ahead rather than driving at its own natural pace.

How it forms

A common trigger is a scheduler quantum expiring while a thread holds the lock: the OS preempts the lock holder mid-critical-section, and every other thread now waiting for the lock has to wait out the rest of that time slice before the holder even resumes, let alone releases the lock. Once several threads pile up waiting, releasing the lock wakes all of them (or one, depending on the wake policy), but only one can proceed; the rest go back to waiting, and the cycle continues indefinitely because the arrival rate of new contenders keeps up with the drain rate.

thread A: acquire lock -> [preempted mid-section] -> ... -> release
threads B, C, D, ...:     pile up waiting the whole time, then re-contend on release

Once a convoy forms, it tends to be self-sustaining even after the original trigger (the preemption) is long past, because the queue of waiters never fully drains before new arrivals refill it. This is what distinguishes a convoy from ordinary contention: ordinary contention is proportional to actual demand for the lock, while a convoy is a feedback loop that persists on its own.

Why it's worse than plain contention

Ordinary Lock contention scales with how often threads genuinely need the critical section. A convoy can persist even after the demand that triggered it has passed, because the queue of waiters never fully drains before the next batch arrives to refill it. This makes convoys much harder to diagnose from a profiler snapshot: the lock looks contended, but the contention doesn't correlate cleanly with the workload's actual need for the critical section, since it's the scheduling interaction around the lock, not the lock's intrinsic hold time, driving the slowdown.

Mitigation

Convoys are usually addressed indirectly rather than by attacking the queueing behavior directly: shrinking the critical section (less time to be preempted inside), avoiding preemption during the critical section where the platform allows it (real-time priority, or disabling preemption briefly in kernel code), or moving to a lock-free structure so there is no lock to convoy behind at all.