12

I found the following example for condition variable on www.cppreference.com, http://en.cppreference.com/w/cpp/thread/condition_variable. The call to cv.notify_one() is placed outside the lock. My question is if the call should be made while holding the lock to guarantee that waiting threads are in fact in waiting state and will receive the notify signal.

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

std::mutex m;
std::condition_variable cv;
std::string data;
bool ready = false;
bool processed = false;

void worker_thread()
{
    // Wait until main() sends data
    std::unique_lock<std::mutex> lk(m);
    cv.wait(lk, []{return ready;});

    // after the wait, we own the lock.
    std::cout << "Worker thread is processing data\n";
    data += " after processing";

    // Send data back to main()
    processed = true;
    std::cout << "Worker thread signals data processing completed\n";

    // Manual unlocking is done before notifying, to avoid waking up
    // the waiting thread only to block again (see notify_one for details)
    lk.unlock();
    cv.notify_one();
}

int main()
{
    std::thread worker(worker_thread);

    data = "Example data";
    // send data to the worker thread
    {
        std::lock_guard<std::mutex> lk(m);
        ready = true;
        std::cout << "main() signals data ready for processing\n";
    }
    cv.notify_one();

    // wait for the worker
    {
        std::unique_lock<std::mutex> lk(m);
        cv.wait(lk, []{return processed;});
    }
    std::cout << "Back in main(), data = " << data << '\n';

    worker.join();
}

Should the notify_one() call be moved inside the lock to guarantee waiting threads receive the notify signal,

// send data to the worker thread
{
    std::lock_guard<std::mutex> lk(m);
    ready = true;
    cv.notify_one();
    std::cout << "main() signals data ready for processing\n";
}
Nemo
  • 2,833
  • 25
  • 21
  • 3
    Would [this](http://stackoverflow.com/a/17102100/1460794) answer your question? – wally Mar 03 '16 at 14:58
  • Possible duplicate of [Why do pthreads’ condition variable functions require a mutex?](http://stackoverflow.com/questions/2763714/why-do-pthreads-condition-variable-functions-require-a-mutex) – wilx Mar 03 '16 at 14:58
  • The documentation answer your question explicitly, just read! For you: the lock does not need to be held for notification... And holding the mutex does not ensure that the thread is in waiting state. – knivil Mar 03 '16 at 15:01
  • @knivil it does ensure that it is in waiting state or haven't started yet, anyway it avoids race condition – Slava Mar 03 '16 at 15:05
  • @wilx if that duplicate then example on cppreference is correct and question is valid, read example there – Slava Mar 03 '16 at 15:07
  • @Slava: There is no race condition on condition variable because it is a synchronization primitive. So I do not need a protecting mutex. – knivil Mar 03 '16 at 15:09
  • @knivil yes there is race condition, see this http://stackoverflow.com/questions/31164748/how-to-make-sure-all-slave-threads-are-waited-for-conditional-variable/31165237#31165237 and this http://stackoverflow.com/questions/20982270/sync-is-unreliable-using-stdatomic-and-stdcondition-variable – Slava Mar 03 '16 at 15:15
  • That was not the question, `ready`, `data` or `processed` are not part of the question. – knivil Mar 03 '16 at 15:17
  • Sidenote: Unless you are just goofing around, my recommendation would be not to write real applications in that style. Consider rather to have long-living threads/thread pools and a message based actor style of architecture. The processing actor then simply waits for a message in its mailbox and sends result back to the actor(s) who needs the result. Once you have a implementation of that scheme, you can use it for all sorts of applications. Better than doing it over and over again in ad hoc style, nah? – BitTickler Mar 03 '16 at 15:23
  • @BitTickler Is there any such facility in the standard library? I'm refreshing my c++ after a long time, sticking to just c++11 standard facilities as I don't want to overwhelm myself. – Nemo Mar 03 '16 at 15:50
  • I think the library has some promises/futures thing in place. Never used it myself. For some actor framework, you might find some approaches with google. Boost has something like that, too, I think. Or you can do it yourself. In my past, I did the latter. – BitTickler Mar 03 '16 at 15:56
  • See for example: http://en.cppreference.com/w/cpp/thread/future – BitTickler Mar 03 '16 at 16:30
  • @BitTickler Thank you. – Nemo Mar 03 '16 at 21:34

3 Answers3

8

if I understand your question correctly, it's equivilant to "should the notifier thread lock the mutex while trying to notify some CV in other thread"

no, it is not mandatory and even does some counter-effect.
when condition_variable is notified from another thread it tries to-relock the mutex on which it was put to sleep. locking that mutex from its working thread will block the other thread which trying to lock it, untill the that lock-wrapper gets out of scope.

PS
if you do remove the locking from the function that send data to the worker threads, ready and processed should at least be atomics. currently they are synchronized by the lock , but when you remove the lock they cease to be thread-safe

Marcel Gosselin
  • 4,430
  • 2
  • 25
  • 50
David Haim
  • 23,138
  • 3
  • 38
  • 70
  • Is there race condiation that will lead to missing signal if lock is not held when `notify_one()` is called? – Slava Mar 03 '16 at 15:04
  • overall mutexes and condition_variables cannot cause race condition within themselves, they are synchroniation primitives. – David Haim Mar 03 '16 at 15:06
  • @Slava Yes, you can miss signals, but you can miss signals if it did hold the lock too - albeit your code seems safe from that due to the `cv.wait(lk, []{return ready;})` , regardless of the notify_one() being done with the lock held or not. (But setting the ready flag must be done with the lock held) – nos Mar 03 '16 at 15:06
  • 1
    @nos no there is no race condition if notify is sent under lock, waiting thread either will check the flag or wakeup. Without lock it may check flag and then miss the signal – Slava Mar 03 '16 at 15:09
  • @Slava, yes, sorry it was meant in general, the specific code here seems fine. – nos Mar 03 '16 at 15:10
  • @nos even so I think this example is misleading, it should show general case where it is safe to send signal under lock – Slava Mar 03 '16 at 15:11
  • @Slava: You do not understand, and thanks for downvote. – knivil Mar 03 '16 at 15:13
  • @knivil I am sorry but you do not understand, read links I gave you above – Slava Mar 03 '16 at 15:17
  • 1
    @DavidHaim that's not quite true read here http://stackoverflow.com/questions/31164748/how-to-make-sure-all-slave-threads-are-waited-for-conditional-variable/31165237#31165237 and here http://stackoverflow.com/questions/20982270/sync-is-unreliable-using-stdatomic-and-stdcondition-variable – Slava Mar 03 '16 at 15:19
  • @DavidHaim Didn't understand your point on relock. In the example, the lock goes out of scope and the call to notify_one() re-locks and sends signal as per your answer. If that was moved inside the scope, it wouldn't need to re-lock, therefore more efficient? – Nemo Mar 03 '16 at 15:27
  • @Nemo your lock gets out of scope *after* you called `notify_one`. that fraction of the moment is the moment the condition variable wakes up and tries to re-lock the lock – David Haim Mar 03 '16 at 15:30
  • @DavidHaim So, here's my understanding. The lock on the mutex goes out of scope after the closing brace "}". At that point, no one owns the mutex. Now the call to notify_one() just signals any waiting threads that the mutex is free. This essentially makes waiting easier instead of having to poll constantly. Is my understanding correct? – Nemo Mar 03 '16 at 15:46
  • many threads can sleep on the same cv. when you notify one - one of the sleeping therads wakes up and re-lock the lock the cv was put to sleep with. that is it. all the rest are unrelated. – David Haim Mar 03 '16 at 15:53
  • The PS is wrong, making the condition atomic will break the condition variable protocol. It must be modified under the same mutex as what cv.wait is waiting on. – Cubbi Mar 03 '16 at 20:57
  • Sorry, downvoted because it is incorrect. I agree with @Slava that notifies can crucially be lost (as in: leading to starvation / dead lock kind of situtations). When you RELY on that one notify_one() to actually wake up one thread, then it must be done inside the lock. Indeed in this particular snippet that seems not to be the case. – Carlo Wood Jan 20 '19 at 22:25
8

You do not need to notify under lock. However, since notify is logically happening when the actual value is changed (otherwise, why would you notify?) and that change must happen under lock, it is often done within the lock.

There would be no practical observable difference.

SergeyA
  • 56,524
  • 5
  • 61
  • 116
  • In this particular code there may be no difference, but it is there in general, I already posted links – Slava Mar 03 '16 at 15:21
  • @Slava, you posted links to your own answers. This is hardly an authority, sorry. I maintain my statements. There will be no race when used in proper manner (that is, locking before checking and locking before setting) – SergeyA Mar 03 '16 at 15:23
  • I posted link to one of my answer and one not mine, read second if you prefer – Slava Mar 03 '16 at 15:23
  • Here is quote from pthread docs "if predictable scheduling behavior is required, then that mutex shall be locked by the thread calling pthread_cond_broadcast() or pthread_cond_signal()." – Slava Mar 03 '16 at 15:25
  • @Slava, this is pthread. However, `std::condition_variable` does not provide for predictable scheduling under any circumstances, so this point is 100% moot. – SergeyA Mar 03 '16 at 15:29
  • you want me to create example program where I check and set flag under lock and it will miss a signal? – Slava Mar 03 '16 at 15:31
  • 2
    @Slava, this is the point you fail to understand. condition_variables are **not** signalling mechanisms. So 'missing a signal' is an absolutely moot point. conditiona_variable usage scenario is always the same: lock mutex, check data, unlock-and-wait. – SergeyA Mar 03 '16 at 15:41
  • I have a queue and pool of thread processing it. Missing signal means message in queue will not be processed in time. That is a moot point? Huh – Slava Mar 03 '16 at 15:49
  • @Slava, why did you remove your answer? In it's current form it does not meet standards for good SO answer, but you can easily re-arrange it, explaining that notifying under lock is important because of so and so and provide an illustration with (pseudo) code. Than people will have the option to voice their opinion and indicate their approval with upvotes or disagreement with downvotes. – SergeyA Mar 03 '16 at 15:51
  • I removed it because I typed comment and by mistake put it into answer. I am working on example it just takes some time – Slava Mar 03 '16 at 15:53
  • @SergeyA Is the following sequence possible if I move notify inside: 1) worker waiting on condition variable 2) main calls notify while holding lock 3) main has not yet released lock 4) worker wakes up and sees lock not yet released, though condition(ready) is true 5) worker goes back to wait 6) main releases lock. Here because main held lock while notify, worker couldn't proceed. – Nemo Mar 03 '16 at 18:04
  • @Nemo, you misunderstand how condition_variable works as well. Step 5) is not going to happen. Instead, worker will wait on mutex. When the mutex is released by main, worker will grab it and enter the loop (provided, no one else is waiting on the same mutex). – SergeyA Mar 03 '16 at 18:06
  • @SergeyA Can you tell the sequence of events after step 3? Main called notify, but hasn't released mutex yet, worker receives notify and wakes up, tries to acquire mutex but cannot. Now how does worker keep checking for release of mutex? – Nemo Mar 03 '16 at 21:41
  • 1
    @Nemo, it this point, conditional variable is out of the picture. The worker remains waiting on mutex the same way as if you simply tried locking an already locked mutex. You see, unlike entrance to `wait`, which is atomic (release mutex and start wait) exit from `wait` is not atomic. Thread first is woken up from wait (or wakes up spuriously!), than tries to grab mutex. If mutex is available, it's cool, if it is not, it is now a simple mutex wait. Once the mutex becomes avaible, thread continues. conditional variable might have signalled 1000 times in between, no one cares. – SergeyA Mar 03 '16 at 21:46
  • @SergeyA Thanks Sergey. I put a sleep for two seconds after notify and see that the condition variable acquires the lock after two seconds and continues execution, which proves its able to detect the release of the mutex. How exactly is this mechanism implemented? After notify is made on the cv, the worker thread starts trying to acquire the lock continuously/periodically until it succeeds? – Nemo Mar 03 '16 at 22:08
  • @Nemo, are you asking how mutexes are implemented in general? Your question is not specific to condition variable. – SergeyA Mar 03 '16 at 22:10
  • @SergeyA I think I have a better understanding now. Thanks for your answers! – Nemo Mar 04 '16 at 16:08
  • I downvoted this because you say that the notify_one() one is often only done inside the lock because you already have the lock anyway, but that there is no difference with doing it after the lock. That is incorrect. – Carlo Wood Jan 20 '19 at 22:09
  • @CarloWood if you think it is incorrect, what do you think is correct behavior? – SergeyA Jan 22 '19 at 14:15
-4

If you do not wait on a condition variable then the notification is lost. It does not matter if you are holding any locks. A condition variable is a synchronization primitive and do not need a lock for protection.

You can miss signals with and without lock. The mutex protects only normal data like ready or processed.

knivil
  • 693
  • 3
  • 9
  • Unfortunately everybody can down vote correct answers. – knivil Mar 03 '16 at 15:36
  • 1
    This user is deeply confused. – SergeyA Mar 03 '16 at 15:41
  • 1
    This is also incorrect because the essence of a wait() is that it *atomically* unlocks the mutex and begins to wait, which guarantees that any notify done after the lock is released will be seen by the waiting thread. So you can't say "A condition variable ... does not need a lock for protection" when it is such an essential part of it. Not downvoted though. – Carlo Wood Jan 20 '19 at 22:13