Deadlock
When two or more processes each wait for something another holds, so none can ever proceed, and the four conditions that must all be true for it to happen.
A deadlock occurs when two or more processes are each waiting for a resource another one holds, so none of them can ever proceed. Thread 1 holds lock A and waits for lock B. Thread 2 holds lock B and waits for lock A. Neither will let go, so both wait forever. Unlike a crash, a deadlock often produces no error, just a program that stops responding.
Edward Coffman and colleagues identified in 1971 four conditions that must all hold for a deadlock to occur:
- Mutual exclusion. A resource can be held by only one process at a time, as with a mutex (mutual exclusion lock).
- Hold and wait. A process holds one resource while waiting for another.
- No preemption. Resources can't be taken away, only released voluntarily.
- Circular wait. A cycle of processes each waits for the next.
Breaking any one prevents deadlock. The most common practical fix targets circular wait with lock ordering: every thread acquires locks in the same global order, so no cycle can form. Others include acquiring all needed locks at once, using timeouts or try-lock so a thread backs off instead of waiting forever, and avoiding shared locks altogether through message passing, as in the Actor Model, or Immutability.
Edsger Dijkstra's dining philosophers problem (1965) is the classic illustration: five philosophers around a table share five forks, each needs two to eat, and if each picks up the left fork first, all wait forever for the right. Numbering the forks and always taking the lower-numbered one first breaks the cycle.
Related failures:
- Livelock. Processes keep reacting to each other without making progress, like two people repeatedly stepping aside in the same direction in a hallway.
- Starvation. A process waits indefinitely because others keep getting the resource first. See Resource Starvation.
- Priority inversion. A high-priority task waits on a lock held by a low-priority one, which is itself preempted by medium-priority work. NASA's Mars Pathfinder kept resetting in 1997 for this reason, until engineers enabled priority inheritance remotely. It is a hazard for any CPU Scheduler.
Databases deal with deadlocks constantly under Pessimistic Locking. They detect them by finding cycles in a graph of which transaction waits for which, the same check a topological sort performs, then abort one transaction as a victim so the rest proceed. Optimistic Locking avoids waiting altogether and retries on conflict. A subtler version is pool exhaustion: if every request holds one connection from a Connection Pool while waiting for a second, the pool runs dry and everything stalls.
Deadlocks, like a Race Condition, depend on timing and are hard to reproduce, a classic Heisenbug. Deterministic Simulation Testing and lock-order checkers help find them before production does.
See also2
Thread Pool
A fixed set of threads kept alive to run many short tasks from a queue, avoiding the cost of creating a thread per task and capping concurrency, with sizing rules and failure modes that shape how servers and runtimes behave under load.
Systems & Tooling10 connections
Lock-Free Data Structure
Concurrent data structures built on atomic hardware instructions instead of locks, so a stalled thread can never block the others, at the price of some of the hardest code in computing.
Computation & Algorithms7 connections
Linked from6
- Core DumpSystems & Tooling
A snapshot of a crashed program's memory saved for later inspection, how to get one, how to read one, and why it can leak secrets.
- FutexSystems & Tooling
The Linux primitive that makes locks cheap by handling the uncontended case entirely in user space and asking the kernel for help only when a thread actually has to wait, the foundation under most thread libraries.
- Lock-Free Data StructureComputation & Algorithms
Concurrent data structures built on atomic hardware instructions instead of locks, so a stalled thread can never block the others, at the price of some of the hardest code in computing.
- Pessimistic LockingData & Content
Blocking every other writer up front for the duration of a transaction, betting that collisions are common enough to prevent rather than clean up.
- Thread PoolSystems & Tooling
A fixed set of threads kept alive to run many short tasks from a queue, avoiding the cost of creating a thread per task and capping concurrency, with sizing rules and failure modes that shape how servers and runtimes behave under load.
- UpsertData & Content
Inserting a row if it doesn't exist and updating it if it does, in one atomic statement, which is harder than it sounds because the obvious check-then-insert version races with itself.