Search code examples
c++multithreadinglockingdeadlockconditional-variable

Can a unique_lock and a normal lock cause together a dead-lock?


I'm using unique_locks and normal lock to lead the threads. I try to interlink the threads, so they don't loose too many cycles and work together and not separately. I don't know, if my code can create a dead-lock:

#include <iostream>

#include <thread>
#include <mutex>
#include <chrono>
#include <condition_variable>

std::mutex mutex1;
std::mutex mutex2;

std::condition_variable cv;
int buffer;

bool notify;

void task(std::string threadName, int loopmax) {

    for(int i = 0; i < loopmax; i++) {

        mutex1.lock();//normal lock for both threads
        buffer++;
        std::cout << threadName << buffer << "\n";
        mutex1.unlock();//normal unlock for both threads
    }
}

int main() {

    std::thread thr1([&] {

        std::unique_lock<std::mutex> lock(mutex2);//unique_lock in working_thread

        for(int j = 0; j < 3; j++) {

            task("working_thread: ", 50);
        }

        notify = true;
        std::cout << "worker complete\n";

        cv.notify_one();
    });
    
    task("Main_thread: ", 50);//can I do the task-functions here?
    task("Main_thread: ", 50);

    std::unique_lock<std::mutex> lock(mutex2);//unique_lock in main_thread

    //if I use the task-function here, the threads don't interlink anymore

    if(!notify) {

        cv.wait(lock);
    }

    printf("Hallo\n");

    thr1.join();

    return 0;
}

I always got a good interlinked output of both threads this way. Could it create a dead-lock, if the working_thread locks its unique_lock and and the main_thread locks mutex1 in the task-function ? Could it also create a dead-lock somewhere else?


Solution

  • As from Here:

    A deadlock requires four conditions: mutual exclusion, hold and wait, no preemption, and circular wait.

    Thanks to @463035818_is_not_an_ai, Wikipedia has a better and more detailed description to the four conditions.

    It does not matter matter if you use a unique lock or normal lock, any waiting operation which fulfills the four conditions can cause a deadlock.

    The current code does not fulfill the deadlock conditions
    As @Mestkon has pointed out in the comments, in your code every thread currently uses only one mutex, thus it is impossible to fulfil the "hold and wait" condition. Thus no deadlock can happen.

    Define a locking sequence
    A usually simple practical approach is to define a locking sequence and use it everywhere.
    For example if you ever need mutex1 and mutex2 at the same time, make sure to always lock mutex1 first, then mutex2 second (or always the other way around).
    By that you can easily prevent the "circular wait" (mutex1 waiting for mutex2 and mutex2 waiting for mutex1) condition, thus no deadlock can happen.