Является ли std :: condition_variable :: notify_one реентерабельным?

Могу ли я безопасно выполнить следующий код? Возможно ли завести здесь тупик или какое-то неожиданное поведение, особенно когда прибывает SIGINT?

#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;
}

0

Решение

pthread функции и std::condition_variable а также std::mutex которые используют те, не безопасный асинхронный сигнал Смотрите список безопасных функций асинхронного сигнала в man signal-safety(7).


Немного не по теме: если вы не блокируете мьютекс при обновлении flag тогда это приводит к пропущенным уведомлениям. Представьте себе сценарий:

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

Это очень распространенная ошибка программирования.

Блокировка мьютекса при обновлении flag исправляет эту проблему.


Если вы хотите уведомить переменную условия при получении сигнала, создайте поток, предназначенный для обработки сигнала. Пример:

#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();
}

Обратите внимание, что сигналы должны быть заблокированы во всех потоках, чтобы их получали только потоки обработчика сигналов.

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]