c++c++11stdconditional-variable

Showing the unlock from std::condition_variable::wait


I've read from https://en.cppreference.com/w/cpp/thread/condition_variable/wait that wait() "Atomically unlocks lock". How do I see this via std::cout? I am trying to understand conditional variables better on what they're actually doing. I've wrote an attempt below.

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

using namespace std;

condition_variable cv;
mutex m;
bool stopped = false;

void f1() {
    unique_lock<mutex> ul{m};
    cout << "f1: " << ul.owns_lock() << endl;
    cv.wait(ul, [&]{
        cout << "f1: " << ul.owns_lock() << endl;
        return stopped;
    });
    cout << "f1 RUNNING\n";
    cout << "f1: " << ul.owns_lock() << endl;
}


void f2() {
    lock_guard<mutex> lg{m};
    cout << "f2 RUNNING\n";
}

int main() {
    unique_lock<mutex> ul{m};
    thread t1(&f1);
    thread t2(&f2);

    cout << ul.owns_lock() << endl;
    this_thread::sleep_for(chrono::seconds(1));
    stopped = true;
    cv.notify_one();
    cout << ul.owns_lock() << endl;
    ul.unlock();
    cout << ul.owns_lock() << endl;
    this_thread::sleep_for(chrono::seconds(1));

    t1.join();
    t2.join();
    return 0;
}

Solution

  • std::unique_lock and std::lock_guard work with any class type that satisfies the requirements of BasicLockable. So, just write your own class that wraps a std::mutex, then you can add whatever logging you want.

    UPDATE: However, std:condition_variable only works with std::mutex specifically, so if you write your own mutex wrapper class then you will have to use std::condition_variable_any instead.

    For example:

    #include <chrono>
    #include <condition_variable>
    #include <iostream>
    #include <mutex>
    #include <thread>
    
    using namespace std;
    
    struct LoggingMutex
    {
        mutex m;
    
        void lock() {
            cout << "Locking" << endl;
            m.lock();
            cout << "Locked" << endl;
        }
    
        bool try_lock() {
            cout << "Attempting to lock" << endl;
            bool result = m.try_lock();
            cout << (result ? "Locked" : "Not locked") << endl;
            return result;
        }
    
        void unlock() {
            cout << "Unlocking" << endl;
            m.unlock()
            cout << "Unlocked" << endl;
        }
    };
    
    condition_variable_any cv;
    LoggingMutex lm;
    bool stopped = false;
    
    void f1() {
        unique_lock<LoggingMutex> ul{lm};
        cout << "f1: " << ul.owns_lock() << endl;
        cv.wait(ul, [&]{
            cout << "f1: " << ul.owns_lock() << endl;
            return stopped;
        });
        cout << "f1 RUNNING\n";
        cout << "f1: " << ul.owns_lock() << endl;
    }
    
    
    void f2() {
        lock_guard<LoggingMutex> lg{lm};
        cout << "f2 RUNNING\n";
    }
    
    int main() {
        unique_lock<LoggingMutex> ul{lm};
        thread t1(&f1);
        thread t2(&f2);
    
        cout << ul.owns_lock() << endl;
        this_thread::sleep_for(chrono::seconds(1));
        stopped = true;
        cv.notify_one();
        cout << ul.owns_lock() << endl;
        ul.unlock();
        cout << ul.owns_lock() << endl;
        this_thread::sleep_for(chrono::seconds(1));
    
        t1.join();
        t2.join();
        return 0;
    }