简体   繁体   中英

Is owning the lock required to request a stop while waiting on a condition_variable_any with stop_token?

While waiting on a condition variable, the thread changing the state of the predicate must own the lock, so the update isn't missed during the wakeup. According to the documentation, this is necessary, even while using atomic variables. However I'm not certain if request_stop() already handles it correctly.

So the question is, which of the two options is the correct and standard conforming one?

~jthread() naturally doesn't take a lock to request_stop() , but then I don't understand where the difference between a stop_token and an atomic shared variable is. And thereby, how one of them requires the lock, while the other doesn't.

#include <thread>
#include <condition_variable>
#include <iostream>
#include <chrono>


std::mutex m;
std::condition_variable_any cv;

void waitingThread(std::stop_token st){
    std::unique_lock<std::mutex> lk(m);
    std::cout<<"Waiting"<<std::endl;
    cv.wait(lk, st, [](){return false;});
    std::cout<<"Awake"<<std::endl;
}

void withoutLock(){
    std::jthread jt{waitingThread};
    std::this_thread::sleep_for(std::chrono::seconds(1));
    jt.request_stop();
}

void withLock(){
    std::jthread jt{waitingThread};
    std::this_thread::sleep_for(std::chrono::seconds(1));
    {
        std::lock_guard<std::mutex> lk(m);
        jt.request_stop();
    }
}

int main(){
    withoutLock();
    withLock();
}

std::condition_variable specifies:

Even if the shared variable is atomic, it must be modified while owning the mutex to correctly publish the modification to the waiting thread.

std::condition_variable_any::wait , std::stop_token and std::stop_source do not specify, if the interrupt of the wait is guaranteed register the change in the stop state.

While waiting on a condition variable, the thread changing the state of the predicate must own the lock, so the update isn't missed during the wakeup.

... do not specify, if the interrupt of the wait is guaranteed register the change in the stop state.

The issue isn't exactly missing the update, but going back to sleep after the notify has happened and before checking the predicate again.

The sequence in this answer is the problematic one.

According to the documentation, this is necessary, even while using atomic variables. However I'm not certain if request_stop() already handles it correctly.

The shared state itself is synchronized just fine: thread.stoptoken.intro/5 says

Calls to the functions request_stop , stop_requested , and stop_possible do not introduce data races. A call to request_stop that returns true synchronizes with a call to stop_requested on an associated stop_token or stop_source object that returns true.

The wait-predicate loop described in thread.condvarany.intwait would have the same problem:

while (!stoken.stop_requested()) {
  if (pred())
    return true;
  wait(lock);
}
return pred();

if request_stop is called between the first line and the wait(lock) , then stop_requested() will become true and the condition variable be notified while nobody is waiting. Then we'll start waiting, for a notification that never comes.

If request_stop can only be called while the mutex is released inside wait , we'll always be guaranteed to check stop_requested() before sleeping again.

In fact though, the GNU ISO C++ library does this for us: std::condition_variable_any has an additional internal mutex used to synchronize the request_stop callback (which just notifies the condvar in the requesting thread) correctly with the wait-predicate loop. So, if that behaviour is required by the standard, you don't need your own mutex here.

All stop_token overloads of condition_variable_any::wait and the timed variants, register a stop_callback , which captures the Lockable object. Then on request_stop() the callback is executed, taking the lock and notifying the waiting threads, therefore eliminating missing updates.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM