Session 3.5: Deadlock Prevention and Avoidance
Strategies to prevent deadlock occurrence and algorithms to avoid unsafe states
Learning Objectives
By the end of this session, you will be able to:
- Understand deadlock prevention by eliminating necessary conditions
- Distinguish between prevention and avoidance approaches
- Define safe and unsafe states in resource allocation
- Implement and analyze the Banker's algorithm
- Apply resource request algorithms for deadlock avoidance
- Compare trade-offs between different approaches
Key Concept
Prevention eliminates the possibility of deadlock by ensuring at least one necessary condition cannot hold. Avoidance allows all conditions but uses algorithms to ensure the system never enters an unsafe state.
Prevention Approach
Eliminate one or more of the four necessary conditions for deadlock. Conservative but may reduce system efficiency.
Avoidance Approach
Allow all necessary conditions but use algorithms to ensure safe resource allocation. More flexible but requires advance knowledge.
Deadlock Prevention Strategies
Prevention works by ensuring that at least one of the four necessary conditions for deadlock cannot occur in the system.
1. Eliminating Mutual Exclusion
Make Resources Shareable
Possible for:
- Read-only files
- Shared memory (read-only)
- Some network resources
Not possible for:
- Printers, scanners
- Write access to files
- Critical sections
Example: Spooling System - Instead of direct printer access - Jobs written to disk spool - Printer daemon processes queue - Eliminates mutual exclusion for printer
2. Eliminating Hold and Wait
Require All Resources at Once
Protocol 1: All-or-Nothing Allocation - Process must request all resources before execution - If any resource unavailable, wait without holding any - Only start execution when all resources granted Example: Process needs: Printer, Scanner, File A, File B - Request all four resources simultaneously - If any unavailable, release all and wait - Proceed only when all four are available Advantages: + Eliminates hold-and-wait condition + Simple to implement Disadvantages: - Low resource utilization - Starvation possible - May not know all resources in advance
Release Before Requesting
Protocol 2: Release and Re-request - Process can hold resources initially - To request new resource, must release all current resources - Request all needed resources (old + new) - Re-acquire in single atomic operation Example: Process holds: Printer, File A Needs additional: Scanner 1. Release Printer and File A 2. Request Printer, File A, and Scanner together 3. Wait until all three available 4. Proceed with all resources Advantages: + Eliminates hold-and-wait + More flexible than Protocol 1 Disadvantages: - Resource preemption overhead - Possible data loss if resources released - Still low utilization
3. Eliminating No Preemption
Allow Resource Preemption
Preemption Protocols: Protocol 1: Preempt Requesting Process - If process P1 requests resource held by P2 - If P2 is waiting for other resources - Preempt all resources from P2 - Give requested resource to P1 - P2 restarts when all its resources available Protocol 2: Preempt Based on Priority - Higher priority process can preempt lower priority - Preempted process must restart - Can lead to starvation of low-priority processes Suitable for: ✓ CPU (context switching) ✓ Memory (swapping/paging) ✓ Registers (save/restore) Not suitable for: ✗ Printers (cannot undo printing) ✗ Mutex locks (data corruption) ✗ Database transactions (consistency)
4. Eliminating Circular Wait
Resource Ordering
Ordered Resource Allocation: 1. Assign unique number to each resource type R1 = Printer (1) R2 = Scanner (2) R3 = File System (3) R4 = Network (4) 2. Processes must request resources in increasing order - Can request R1, then R2, then R3 - Cannot request R1 after requesting R2 3. If need lower-numbered resource, must release higher ones Example Implementation: Process P1: Needs Printer(1) and Network(4) - Request Printer(1) first ✓ - Then request Network(4) ✓ - Order: 1 → 4 (increasing) ✓ Process P2: Needs Network(4) and Scanner(2) - Request Scanner(2) first ✓ - Then request Network(4) ✓ - Order: 2 → 4 (increasing) ✓ Proof: No circular wait possible - If Pi waits for Pj, then Pi requested higher-numbered resource - Pj cannot wait for Pi (would violate ordering) - No cycle can form
Deadlock Avoidance Concept
Deadlock avoidance allows all four necessary conditions to exist but uses algorithms to ensure the system never enters a state where deadlock is possible.
Key Requirements for Avoidance
- Advance Information: Must know maximum resource needs of each process
- Dynamic Allocation: Resources allocated dynamically based on current state
- Safe State Maintenance: System must always remain in a safe state
- Conservative Approach: May deny requests even when resources are available
Resource Allocation State
The resource allocation state is defined by:
- Available: Number of available instances of each resource type
- Allocation: Number of instances currently allocated to each process
- Max: Maximum number of instances each process may need
- Need: Remaining resource needs (Need = Max - Allocation)
Example System State
System: 3 resource types (A, B, C)
Total instances: A=10, B=5, C=7
Current state:
Process | Allocation | Max | Need
| A B C | A B C | A B C
--------|-----------|-------|-------
P0 | 0 1 0 | 7 5 3 | 7 4 3
P1 | 2 0 0 | 3 2 2 | 1 2 2
P2 | 3 0 2 | 9 0 2 | 6 0 0
P3 | 2 1 1 | 2 2 2 | 0 1 1
P4 | 0 0 2 | 4 3 3 | 4 3 1
Available: A=3, B=3, C=2
Question: Is this a safe state?
Answer: Need to check if there exists a safe sequence...
Safe vs Unsafe States
Safe State
A state is safe if there exists a safe sequence of processes such that all processes can complete their execution.
Safe Sequence Properties:
- For each Pi, resources needed ≤ available + allocated to Pj (j < i)
- Pi can wait until all Pj (j < i) finish
- When Pj finishes, Pi gets needed resources
- Pi completes and releases all resources
Unsafe State
A state is unsafe if no safe sequence exists. This doesn't guarantee deadlock, but deadlock is possible.
Unsafe State Characteristics:
- No process can complete with available resources
- All processes need more than currently available
- System may enter deadlock if processes request maximum
- Avoidance algorithms prevent entering unsafe states
Safe Sequence Example
Finding Safe Sequence for Previous Example
Available: A=3, B=3, C=2 Step 1: Check P1 (Need: 1,2,2) - Need ≤ Available? (1,2,2) ≤ (3,3,2) ✓ - P1 can finish, releases (2,0,0) - New Available: (3,3,2) + (2,0,0) = (5,3,2) Step 2: Check P3 (Need: 0,1,1) - Need ≤ Available? (0,1,1) ≤ (5,3,2) ✓ - P3 can finish, releases (2,1,1) - New Available: (5,3,2) + (2,1,1) = (7,4,3) Step 3: Check P4 (Need: 4,3,1) - Need ≤ Available? (4,3,1) ≤ (7,4,3) ✓ - P4 can finish, releases (0,0,2) - New Available: (7,4,3) + (0,0,2) = (7,4,5) Step 4: Check P2 (Need: 6,0,0) - Need ≤ Available? (6,0,0) ≤ (7,4,5) ✓ - P2 can finish, releases (3,0,2) - New Available: (7,4,5) + (3,0,2) = (10,4,7) Step 5: Check P0 (Need: 7,4,3) - Need ≤ Available? (7,4,3) ≤ (10,4,7) ✓ - P0 can finish Safe sequence found: P1 → P3 → P4 → P2 → P0 Therefore, the state is SAFE!
Banker's Algorithm
The Banker's algorithm is a deadlock avoidance algorithm that ensures the system always remains in a safe state by checking resource allocation requests before granting them.
Algorithm Concept
Named after the banking system where a banker ensures they can satisfy all customers' credit needs. The algorithm simulates resource allocation to check if the resulting state would be safe.
Data Structures
Let n = number of processes, m = number of resource types
Available[m]: Available instances of each resource type
Max[n][m]: Maximum demand of each process for each resource type
Allocation[n][m]: Currently allocated instances to each process
Need[n][m]: Remaining need of each process
Need[i][j] = Max[i][j] - Allocation[i][j]
Work[m]: Working copy of Available (used in algorithm)
Finish[n]: Boolean array to track completed processes
Safety Algorithm
Step-by-Step Safety Check
Algorithm: Check if current state is safe
1. Initialize:
Work = Available
Finish[i] = false for all i
2. Find process Pi such that:
a) Finish[i] == false
b) Need[i] ≤ Work
If no such Pi exists, go to step 4
3. Simulate Pi completion:
Work = Work + Allocation[i]
Finish[i] = true
Go to step 2
4. Check result:
If Finish[i] == true for all i:
System is in safe state
Else:
System is in unsafe state
Time Complexity: O(m × n²)
Space Complexity: O(m + n)
Banker's Algorithm Implementation
function bankers_algorithm(processes, resources, available, max, allocation):
n = len(processes) # number of processes
m = len(resources) # number of resource types
# Calculate need matrix
need = [[max[i][j] - allocation[i][j] for j in range(m)] for i in range(n)]
# Check if current state is safe
def is_safe():
work = available[:]
finish = [False] * n
safe_sequence = []
for _ in range(n):
found = False
for i in range(n):
if not finish[i] and all(need[i][j] <= work[j] for j in range(m)):
# Process i can finish
for j in range(m):
work[j] += allocation[i][j]
finish[i] = True
safe_sequence.append(i)
found = True
break
if not found:
return False, []
return True, safe_sequence
return is_safe()
# Example usage:
processes = ['P0', 'P1', 'P2', 'P3', 'P4']
resources = ['A', 'B', 'C']
available = [3, 3, 2]
max = [[7,5,3], [3,2,2], [9,0,2], [2,2,2], [4,3,3]]
allocation = [[0,1,0], [2,0,0], [3,0,2], [2,1,1], [0,0,2]]
safe, sequence = bankers_algorithm(processes, resources, available, max, allocation)
print(f"Safe: {safe}, Sequence: {sequence}")
Resource Request Algorithm
When a process requests resources, the system must decide whether to grant the request immediately or make the process wait to avoid entering an unsafe state.
Request Processing Steps
Algorithm for Processing Resource Request
Let Request[i] be the request vector for process Pi
Request[i][j] = k means Pi wants k instances of resource type Rj
Step 1: Validity Check
If Request[i] ≤ Need[i]:
Continue to step 2
Else:
Raise error (process exceeded maximum claim)
Step 2: Availability Check
If Request[i] ≤ Available:
Continue to step 3
Else:
Pi must wait (insufficient resources)
Step 3: Simulate Allocation
Temporarily modify state:
Available = Available - Request[i]
Allocation[i] = Allocation[i] + Request[i]
Need[i] = Need[i] - Request[i]
Step 4: Safety Check
Run safety algorithm on new state:
If safe:
Grant request (make changes permanent)
Else:
Deny request (restore original state)
Pi must wait
Example: Resource Request Processing
Current State:
Process | Allocation | Need | Max
| A B C | A B C | A B C
--------|-----------|----------|--------
P0 | 0 1 0 | 7 4 3 | 7 5 3
P1 | 2 0 0 | 1 2 2 | 3 2 2
P2 | 3 0 2 | 6 0 0 | 9 0 2
P3 | 2 1 1 | 0 1 1 | 2 2 2
P4 | 0 0 2 | 4 3 1 | 4 3 3
Available: A=3, B=3, C=2
Request: P1 requests (1,0,2)
Step 1: Check Request ≤ Need
(1,0,2) ≤ (1,2,2) ✓ Valid
Step 2: Check Request ≤ Available
(1,0,2) ≤ (3,3,2) ✓ Sufficient resources
Step 3: Simulate allocation
New Available: (3,3,2) - (1,0,2) = (2,3,0)
New Allocation[P1]: (2,0,0) + (1,0,2) = (3,0,2)
New Need[P1]: (1,2,2) - (1,0,2) = (0,2,0)
Step 4: Check if new state is safe
Try to find safe sequence with Available=(2,3,0)...
- P1 needs (0,2,0) ≤ (2,3,0) ✓ Can finish
- After P1: Available = (2,3,0) + (3,0,2) = (5,3,2)
- Continue checking other processes...
If safe sequence found: Grant request
If no safe sequence: Deny request, restore original state
Implementation Example
def process_request(process_id, request, available, allocation, need, max_claim):
n = len(allocation)
m = len(available)
# Step 1: Check if request is valid
if not all(request[j] <= need[process_id][j] for j in range(m)):
return False, "Request exceeds maximum claim"
# Step 2: Check if resources are available
if not all(request[j] <= available[j] for j in range(m)):
return False, "Insufficient resources available"
# Step 3: Simulate allocation
# Save current state
old_available = available[:]
old_allocation = [row[:] for row in allocation]
old_need = [row[:] for row in need]
# Modify state
for j in range(m):
available[j] -= request[j]
allocation[process_id][j] += request[j]
need[process_id][j] -= request[j]
# Step 4: Check safety
safe, sequence = is_safe_state(available, allocation, need)
if safe:
return True, f"Request granted. Safe sequence: {sequence}"
else:
# Restore original state
available[:] = old_available
allocation[:] = old_allocation
need[:] = old_need
return False, "Request denied - would lead to unsafe state"
Prevention vs Avoidance Comparison
Prevention Approach
Advantages:
- Simple to implement
- No need for advance information
- Guarantees no deadlock
- Works with any resource allocation pattern
Disadvantages:
- Low resource utilization
- Reduced system throughput
- May cause starvation
- Overly conservative
Avoidance Approach
Advantages:
- Better resource utilization
- Higher system throughput
- More flexible allocation
- Allows all necessary conditions
Disadvantages:
- Requires advance knowledge of maximum needs
- Complex algorithms (O(m×n²))
- Runtime overhead for safety checks
- Fixed number of processes and resources
Practical Considerations
Real-World Application
Prevention is suitable for:
- Simple embedded systems
- Real-time systems with predictable patterns
- Systems where safety is critical
- Resource-constrained environments
Avoidance is suitable for:
- Batch processing systems
- Systems with known workloads
- High-performance computing
- Database management systems
Performance Comparison
Resource Utilization
- Prevention: 40-60%
- Avoidance: 70-85%
- Detection: 85-95%
Algorithm Complexity
- Prevention: O(1)
- Avoidance: O(m×n²)
- Detection: O(m×n²)
Information Required
- Prevention: None
- Avoidance: Maximum needs
- Detection: Current state
Hybrid Approaches
Combining Strategies
Modern systems often combine multiple approaches:
- Layered approach: Prevention for critical resources, avoidance for others
- Resource-specific: Different strategies for different resource types
- Adaptive systems: Switch strategies based on system load
- Timeout mechanisms: Combine with detection and recovery
Session Summary
Key Takeaways:
- Prevention Strategies: Eliminate one of the four necessary conditions
- Resource Ordering: Most practical prevention method for circular wait
- Safe States: States where all processes can complete execution
- Banker's Algorithm: Classic avoidance algorithm ensuring safe states
- Request Processing: Dynamic safety checking before resource allocation
- Trade-offs: Prevention is simple but inefficient; avoidance is complex but flexible
Study Tips:
- Practice Banker's algorithm with different scenarios
- Understand safe vs unsafe state differences
- Work through resource request examples
- Compare prevention method trade-offs
Prevention Methods Summary:
- Mutual Exclusion: Spooling, read-only sharing
- Hold and Wait: All-or-nothing, release-and-request
- No Preemption: Forced preemption, priority-based
- Circular Wait: Resource ordering (most practical)
Avoidance Components:
- State Information: Available, Allocation, Max, Need
- Safety Algorithm: Check for safe sequences
- Request Algorithm: Simulate and verify safety
- Complexity: O(m×n²) time, O(m+n) space
Next Session:
Session 3.6: Deadlock Detection and Recovery
Algorithms for detecting deadlock occurrence and methods for system recovery