Deadlocks
Definition
A set of processes is deadlocked when every process in the set is waiting for an event that can only be triggered by another process in the set. No process can proceed.
Four Necessary Conditions (Coffman Conditions)
All four must hold simultaneously for deadlock to occur:
- Mutual exclusion: At least one resource is held in a non-shareable mode
- Hold and wait: A process holds resources while waiting for additional resources
- No preemption: Resources cannot be forcibly taken from a process; they must be released voluntarily
- Circular wait: A circular chain of processes exists, each waiting for a resource held by the next
Resource Allocation Graph (RAG)
A directed graph to model resource allocation state.
- Circles = processes
- Squares = resource types (dots inside = instances)
- Request edge P → R = process P is waiting for resource R
- Assignment edge R → P = resource R is assigned to process P
Cycle detection:
- If each resource type has one instance: cycle → deadlock
- If resources have multiple instances: cycle is necessary but not sufficient for deadlock
Deadlock Handling Strategies
1. Prevention (Eliminate a Coffman Condition)
Eliminate mutual exclusion: Make resources shareable (not always possible).
Eliminate hold-and-wait:
- Require process to request ALL resources before starting (low utilization, starvation possible)
- Require process to release all held resources before requesting new ones
Eliminate no preemption:
- If a process requests a resource and can’t get it, release all currently held resources and retry
- Works for resources whose state can be saved/restored (CPU registers, memory)
Eliminate circular wait:
- Impose a total ordering on resource types
- Require processes to request resources in increasing order
- Guarantees no cycle can form
2. Avoidance (Stay in Safe State)
Requires advance knowledge of maximum resource needs per process.
Safe state: A state where the OS can find a sequence to allocate resources such that every process completes.
If we’re in a safe state → no deadlock. If in unsafe state → deadlock is possible (but not certain).
Banker’s Algorithm (Dijkstra):
For each process i:
Need[i] = Max[i] - Allocation[i]
Safety Algorithm:
Work = Available
Finish[i] = false for all i
Find i such that Finish[i]=false AND Need[i] ≤ Work
Work += Allocation[i]; Finish[i] = true
Repeat until no such i
If all Finish[i] = true → safe state
Resource request: Grant only if resulting state is safe.
Limitation: Must know maximum needs in advance. Not practical for most OS use.
3. Detection and Recovery
Allow deadlock to occur, detect it, then recover.
Detection:
- Single instance per resource type: detect cycle in RAG
- Multiple instances: run a detection algorithm (similar to Banker’s safety check but with actual allocations)
Recovery options:
Process termination:
- Kill all deadlocked processes (simple, expensive)
- Kill one at a time until deadlock broken (needs re-detection after each kill)
- Priority for killing: lowest priority, most time remaining, most resources held
Resource preemption:
- Select a victim process and take its resources
- Roll back the victim to a safe state (requires checkpointing)
- Must avoid starvation (same process should not always be victim)
4. Ignorance (Ostrich Algorithm)
Pretend deadlock never happens. Reboot or let user handle it.
Used in most practical OS (Linux, Windows) because:
- Deadlocks are rare in normal usage
- Prevention/avoidance is expensive and limits performance
- Users can kill processes manually
Livelock
Processes are not blocked but keep retrying failed operations and make no progress.
Example: Two processes each step aside for the other in a corridor and keep doing so forever.
Solution: Introduce randomness in retry timing (e.g., Ethernet CSMA/CD backoff).
Starvation vs Deadlock
| Deadlock | Starvation | |
|---|---|---|
| Cause | Circular wait for resources | Indefinite low-priority waiting |
| Affected processes | All in the cycle | Low-priority processes |
| Resolution | Break the cycle | Aging (increase priority over time) |
Practical Examples
Database deadlock: Two transactions each hold a lock and wait for the other’s lock.
- Detection: Databases run periodic deadlock detection and roll back one transaction.
Mutex deadlock in code:
// Thread 1 // Thread 2
lock(A); lock(B);
lock(B); // waits lock(A); // waits
Solution: Always acquire locks in the same order.
Dining philosophers: Classic model of circular wait (see Process Synchronization notes).