Использование stati c объектов из обработчика сигнала - PullRequest
0 голосов
/ 13 марта 2020

Согласно cppreference.com (я не искал его в стандарте), это UB, чтобы использовать stati c объекты из обработчиков сигналов.

Почему UB делает такие вещи? Каковы потенциальные проблемы этого?

Если обработчик сигнала вызывается НЕ в результате std::abort или std::raise (асинхронный сигнал), поведение не определено, если [...] обработчик сигнала относится к любому объекту со сроком хранения stati c, который не std::atomic (начиная с C ++ 11) или volatile std::sig_atomic_t.

Ответы [ 2 ]

2 голосов
/ 13 марта 2020

Стандарт C ++ говорит об этом в [intro.execution]:

19 Если обработчик сигнала выполняется в результате вызова функции std::raise, то выполнение обработчика выполняется после вызова функции std::raise и до ее возврата. [ Примечание: Когда сигнал принимается по другой причине, выполнение обработчика сигнала обычно не выполняется по сравнению с остальной частью программы. - конечная нота ]

Значение слова «непоследовательность» было разъяснено ранее:

15 ... SNIP ... [ Примечание: Выполнение неупорядоченных оценок может перекрываться. - конечная нота ]

Затем в [intro.races]:

20 Два действия потенциально одновременны если

(20.1) - они выполняются разными потоками, или

(20.2) - они не секвенированы, по крайней мере, один выполняется обработчиком сигнала, и они не выполняются одним и тем же вызов обработчика сигнала.

Выполнение программы содержит гонку данных , если она содержит два потенциально одновременных конфликтующих действия, по крайней мере одно из которых не атоми c, и ни одно из них не происходит раньше другой, за исключением специального случая для обработчиков сигналов, описанных ниже. Любая такая гонка данных приводит к неопределенному поведению.

Особый случай, о котором идет речь:

21 Два доступа к одному и тому же объекту типа volatile std::sig_atomic_t не приводят к гонка данных, если оба происходят в одном и том же потоке, даже если один или несколько из них встречаются в обработчике сигналов.

Подводя итоги: обработчик сигналов (вызываемый для асинхронного сигнала) обращается к объект со стати c длительностью хранения, который не является атомом c, этот доступ не секвенирован, и когда это происходит одновременно с конфликтующим доступом (к тому же объекту, например), тогда возникает гонка данных, что приводит к неопределенному поведению.

Обратите внимание, что это может происходить так же, как в однопоточном приложении, так и в многопоточном приложении. Пример (замените int любым другим типом, который более явно не атоми c, если необходимо):

#include <csignal>

int global = 0;

void signal_handler(int signal) {
    global = 0;  // OOPS : this access is (typically) unsequenced
                 // and might happen concurrently with the access
                 // in main, when the interrupt happens right in
                 // the middle of that access
}

int main(void) {
    std::signal(SIGINT, signal_handler);

    while (true) {
        ++global;  // potentially concurrent access
    }

    return 0;
}
0 голосов
/ 16 марта 2020

Эта проблема восходит к стандарту C, который использует термин UB для характеристики общих ситуаций, которые иногда могут быть дорогостоящими для реализаций, чтобы последовательно обрабатывать код, даже если большинство реализаций должно осмысленно обрабатывать такие ситуации, когда практично. Рассмотрим такую ​​функцию, как:

extern int x,y,z;

void test(int a)
{
  int i;
  for (i=0; i<a; i++)
  {
    x=a*i;
    y=a*a;
    z=a*i;
  }
}

Если компилятор должен сохранить значение от a*a до y между записями в x и z или ему должно быть разрешено его Для отдыха либо поднимите задание на y перед l oop, либо отложите его до завершения l oop. Если компилятору разрешено поднимать или откладывать присвоение на y, будет ли какой-либо простой и понятный способ описания поведения программы, если во время выполнения l oop происходит сигнал, а обработчик сигнала читает значения х, у и г? Стоимость и ценность предоставления различных поведенческих гарантий в таких случаях будет зависеть от множества факторов, о которых авторы Стандарта не могли бы знать. Вместо того, чтобы пытаться писать правила для того, что должно быть гарантировано, авторы Стандарта ожидали, что авторы компиляторов смогут лучше, чем Комитет, судить о потребностях своих клиентов.

...