简体   繁体   中英

Is std::condition_variable::notify_one reentrant?

Can I safely execute following code? Is it possible to have deadlock here or some unexpected behaviour, especially when SIGINT arrives?

#include <atomic>
#include <condition_variable>
#include <csignal>

std::mutex m;
std::condition_variable cv;
std::atomic<bool> flag(false);

void f1(){
             std::signal(SIGTERM, [](int signal){flag.store(true, 
             std::memory_order_release); cv.notify_one();});//register for signal
             std::unique_lock<std::mutex> mtx(m);
             cv.wait(mtx, []{return flag.load(std::memory_order_consume);});//wait for signal or f2() notify
         }

void f2(){
             flag.store(true, std::memory_order_release);
             cv.notify_one();
         }

int main(){
    std::thread th1(f1);
    std::thread th2(f2);
    th1.join();
    th2.join();
    return 0;
}

pthread functions and std::condition_variable and std::mutex which use those are not async-signal safe. See the list of async-signal safe functions in man signal-safety(7) .


A bit off-topic: if you do not lock the mutex when updating flag then that leads to missed notifications. Imagine scenario:

Thread 1        | Thread 2
                | mutex.lock()
                | flag == false
flag = true     | 
cv.notify_one() | <--- notification is lost
                | cv.wait()

This is a very common programming mistake.

Locking the mutex when updating flag fixes this problem.


If you would like to notify a condition variable when a signal is received create a thread dedicated to signal handling. Example:

#include <condition_variable>
#include <iostream>
#include <thread>
#include <signal.h>

std::mutex m;
std::condition_variable cv;
bool flag = false;

void f1(){
    std::unique_lock<std::mutex> lock(m);
    while(!flag)
        cv.wait(lock);
}

void signal_thread() {
    sigset_t sigset;
    sigfillset(&sigset);
    int signo = ::sigwaitinfo(&sigset, nullptr);
    if(-1 == signo)
        std::abort();
    std::cout << "Received signal " << signo << '\n';

    m.lock();
    flag = true;
    m.unlock();
    cv.notify_one();
}

int main(){
    sigset_t sigset;
    sigfillset(&sigset);
    ::pthread_sigmask(SIG_BLOCK, &sigset, nullptr);

    std::thread th1(f1);
    std::thread th2(signal_thread);

    th1.join();
    th2.join();
}

Note that the signals must be blocked in all threads, so that only the signal handler threads receives those.

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