How to use std::condition_variable with my own mutex wrapper

Viewed 214

I wanted to demonstrate / log some std::mutex behaviour. I would like to log every call to lock() and unlock() (regardless whether they succeed or have to block). Originally, since those methods are not virtual, I tried to wrap the standard std::mutex in the following way, instead of using inheritance:

struct mutex_wrapper {
    std::mutex mut = std::mutex();

    void lock() {
        std::cout << "LOCKING MUTEX\n";
        mut.lock();
    }

    void unlock() {
        std::cout << "UNLOCKING MUTEX\n";
        mut.unlock();
    }
};

Ignore the fact that calls to std::cout << could produce mangled output when called within multiple threads.

Then I wanted to inspect the output of a simple std::condition_variable example, as such:

int main() {
    auto ready = false;
    auto mutex = mutex_wrapper();
    auto cvar = std::condition_variable();

    auto t = std::thread([&mutex, &cvar, &ready] {
        std::unique_lock lock(mutex);
        cvar.wait(lock, [&ready] { return ready; });
        std::cout << "woke up\n";
    });

    std::this_thread::sleep_for(std::chrono::seconds(2));
    {
        std::lock_guard lock(mutex);
        ready = true;
    }
    std::cout << "notifying\n";
    cvar.notify_one();

    t.join();
}

The problem is that it doesn't compile because, apparently, wait() requires that the corresponding std::unique_lock is instantiated with the regular std::mutex while I tried to use it with my own mutex_wrapper.

Is there any way to log the internal usage of locking and unlocking of a standard mutex? Or is there a way to make std::condition_variable work with wrappers?

1 Answers

std::condition_variable only works specifically with std::unique_lock<std::mutex> -- so you'll probably want to use std::condition_variable_any. This is a generalization of std::condition_variable that can work with any BasicLockable type, such as your mutex wrapper.

Note that it may be a slight pessimization over using std::condition_variable/std::mutex directly since usually these are tightly coupled in the implementation for better performance. But since you're logging here anyway, I suspect this won't be that much of an issue overall.

An alternative approach is to write your own custom condition_variable wrapper that operates directly in terms of your wrapped mutex type and an underlying std::condition_variable -- and extract the std::mutex directly when locking.

Related