Concurrency primitives

Multiple threads accessing shared resources must be coordinated to avoid deadlocks.

The idea

When threads need exclusive access to a resource (like a database row or a file), they acquire a Lock (or Mutex). But what happens if Thread A holds Lock 1 and wants Lock 2, while Thread B holds Lock 2 and wants Lock 1?

They wait forever. This is a Deadlock. We can visualize this using a Wait-For Graph: nodes are threads and locks. An edge from Thread → Lock means "waiting for". An edge from Lock → Thread means "held by". If this graph contains a Cycle, you have a deadlock!

Thread 1 and Thread 2 are about to start.

How it works (Deadlock Avoidance)

# To avoid deadlocks, threads should always acquire locks
# in the SAME globally agreed-upon order.

def safe_transfer(account_a, account_b, amount):
    # Lock the account with the smaller ID first!
    first_lock = min(account_a.id, account_b.id)
    second_lock = max(account_a.id, account_b.id)
    
    with first_lock:
        with second_lock:
            # Safely transfer money
            account_a.balance -= amount
            account_b.balance += amount
            
# By enforcing order, a cycle can never form in the Wait-For graph.