#include <unistd.h>
#include <csignal>
#include <exception>
#include <functional>
#include <iostream>
std::function<void(int)> g_signalHandler;
void signalWrapper(const int a) { g_signalHandler(a); }
int main() {
bool abort = false;
g_signalHandler = [&abort](const int) {
std::cout << "Abort" << std::endl;
abort = true;
};
struct ::sigaction signalAction = {};
signalAction.sa_handler = signalWrapper;
if (
::sigaction(SIGHUP, &signalAction, nullptr) != 0 ||
::sigaction(SIGINT, &signalAction, nullptr) != 0 ||
::sigaction(SIGTERM, &signalAction, nullptr) != 0
)
throw std::exception();
// Is it guaranteed that the new value of abort will be seen here without
// the need of std::atomic<bool> or volatile bool?
while (!abort)
::sleep(1);
return 0;
}
Предположим, однопоточная программа, C++17 и Linux.
Насколько мне известно, использование обычного bool достаточно для while (!abort), а std::atomic<bool>/volatile bool не требуется. Я хотел бы подтвердить, что это всегда так и что мне не нужно беспокоиться о том, что компилятор оптимизирует чтение значения.
@TedLyngmo Я использую C++17
@asimes Да, это отлично работает и на C++17.
@TedLyngmo Теперь я понимаю, что volatile гарантирует, что значение считывается каждый раз, и что std::sig_atomic_t гарантирует, что его можно будет записать в одной инструкции. Однако почему static?
Я не читал мотивацию этого и не могу найти никаких упоминаний о том, что это должно быть static в стандарте C++23. Возможно, cppreference.com ошибся — или так было до C++11.





Насколько мне известно, для while (!abort) достаточно обычного bool, а std::atomic / Volatible bool не требуется. Я хотел бы подтвердить, что это всегда так и что мне не нужно беспокоиться о том, что компилятор оптимизирует чтение значения.
Нет, это неправильно и приведет к гонке данных, что всегда является неопределенным поведением.
Чтобы продемонстрировать, как такое неопределенное поведение может разумно проявляться: компилятор может видеть, что abort неатомарный и что ::sleep не подразумевает никакой синхронизации. Следовательно, он может сделать вывод, что abort никогда не изменится, если изначально он был false, поскольку это приведет к гонке данных с неопределенным поведением, и либо полностью избавиться от проверки цикла, либо сохранить abort в регистре, даже не перезагружая его из памяти. В любом случае ваш цикл никогда не завершится.
volatile bool тоже неправильно. volatile недостаточно согласно требованиям стандарта для обработчиков сигналов.
volatile гарантирует, что каждое чтение и запись в коде фактически преобразуется в загрузку и сохранение, однако volatile не дает никаких гарантий атомарности этих загрузок/сохранений. (Однако определенные комбинации компилятора/платформы могут давать такие гарантии, поэтому volatile можно использовать для реализации атомарных операций.)
Единственными нелокальными переменными, к которым вы гарантированно сможете получить доступ с определенным поведением в обработчике сигнала, являются (нелокальные для потока)
volatile std::atomic_sig_t (доступ к которым осуществляется через volatile glvalue); только если совместно используется (без другой синхронизации) исключительно между обработчиком сигнала и потоком, в котором выполняется обработчик сигнала, никаких других потоковstd::atomic_flag гарантированно без блокировки; какие из специализаций std::atomic не блокируются, зависит от платформы и может быть проверено с помощью участников is_lock_free и is_always_lock_free из std::atomicБолее того, библиотечные функции по умолчанию небезопасны для использования в обработчиках сигналов. В стандартах C/C++/POSIX есть только определенные исключения. В частности, std::function и потоковые операции ввода-вывода, такие как std::cout << , небезопасны в обработчиках сигналов, а также вызывают неопределенное поведение.
См. https://en.cppreference.com/w/cpp/utility/program/signal о требованиях, которые стандарт C++ налагает на обработчики сигналов, чтобы они имели четко определенное поведение в соответствии со стандартом.
См. https://pubs.opengroup.org/onlinepubs/9799919799/functions/V2_chap02.html#tag_16_04_03 список функций, безопасных для асинхронных сигналов в средах POSIX.