The short answer is, your code may be correct or wrong; you didn't show exactly how full
is manipulated.
Individual bits of C++ code are never thread safe. Thread safety is a relational property of code; two bits of code can be thread safe with respect to each other if they can never cause a race condition.
But one bit of code is never thread safe; saying something is thread safe is like saying something is "the same height".
The "monkey see monkey do" condition variable pattern is this:
template<class T>
class cv_bundle {
std::mutex m;
T payload;
std::condition_variable cv;
public:
explicit cv_bundle( T in ):payload(std::move(in)) {}
template<class Test, class Extract>
auto wait( Test&& test, Extract&& extract ) {
std::unique_lock<std::mutex> l(m);
cv.wait( l, [&]{ return test(payload); } );
return extract(payload);
}
template<class Setter>
void load( Setter&& setter, bool only_one = true ) {
std::unique_lock<std::mutex> l(m);
bool is_set = setter( payload );
if (!is_set) return; // nothing to notify
if (only_one)
cv.notify_one();
else
cv.notify_all();
}
};
test
takes a T& payload
and returns true if there is something to consume (ie, the wakeup is not-spurious).
extract
takes a T& payload
and returns whatever information you want from it. It should reset the payload usually.
setter
modifies the T& payload
in a way that test
will return true
. If it does so, it returns true
. If it chooses not to, it returns false
.
All 3 get called within a mutex locking access to the T payload
.
Now, you can generate variations on this, but doing so is very hard to get right. Do not, for example, assume an atomic payload means you don't have to lock a mutex.
While I bundled these 3 things together, you could use a single mutex for a pile of condition variables, or use the mutex for more than just the condition variable. The payload could be a bool, a counter, a vector of data, or something more alien; in general, it must always be protected by the mutex. If it is atomic, during some point in the open interval between the value being modified and the notification the mutex must be locked, or you risk losing the notification.
Manual loop control instead of passing in the lambda is a modification, but describing what kind of manual loops are legal and which are race conditions is a complex problem.
In effect, I avoid leaving this monkey-see monkey-do "cargo cult" style of use of condition variables unless I have extremely good reason. And then I am forced to read up on the C++ memory and threading model, which doesn't make my day, and it means my code is most likely not going to be correct.
Note that if any of the lambdas passed in go and call back into cv_bundle
the code I showed is no longer valid.
1)
and2)
are the same code. Did you mean to show something different? – Lias