Reputation: 4780
As it turns out, condition_variable::wait_for
should really be called condition_variable::wait_for_or_possibly_indefinitely_longer_than
, because it needs to reacquire the lock before really timing out and returning.
See this program for a demonstration.
Is there a way to express, "Look, I really only have two seconds. If myPredicate()
is still false at that time and/or the lock is still locked, I don't care, just carry on regardless and give me a way to detect that."
Something like:
bool myPredicate();
auto sec = std::chrono::seconds(1);
bool pred;
std::condition_variable::cv_status timedOut;
std::tie( pred, timedOut ) =
cv.really_wait_for_no_longer_than( lck, 2*sec, myPredicate );
if( lck.owns_lock() ) {
// Can use mutexed resource.
// ...
lck.unlock();
} else {
// Cannot use mutexed resource. Deal with it.
};
Upvotes: 22
Views: 33311
Reputation: 69988
Is there a way to express, "Look, I really only have two seconds. If myPredicate() is still false at that time and/or the lock is still locked, I don't care, just carry on regardless ..."
Yes, there is a way, but unfortunately in case of wait_for
it has to be manual. The wait_for
waits indefinitely because of Spurious Wakeup. Imagine your loop like this:
while(!myPredicate())
cv.wait_for(lock, std::chrono::duration::seconds(2);
The spurious wakeup can happen anytime in an arbitrary platform. Imagine that in your case it happens within 200 ms. Due to this, without any external notification wait_for()
will wakeup and check for myPredicate()
in the loop condition.
As expected, the condition will be false, hence the loop will be true and again it will execute cv.wait_for(..)
, with fresh 2 seconds. This is how it will run infinitely.
Either you control that updation duration by yourself or use wait_until()
which is ultimately called in wait_for()
.
Upvotes: 0
Reputation: 11
Actually, the condition_variable::wait_for
does exactly what you want. The problem with your example is that you locked a 2-second sleep along with the ready = true
assignment, making it impossible for the condition variable to even evaluate the predicate before reaching the time limit.
Put that std::this_thread::sleep_for(2*sec);
line outside the lock and see for yourself.
Upvotes: 1
Reputation: 6537
I think that you misuse the condition_variable
's lock. It's for protecting condition only, not for protecting a time-consuming work.
Your example can be fixed easily by splitting the mutex
into two - one is for critical section, another is for protecting modifications of ready
condition. Here is the modified fragment:
typedef std::unique_lock<std::mutex> lock_type;
auto sec = std::chrono::seconds(1);
std::mutex mtx_work;
std::mutex mtx_ready;
std::condition_variable cv;
bool ready = false;
void task1() {
log("Starting task 1. Waiting on cv for 2 secs.");
lock_type lck(mtx_ready);
bool done = cv.wait_for(lck, 2*sec, []{log("Checking condition..."); return ready;});
std::stringstream ss;
ss << "Task 1 finished, done==" << (done?"true":"false") << ", " << (lck.owns_lock()?"lock owned":"lock not owned");
log(ss.str());
}
void task2() {
// Allow task1 to go first
std::this_thread::sleep_for(1*sec);
log("Starting task 2. Locking and sleeping 2 secs.");
lock_type lck1(mtx_work);
std::this_thread::sleep_for(2*sec);
lock_type lck2(mtx_ready);
ready = true; // This happens around 3s into the program
log("OK, task 2 unlocking...");
lck2.unlock();
cv.notify_one();
}
It's output:
@2 ms: Starting task 1. Waiting on cv for 2 secs.
@2 ms: Checking condition...
@1002 ms: Starting task 2. Locking and sleeping 2 secs.
@2002 ms: Checking condition...
@2002 ms: Task 1 finished, done==false, lock owned
@3002 ms: OK, task 2 unlocking...
Upvotes: 12