Allanqunzi
Allanqunzi

Reputation: 3260

why lock_guard can get an already locked mutex by unique_lock?

I am reading the example code of using condition_variable here. I post the code below:

std::mutex m;
std::condition_variable cv;
std::string data;
bool ready = false;
bool processed = false;

void worker_thread()
{
    // Wait until main() sends data
    std::cout << "------------------------\n";
    std::unique_lock<std::mutex> lk(m);
    cv.wait(lk, []{return ready;});

    // after the wait, we own the lock.
    std::cout << "Worker thread is processing data\n";
    data += " after processing";

    // Send data back to main()
    processed = true;
    std::cout << "Worker thread signals data processing completed\n";

    // Manual unlocking is done before notifying, to avoid waking up
    // the waiting thread only to block again (see notify_one for details)
    lk.unlock();
    cv.notify_one();
}

int main()
{
    std::thread worker(worker_thread);

    data = "Example data";
    // send data to the worker thread
    {
        std::lock_guard<std::mutex> lk(m);
        ready = true;
        std::cout << "main() signals data ready for processing\n";
    }
    cv.notify_one();

    // wait for the worker
    {
        std::unique_lock<std::mutex> lk(m);
        cv.wait(lk, []{return processed;});
    }
    std::cout << "Back in main(), data = " << data << '\n';

    worker.join();

    return 0;
}

My question is the worker_thread is launched first, so I would assume the mutex m is locked by the worker_thread, but why in the main the mutex m still can be locked by lock_guard?

Upvotes: 2

Views: 1181

Answers (1)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275200

A condition variable is only one part of a tripod.

The three parts are the condition variable, state and the mutex that guards the state.

The condition variable provides a mechanism to notify when the state changes.

This operation uses all 3:

cv.wait(lk, []{return ready;})

The condition variable's method takes a lock (which must have been acquired), and a lambda (which tests the state).

Within the wait method, the lk is unlocked until the condition variable detects a message (which could be spurious). When it detects a message, it relocks the mutex and runs the test (whose goal is to determine if the detection was spurious). If the test fails, it unlocks and waits again: if the test passes, it keeps the lock locked and exits.

There is also the "the test threw" path, which results in a different lock state depending on the version of the standard your code implemented (C++11 had a defect, IIRC).

The important thing you missed is that wait unlocks the mutex passed in.

Upvotes: 8

Related Questions