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;
}
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;
}