Reputation: 1219
This is simple code from http://www.cplusplus.com/reference/condition_variable/condition_variable/wait_for/
Why does wait_for() return instantly if I comment line with starting thread?
Like this:
// condition_variable::wait_for example
#include <iostream> // std::cout
#include <thread> // std::thread
#include <chrono> // std::chrono::seconds
#include <mutex> // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable, std::cv_status
std::condition_variable cv;
int value;
void read_value() {
std::cin >> value;
cv.notify_one();
}
int main ()
{
std::cout << "Please, enter an integer (I'll be printing dots): ";
//std::thread th (read_value);
std::mutex mtx;
std::unique_lock<std::mutex> lck(mtx);
while (cv.wait_for(lck,std::chrono::seconds(1))==std::cv_status::timeout) {
std::cout << '.';
}
std::cout << "You entered: " << value << '\n';
//th.join();
return 0;
}
Update:
Please don't look for other problems in this example (related buffering cout ...). The original question was about why wait_for is skipped.
Upvotes: 2
Views: 4526
Reputation: 104484
Short answer: compile with -pthread
and your issue will go away.
This is a confirmed bug/issue in libstdc++. Without -pthread
being passed in as a compiler flag, the timed wait call will return immediately. Given the history of the issue (3 years), it's not likely to be fixed anytime soon. Anyway, read my message below on why you should be using condition variables with predicates to avoid the spurious wakeup problem. It still holds true even if you are linking with the posix threads library.
That sample code on cplusplus.com has several issues. For starters, amend this line:
std::cout << '.';
To be like this:
std::cout << '.';
std::cout.flush()
Otherwise, you won't see any dots if stdout isn't getting flushed.
If you compile your program (with the thread commented out) like this:
g++ yourcode.cpp -std=c++11
Then the resulting a.out program exhibits the issue you described when the thread is not used. That is, there's a spurious wakeup when the thread is not used. It's like there's a phantom notify()
call being invoked on the condition variable from some unknown source. This is odd, but not impossible.
But as soon as you uncomment out the declaration of the thread variable, the program will throw an exception (and crash) as a result of the program not using a multithreaded:
terminate called after throwing an instance of 'std::system_error'
what(): Enable multithreading to use std::thread: Operation not permitted
Please, enter an integer (I'll be printing dots): Aborted (core dumped)
Interesting, so let's fix that by recompiling with -pthread
g++ yourcode.cpp -std=c++11 -pthread
Now everything works as expected with or without the thread. No more spurious wakeup it seems.
Now let's talk about why you are seeing the behavior you are seeing. Programs using condition variables should always be written to deal with spurious wakeup. And preferably, use a predicate statement. That is, you might get a phantom notify causing your wait or wait_for statement to return early. The example code on the web from cplusplus.com doesn't use a predicate nor does it deal with this possibility.
Let's amend it as follows:
Change this block of code:
while (cv.wait_for(lck,std::chrono::seconds(1))==std::cv_status::timeout) {
std::cout << '.';
}
To be this:
while (cv.wait_for(lck,std::chrono::seconds(1), condition_check)==false) {
std::cout << '.';
std::cout.flush();
}
And then elsewhere outside of main
, but after the declaration of value
, add this function:
bool condition_check() {
return (value != 0);
}
Now the wait loop will wake up every second and/or when the notify
call is made by the input thread. The wait loop will continue until value != 0
. (Technically, value
should be synchronized between threads, either with the lock or as a std::atomic value, but that's a minor detail).
Now the mystery is why does the non-predicate version of wait_for
suffer from the spurious wake_up problem. My guess is that's an issue with the single threaded C++ runtime that goes away when the multithreaded runtime (-pthread
) is used. Perhaps condition_variable
has different behavior or a different implementation when the posix thread library is linked in.
Upvotes: 8
Reputation: 16888
There are several issue with this code:
First, as you have noticed, the program has to be build with the -pthread
option.
Second, you need to flush the output if you want to see the dots printed.
Most importantly, this is entirely incorrect usage of mutex and condition variable. A condition variable notification indicates a change of value in a user-specified predicate/condition: the changing of the condition and examining it must be atomic and serialized: otherwise there is a data race and the behavior of the program would be undefined.
As is the case with the example program: value
is read and written by two threads, but without any concurrency control mechanism, or to put it differently, there's no "happens-before" relation between the operation, which reads value
and the operation which writes value
.
Fixed example follows:
// condition_variable::wait_for example
#include <chrono> // std::chrono::seconds
#include <condition_variable> // std::condition_variable, std::cv_status
#include <iostream> // std::cout
#include <mutex> // std::mutex, std::unique_lock
#include <thread> // std::thread
std::mutex mtx;
std::condition_variable cv;
int value;
void read_value() {
int v;
std::cin >> v;
std::unique_lock<std::mutex> lck(mtx);
value = v;
cv.notify_one();
}
int main() {
std::cout << "Please, enter an integer (I'll be printing dots): ";
std::thread th(read_value);
std::unique_lock<std::mutex> lck(mtx);
while (cv.wait_for(lck, std::chrono::seconds(1)) == std::cv_status::timeout) {
std::cout << '.' << std::flush;
}
std::cout << "You entered: " << value << '\n';
th.join();
return 0;
}
So, what are the changes:
value
can lock it, in order to modify value
.value
, because value
must be modified only under the protection of the mutex, but holding the mutex, while waiting from input form std::cin
would prevent the main thread from printing dots, as it will try to acquire the mutex upon timeout.std::cout
is flushedUpvotes: 0