Как я могу отменить вызов sigwaitinfo?

52
6

Фон

Моя цель - обрабатывать определенные сигналы в выделенном потоке, а не обрабатывать их ни в одном из потоков, которые выполняются в моем процессе, когда сигнал поднят.

Я делаю это следующим образом (в этом примере только для сигнала 16):

На основном потоке, прежде чем запускать какие-либо другие потоки (ошибка обработки исключена)

sigset_t sigset;
sigaddset(&sigset, 16);
sigprocmask(SIG_BLOCK, &sigset, nullptr);

Затем я создаю поток, который ждет этих сигналов (только 16 в этом примере):

std::thread _thread = std::thread([&]()
{
int ret = sigwaitinfo(&sigset, nullptr);
if (ret == 16)
{
// handle signal 16
}
});

Это хорошо работает.

проблема

Тем не менее, я хотел бы иметь возможность отменить вызов sigwaitinfo, когда это необходимо.

Два неадекватных решения

Я пробовал два решения, но ни один из них не подходит:

1. Опрос

Один из вариантов (который работает) заключается не в использовании sigwaitinfo, а в использовании sigtimedwait, который принимает аргумент таймаута. Это позволяет мне использовать опрос и отменять, когда следующий вызов возвращается, и установлен флаг отмены. Затем код в потоке выглядит так:

std::atomic<bool> _cancel (false);
std::thread _thread = std::thread([&]()
{
timespec _timespec {0, 1}; // 1 second
int ret = sigtimedwait(&sigset, nullptr, _timespec);
if (_cancel)
{
return;
}
if (ret == 16)
{
// handle signal 16
}
});

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

2. raise()/sigqueue()/kill()

В этом решении я добавляю к маске сигнала выделенный сигнал, например SIGUSR1 со следующим вызовом:

sigset_t sigset;
sigaddset(&sigset, 16);
sigaddset(&sigset, SIGUSR1); // <-- added call here
sigprocmask(SIG_BLOCK, &sigset, nullptr);

Затем, когда мне нужно отменить вызов sigwaitinfo, я установил флаг отмены и повысил ставку (SIGUSR1)

Затем код в потоке выглядит так:

std::atomic<bool> _cancel (false);
std::thread _thread = std::thread([&]()
{
int ret = sigwaitinfo(&sigset, nullptr);
if (_cancel) // <-- now check _cancel flag before handling signal
{
return;
}
if (ret == 16)
{
// handle signal 16
}
});

Отмена теперь выполняется следующим образом:

_cancel = true; // <-- set the flag before raising the signal
raise(SIGUSR1);

Проблема с этим решением заключается в том, что он не работает, потому что вызов функции raise() не приводит к возврату sigwaitinfo в выделенный поток. Я считаю, что в соответствии с документацией он только повысит сигнал в исполняющем потоке. sigqueue() и kill() также не работают.

Резюме

Есть ли способ заставить sigwaitinfo вернуться преждевременно, не требуя цикла, в котором вызовы sigtimedwait вызываются с таймаутом?

спросил(а) 2018-08-04T19:55:00+03:00 1 год, 8 месяцев назад
1
Решение
72

Это решение, которое я нашел.

Вместо того, чтобы ждать с помощью sigtimedwait, используйте signalfd, чтобы получить файловый дескриптор, который представляет сигналы, которые будут обрабатываться. (sigprocmask или аналогичный должен быть назван первым, как с решением, представленным в вопросе). Вызвать eventfd, чтобы вернуть дескриптор файла события. Ожидание вызова в обоих дескрипторах файлов. Это блокирует. Сделайте это в цикле. Отмена сигнала путем записи в дескриптор файла событий в другом потоке. Когда результаты опроса проверяют, какой файловый дескриптор сигнализировали, проверяя поля "Восстанавливает". Если описатель файла события был передан в сигнал, прорыв от цикла. Else (сигнализатор signalfd был сигнализирован) прочитал описание сигнала и обработал сигнал, вызвав обработчик. Затем снова включите повторный опрос.

Я проверял, что это решение является надежным. Более подробную информацию можно найти в документации для: signalfd, eventfd и poll

ответил(а) 2018-08-13T22:12:00+03:00 1 год, 8 месяцев назад
36

Используйте pthread_kill для отправки сигнала в конкретный поток.

Например, вместо raise(SIGUSR1); делать:

if(int rc = ::pthread_kill(_thread.native_handle(), SIGUSR1))
// Handle pthread_kill error.

ответил(а) 2018-08-06T16:58:00+03:00 1 год, 8 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема