Question

I would like to use a std::condition_variable as an exit condition for a method called on a thread. I envision that it will be used like this:

std::mutex m;
std::condition_variable exit_condition;

void func() {
    std::unique_lock<std::mutex> lk(m);
    while (exit_condition.wait_for(lk, std::chrono::milliseconds(0)) == std::cv_status::timeout)
        // Do something
}

int main() {
    std::thread t(func);

    // Do something

    exit_condition.notify_one();
    t.join();
}

However it seems that with a zero timeout, the return value of wait_for is always std::cv_status::timeout.

If I were to write func using the Windows API, I would write it like this:

HANDLE exit_condition;
void func() {
    while (WaitForSingleObject(exit_condition, 0) == WAIT_TIMEOUT)
        // Do something
}

Note that I can write func with a small timeout and it will exit.

void func() {
        std::unique_lock<std::mutex> lk(m);
        while (exit_condition.wait_for(lk, std::chrono::milliseconds(10)) == std::cv_status::timeout)
            // Do something
    }

I'm a bit concerned that under some conditions it will not exit though.

So, can I check a std::condition_variable without a timeout in some way that will guarantee the return value of the wait_for method will be std::cv_status::no_timeout?

Was it helpful?

Solution

If you don't actually need the timeout, you can get similar functionality without it by calling wait() instead of wait_for().

If the only purpose of all this is to have one thread tell the other one when to stop running, a typical thing to do is to check an atomic bool at the top of the loop (while (runnable)) and toggle it from the other thread when you want the loop to exit.

OTHER TIPS

Condition variables are not event objects. In particular, they don't have memory.

Set an explicit exit flag. And you actually do polling, so there's no need for any condition variables (merits of polling discussion aside).

bool exit_request = false;
std::mutex m;

void func() {
    while (true) {
        {
           std::unique_lock<std::mutex> lk(m);
           if (exit_request)
               return;
        }
        do_something();
    }
}

int main() {
    std::thread t(func);

    // Do something

    {
        std::unique_lock<std::mutex> lk(m);
        exit_request = true;
    }
    t.join();
}

PS. And, yeah, as said above, a simple atomic flags will do in this case.

To summarize the answers and the various discussion topics, I wanted to pull things together in one answer.

The answer to my original question is yes, you can check a std::condition_variable without a timeout, but it must be one of the wait overloads which accepts a predicate.

First, it seems that a std:condition_variable may not be the best tool to signal a thread to exit . A std::mutex and a shared bool or an std::atomic<bool> or std::atomic_flag work better.

If you do plan to use std::condition_variable as I proposed in the question, be aware of the possibility of a spurious wake. According to 3.50.1[#10] in the C++11 standard, a call to wait, wait_for, or wait_until may return without a call to notify_one, notify_all, or the predicate returning true.

This implies two guidelines:

  • Any code after wait returns must explicitly check the return value of the predicate.
  • None of the wait methods which do not accept a predicate should be called.

The following code accomplishes the goal of the original code in my answer, but does so safely.

std::mutex m;
std::condition_variable exit_condition;
bool should_exit;

void func() {
    std::unique_lock<std::mutex> lk(m);
    while (!exit_condition.wait_for(lk, std::chrono::milliseconds(0), []{return should_exit;})) {
        if (should_exit)
            break;
        // Do something
    }
}

int main() {
    should_exit = false;
    std::thread t(func);

    // Do something

    should_exit  = true;
    exit_condition.notify_one();
    t.join();
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top