signal unsafety

This commit is contained in:
Dmis
2021-06-20 19:01:36 +03:00
parent 2b0ef728c9
commit f357e83ea8
2 changed files with 47 additions and 0 deletions
+1
View File
@@ -74,6 +74,7 @@
2. [shared_ptr](concurrency/shared_ptr.md)
3. [thread::join](concurrency/jthread.md)
4. [Повторный захват мьютекса](concurrency/double_lock.md)
5. [Signal-unsafe](concurrency/signal_unsafe.md)
---
+46
View File
@@ -0,0 +1,46 @@
# Сигнало(не)безопасность
Разработчик любого сколько-нибудь серьезного приложения рано или поздно вынужден озаботиться
вопросами поведения программы в различных краевых и внештатных ситуациях: запрос досрочного завершения, внезапное закрытие терминала, обработка маловероятных ошибочных состояний. Во многих этих случаях приходится иметь дело с довольно примитивным механизмом межпроцессного взаимодействия — с обработкой сигналов.
Программист регистрирует обработчики нужных ему сигналов и забот не знает, очень часть допуская серьезную ошибку —
выполняет в обработчике сигналов код, который там выполнять небезопасно: выделяет память, делает I/O, захватывает блокировки...
Сигналы прерывают нормальный ход исполнения программы и могут быть обработаны в произвольном потоке.
Поток мог начать выделять память, захватить блокировку в аллокаторе и в этот момент быть прерванным сигналом. Если обработчик сигнала в свою очередь запросит выделение памяти... Будет повторный захват блокировки в одном и том же потоке. Неопределенное поведение.
Очень легко можно продемонстрировать проблему на следующем примере
```C++
std::mutex global_lock;
int main() {
std::signal(SIGINT, [](int){
std::scoped_lock lock {global_lock};
printf("SIGINT!\n");
});
{
std::scoped_lock lock {global_lock};
printf("start long job\n");
sleep(10);
printf("end long job\n");
}
sleep(10);
}
```
Если мы скомпилируем эту программу под Linux (не забыв указать `-pthread`), запустим и нажмем `Ctrl+C`, то она зависнет навсегда из-за повторного захвата мьютекса одним и тем же потоком. Если же забудем `-pthread`, то не зависнет и отработает «ожидаемым» образом.
Под Windows эта программа также работает «ожидаемо» из-за специфики обработки сигналов — там для обработки SIGINT/SIGTERM всегда неявно порождается новый поток.
В любом случае этот код некорректен из-за использования сигналонебезопасной функции внутри обработчика сигналов.
### Как бороться?
1. Делать обработчики сигналов как можно более простыми
2. Сверяться с документацией, безопасно ли использование той или иной функции в контексте обработчика сигналов
3. Отключать автоматический прием сигналов и выполнять их обработку в рамках обычного исполнения программы (см., например, `sigprocmask` и `sigwait`)
### Полезные ссылки
1. https://man7.org/linux/man-pages/man7/signal-safety.7.html
2. https://www.gnu.org/software/libc/manual/html_node/Blocking-Signals.html