Using a lock with condition variables
Asked Answered
B

1

3

Consider the following simplistic example of condition variables:

bool pause = true;

boost::mutex::scoped_lock lock(m_mutex);
while (!pause) cv.wait(lock);

and

boost::mutex::scoped_lock lock(m_mutex);
pause = false;
cv.notify_one();

Do we essentially need the scoped_lock or any other lock here, if we are running the code on a processor which supports updates on byte-granularity. This essentially means that the assignment of bools are atomic which is typically the case with x86 processors.

Has it got something to do with syncing of the variable in the case when the two threads are running on two different processors and have separate caches?

Barnwell answered 22/9, 2016 at 6:59 Comment(4)
while (!pause) cv.wait(lock); can be written cv.wait(lock, [&]{ return pause; });Maniac
The shown code is correct, pause is protected with the lock currently. Do you ask if you can remove the lock guard ?Maniac
Yes, the code is indeed correct. My question is, do we need the lock guard?Barnwell
yes, or use std::atomic<bool>.Maniac
W
2

Yes you do, and using atomic is not sufficient.

CVs for efficiency can be woken spuriously, and those spurious lookups (or similar issues) can cause a write to be missed.

Imagine a spurious wake up. The recieving thread checks the bool, sees nothing (false), then is preempted. Someone notify all's and sets the bool. The notification is discarded, as the recieving thread is already processing one. The recieving thread now completes, and misses the message.

Now, add a lock in the sender that overlaps some time sequenced after the bool is set and before the cv notification. This communicaton hole no longer exists.

(Even without spurious wakeups, multiple notifications can cause similar problems sometimes.)

You do not have to hold the lock while notifying (and in fact this is a pessimization), but the lock must be held post-write and pre-notify for some period, in general.

Winsome answered 22/9, 2016 at 8:19 Comment(8)
What do you mean by "some period"? Who decides that period?Barnwell
@user "a lock in the sender that overlaps some time sequenced after the bool is set and before the cv notification."Winsome
If we release the lock before notifying, it might still lead to the receiving thread missing the message if "some period" is not sufficient enough.Barnwell
@user No, not that I can tell.Winsome
How can we ensure that the lock in the sender overlaps with the time after bool is set and before cv notification?Barnwell
@user however you want? I am giving requirements, how you fulllfil them is up to you. You could do something as stupid as launching an async process afer setting the bool, in that process causing the lock to be aquired and released, then waiting for it to complete prior to setting the condition variable. Myself, I'd b=true; std::unique_lock<std::mutex>{m}; cv.notify_all(); or { std::unique_lock lock<std::mutex>{m}; b=true; } cv.notify_all(); or even { std::unique_lock lock<std::mutex>{m}; b=true; cv.notify_all();}or somesuch. How exactly you do it is up to you.Winsome
It does not depend on how I want to do it. First two method is wrong and could produce the same problem as shown by you in your answer. Only the last method avoids that problem.Barnwell
@user no, it does not (presuming atomic bool, either abi or explicitly). Feel free to walk through the steps, I have done so in the past, and I am being quite specific. The lock being engaged between the setting and the notification is exactly what I am stating is required, nothing else. It being actually after and actually before takes some care (ie, no race conditions or other UB).Winsome

© 2022 - 2024 — McMap. All rights reserved.