warning Critical OS Concept

What is a Deadlock?

In technical terms, a deadlock is a situation in concurrent computing where two or more processes are blocked forever because they are waiting for each other to release a resource.

Imagine Process A holds Key 1 and needs Key 2, while Process B holds Key 2 and needs Key 1. Neither can proceed.

Deadlock Animation
Source: Wikimedia Commons

The "Real Life" Analogy

The best way to explain this to anyone (even non-techies) is the Traffic Intersection.

Real Traffic Gridlock
Real World Gridlock

traffic The Gridlock

Imagine a four-way intersection with four cars.

  • check_circle Each car occupies one lane (Holding a resource).
  • check_circle Each car wants to move forward into the next lane (Waiting for a resource).
  • check_circle No car can reverse (No preemption).

Result: No car can move. The traffic is deadlocked forever unless an external force (police/tow truck) intervenes.

The 4 Coffman Conditions

For a deadlock to occur, ALL FOUR of these conditions must be true simultaneously. If you break even one, the deadlock is resolved.

vpn_key
Condition 01

Mutual Exclusion

Resources cannot be shared. Only one process can use a resource at a time.

Example: A printer. Only one person can print at a time.
pan_tool
Condition 02

Hold and Wait

A process holding at least one resource is waiting to acquire additional resources held by others.

Example: I have the cheese, waiting for bread. You have bread, waiting for cheese.
lock_clock
Condition 03

No Preemption

A resource cannot be taken from a process forcibly. It must be released voluntarily.

Example: I cannot snatch the phone from your hand; I must wait for you to hang up.
sync_problem
Condition 04

Circular Wait

A set of processes are waiting for each other in a circular chain.

Example: A waits for B. B waits for C. C waits for A.

Handling Deadlocks

How do Operating Systems deal with this mess?

Break one of the 4 conditions!

If we can guarantee that at least one of the conditions cannot hold, deadlock is impossible.

  • check_circle
    Eliminate Mutual Exclusion: Make resources shareable (e.g., Read-only files).

    Challenge: Some resources like printers cannot be shared.

  • check_circle
    Eliminate Hold and Wait: Process must request all resources at start.

    Challenge: Low resource utilization; starvation possible.

  • check_circle
    Eliminate Circular Wait: Order resources numerically. Can only request higher #.

    Commonly used in practical systems.

The Banker's Algorithm

The OS requires additional information about which resources a process will need. Before granting a request, the OS checks: "Will granting this put me in an Unsafe State?"

Safe State

A state is safe if the system can allocate resources to each process in some order and still avoid a deadlock.

Request Allowed

If system remains in SAFE state.

Request Denied (Wait)

If system would enter UNSAFE state.

Let it break, then fix it.

1. Detection

The OS periodically runs an algorithm (like looking for cycles in a Resource Allocation Graph) to see if a deadlock exists.

Check_Cycle(Graph G) -> Returns True/False
2. Recovery
  • cancel Process Termination: Kill one or all deadlocked processes.
  • history Rollback: Roll back a process to a safe checkpoint and restart.

Technical Example in C++

Here we use std::mutex to demonstrate a classic deadlock.

1

Thread 1 Logic

Locks Resource A, sleeps (to let Thread 2 run), then tries to lock Resource B.

2

Thread 2 Logic

Locks Resource B, sleeps (to let Thread 1 run), then tries to lock Resource A.

!

The Problem

Both threads end up waiting forever for the resource the other holds.

deadlock_example.cpp
#include <iostream>
#include <thread>
#include <mutex>

std::mutex mutexA;
std::mutex mutexB;

void thread1_func() {
    mutexA.lock();
    // Acquired A, waiting for B
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    
    mutexB.lock(); // BLOCKS HERE FOREVER
    
    mutexB.unlock();
    mutexA.unlock();
}

void thread2_func() {
    mutexB.lock();
    // Acquired B, waiting for A
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    
    mutexA.lock(); // BLOCKS HERE FOREVER
    
    mutexA.unlock();
    mutexB.unlock();
}

int main() {
    std::thread t1(thread1_func);
    std::thread t2(thread2_func);
    
    t1.join();
    t2.join();
    return 0;
}
Career Focus

Ace the Interview

Q: "Explain Deadlock to me."

The Answer Strategy: Start with the definition, mention the 4 conditions, and give a quick example.

"A deadlock is a situation where a set of processes are blocked because each process is holding a resource and waiting for another resource acquired by some other process.

It happens when four specific conditions are met simultaneously: Mutual Exclusion, Hold and Wait, No Preemption, and Circular Wait. A classic example is a traffic gridlock where no car can move because each is blocking the other."

Q: "How do you prevent Deadlock?"

The Answer Strategy: Focus on breaking one of the 4 conditions.

  • Attack Circular Wait: This is the most practical. Enforce a strict ordering of resource acquisition (e.g., always lock Mutex A before Mutex B).
  • Attack Hold and Wait: Request all resources at the very beginning (inefficient but safe).
  • Attack No Preemption: If a process can't get what it wants, it must release what it currently holds and try again later.