How to use a condition_variable to really wait_for no longer than a certain duration How to use a condition_variable to really wait_for no longer than a certain duration multithreading multithreading

How to use a condition_variable to really wait_for no longer than a certain duration


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...


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.


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().