AdamF
AdamF

Reputation: 2601

How to correctly wait for condition variable timeout

I'm working on simple cancellation mechanism. But I have found problem with waiting for timeout on condition variable.

Lets consider the sample program from: https://www.cplusplus.com/reference/condition_variable/condition_variable/wait_for/ It looks like this sample is broken. If someone would provide the data very fast then the program would go into infinite loop. To visualize it I did little modification to the sample program:

#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
using namespace std::chrono_literals;

std::condition_variable cv;

int value = -1;

void compute() {
    value = 0;;
    cv.notify_one();
}

int main()
{
    std::thread th(compute);

    std::this_thread::sleep_for(1s);

    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::endl;
    }
    std::cout << "You entered: " << value << '\n';

    th.join();

    return 0;
}

As I can't type as fast I just set the value to 0 and execute notify_one. On the main thread I simulate simple delay. sleep_for(1s). Finally the program does not see the notify_one and loops infinitely. The output is: .....

My question is how to implement it correctly ? I would like to know also if the waiting was stopped by timeout.

Upvotes: 2

Views: 9760

Answers (1)

Mike Vine
Mike Vine

Reputation: 9837

If the notify happens before the wait then it indeed gets "lost".

Most usage of CVs also require a flag of some sort which should be checked in the predicate. You already have this flag - value. Just use this as a predicate:

EDIT: Removed wrong code.

Note that as a separate matter you should protect the writing to value with your mutex or you're likely to hit UB. Which means you need to make your mutex global along with the CV/Flag.

Better way:

auto endTime = std::chrono::now() + std::chrono::seconds(1);
while(flag != 0)
{
   auto res = cv.wait_until(lck, endTime);
   if (res == std::cv_status::timeout)
   {
        // Add Timeout logic here
        break;
   }
}

Upvotes: 6

Related Questions