Sobes.tech
Middle

What is the minimum number of mutexes required to cause a deadlock?

sobes.tech AI

Answer from AI

The minimum number of mutexes for a deadlock to occur is two.

A deadlock can occur when four conditions (Coffman conditions) are met:

  1. Mutual Exclusion: Resources must be non-shareable. In this case, mutexes provide this.
  2. Hold and Wait: A process holding one mutex attempts to acquire another mutex that is held by a different process.
  3. No Preemption: A mutex that is already acquired cannot be forcibly taken away from a process; it can only be released by the process holding it.
  4. Circular Wait: There exists a chain of processes where each process is waiting for a mutex held by the next process in the chain, and the last process is waiting for a mutex held by the first process.

Example with two mutexes:

#include <mutex>
#include <thread>
#include <iostream>

std::mutex mtx1;
std::mutex mtx2;

void thread1_func() {
    mtx1.lock(); // Thread 1 locks mtx1
    std::cout << "Thread 1 acquired mtx1" << std::endl;
    // Artificial delay to increase the likelihood of deadlock
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    mtx2.lock(); // Thread 1 attempts to lock mtx2 (possibly already held by Thread 2)
    std::cout << "Thread 1 acquired mtx2" << std::endl;

    mtx2.unlock();
    mtx1.unlock();
}

void thread2_func() {
    mtx2.lock(); // Thread 2 locks mtx2
    std::cout << "Thread 2 acquired mtx2" << std::endl;
    // Artificial delay to increase the likelihood of deadlock
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    mtx1.lock(); // Thread 2 attempts to lock mtx1 (possibly already held by Thread 1)
    std::cout << "Thread 2 acquired mtx1" << std::endl;

    mtx1.unlock();
    mtx2.unlock();
}

int main() {
    std::thread t1(thread1_func);
    std::thread t2(thread2_func);

    t1.join();
    t2.join();

    return 0;
}

In this example, if Thread 1 acquires mtx1 and Thread 2 acquires mtx2 almost simultaneously, then Thread 1 will wait for mtx2 (which is held by Thread 2), and Thread 2 will wait for mtx1 (which is held by Thread 1). This creates a circular wait between the two threads and two mutexes.