Deadlocks and Prevention
[!NOTE] This module explores the core principles of Deadlocks and Prevention, deriving solutions from first principles and hardware constraints to build world-class, production-ready expertise.
1. The Deadly Embrace
A Deadlock is a state where a set of processes are blocked because each process is holding a resource and waiting for another resource acquired by some other process.
Imagine two trains approaching each other on a single track. Neither can move forward until the other moves back. Neither moves back. They are stuck forever.
The 4 Necessary Conditions (Coffman Conditions)
A deadlock can arise if and only if all four of these conditions hold simultaneously:
- Mutual Exclusion: At least one resource must be non-shareable. (e.g., A printer or a mutex).
- Hold and Wait: A process holding at least one resource is waiting to acquire additional resources held by other processes.
- No Preemption: Resources cannot be forcibly taken from a process. They must be released voluntarily.
- Circular Wait: A set of processes
{P0, P1, ..., Pn}exists such thatP0waits forP1,P1waits forP2, …, andPnwaits forP0.
2. Interactive: The Cycle of Doom
Build a deadlock step-by-step.
- Thread A acquires Lock 1.
- Thread B acquires Lock 2.
- Thread A tries to acquire Lock 2 (and waits).
- Thread B tries to acquire Lock 1 (and waits). Result: DEADLOCK!
3. Prevention: Resource Ordering
The most practical way to prevent deadlocks is to break the Circular Wait condition. Rule: Assign a global order to all resources (e.g., L1 < L2). All threads must acquire resources in increasing order.
If T1 wants L1 and L2, it must grab L1 first. If T2 wants L1 and L2, it must also grab L1 first. Now, T2 will block on L1 while T1 holds it. T2 cannot grab L2 yet. No cycle is possible.
4. Code Example: Causing and Fixing Deadlock
// Broken: Deadlock prone
void methodA() {
synchronized(lock1) {
synchronized(lock2) { /* ... */ }
}
}
void methodB() {
synchronized(lock2) { // WRONG ORDER
synchronized(lock1) { /* ... */ }
}
}
// Fixed: Consistent Ordering
void methodB_Fixed() {
synchronized(lock1) { // ALWAYS Lock 1 First
synchronized(lock2) { /* ... */ }
}
}
package main
import "sync"
func main() {
var mu sync.Mutex
mu.Lock()
mu.Lock() // DEADLOCK!
// Go Runtime will panic:
// "fatal error: all goroutines are asleep - deadlock!"
}