Search code examples
c++pthreadsmutexdeadlock

Applying two or more mutexes to a section of code


I'm studying mutexes and deadlocks in c++ pthreads from http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html

I understood them but i stacked at a point. I didn't understand what is the problem of the below code. Can you explain it with simple and clear words please? I quoted from this site:

Mutex Deadlock: ... Be careful when applying two or more mutexes to a section of code. If the first pthread_mutex_lock is applied and the second pthread_mutex_lock fails due to another thread applying a mutex, the first mutex may eventually lock all other threads from accessing data including the thread which holds the second mutex. The threads may wait indefinitely for the resource to become free causing a deadlock. It is best to test and if failure occurs, free the resources and stall before retrying.

01  ...
02  pthread_mutex_lock(&mutex_1);
03  while ( pthread_mutex_trylock(&mutex_2) )  /* Test if already locked   */
04  {
05     pthread_mutex_unlock(&mutex_1);  /* Free resource to avoid deadlock */
06         ...
07     /* stall here   */
08     ...
09     pthread_mutex_lock(&mutex_1);
10  }
11  count++;
12  pthread_mutex_unlock(&mutex_1);
13  pthread_mutex_unlock(&mutex_2);
14  ...

Thanks in advance.


Solution

  • Essentially if you need two mutexes to perform a task, mutex_1 and mutex_2, if thread_A grabs mutex_1 and thread_B grabs mutex_2, they are stuck in a deadlock because they are both waiting for the other mutex to become available, but they never will because the other is waiting too.

    That is why you typically enforce an order that mutexes can be acquired. For example, you must acquire mutex_1 before attempting to acquire mutex_2. That way you can't have mutex_2 without first having mutex_1, so there is no chance to cause a deadlock.

         Thread_A                Thread_B
            |                       |
            |                       |
       lock(mutex_1)                |            // Thread_A got mutex_1
            |                       |
            |                  lock(mutex_2)     // Thread_B got mutex_2
            |                       |
            |                       |
      trylock(mutex_2)              |            // Thread_A wants mutex_2
            |                 trylock(mutex_1)   // Thread_B wants mutex_1
            |                       |
    

    Due to the timing, these threads are now at a deadlock. Both are waiting for a mutex that they will never get, because the other thread won't unlock the mutex it is holding until it finishes its work.

    In your code example, note that the first thing they do is lock mutex_1. They then spin on mutex_2 until that comes available. This is enforcing the order mutex_1 then mutex_2.