2

In the code below, which I have tried to make minimially verifiable, runs fine and does what it should (print 1,2,3 in order no matter which order I pass in threads). However, if I change m1 to m2 in the line that I have commented in the function third, this code crashes with the message "terminated without an active exception". Why can't I use the same condition variable to lock on two different mutex's at the same time?

#include <functional>
#include <mutex>
#include <condition_variable>
#include <future>
#include <iostream>

void printFirst() {
    cout << "1";
}

void printSecond() {
    cout << "2";
}

void printThird() {
    cout << "3";
}
struct test {

    condition_variable c, c2;
    int count = 0;
    mutex m1,m2;

void first(function<void()> printFirst) {
    printFirst();
    count++;
    c.notify_all();
}

void second(function<void()> printSecond) {
    unique_lock<mutex> sL1(m1);
    c.wait(sL1,[&]{return count>=1;});
    printSecond();
    count+=1;
    c.notify_all();
}

void third(function<void()> printThird) {
    unique_lock<mutex> sL2(m1); //If I make m1, m2, this code crashes
    c.wait(sL2,[&]{return count>=2;});
    printThird();
}
};
int main() {
    test t;

    function<void()> printFirstN =[&](){ t.first(printFirst);};
    function<void()> printSecondN=[&](){ t.second(printSecond);};
    function<void()> printThirdN=[&](){ t.third(printThird);};
    std::thread t1(printFirstN);
    std::thread t2( printThirdN);
    std::thread t3( printSecondN);
    t1.join();
    t2.join();
    t3.join();
}
2
  • Ignoring the crash for a second, if you want 3 threads to run serially, it doesn't make sense to use 2 mutexes in the first place, only 1 mutex. Using 2 mutexes would allow 2 threads to run concurrently, defeating your serialization Commented Nov 6, 2019 at 23:25
  • @RemyLebeau Agreed. This is just a toy program designed to highlight the issue I had in a bigger piece of code :) Commented Nov 6, 2019 at 23:36

1 Answer 1

6

You can't do it because the C++ standard says you can't.

33.5.3 Class condition_variable [thread.condition.condvar]

void wait(unique_lock& lock);

Requires: lock.owns_lock() is true and lock.mutex() is locked by the calling thread, and either

(9.1) — no other thread is waiting on this condition_variable object or

(9.2) — lock.mutex() returns the same value for each of the lock arguments supplied by all concurrently waiting (via wait, wait_for, or wait_until) threads.

The second clause imposes a requirement that all execution threads must have the same mutex locked, if they are also blocking on the condition variable.

(The above is for the wait method that takes no additional parameters, the same requirement is repeated for all overloads/variations, including the one that your code uses, which takes a predicate).

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.