О pcntl_signal() и открытых сокетах

tuxСейчас пишу асинхронный PHP-демон и довольно глубоко пришлось погрузиться в библиотеку pcntl, а в частности в её функции работы с сигналами. Надо заметить, что касается написания всяких серверных долгоживущих штук – то там вообще всё очень интересно, и, на первый взгляд, совсем не очевидно.

Интро

Представим себе что у нас есть демон, который слушает сокет в неблокирующем режиме. Например, если мы пишем веб-сервер на php (хотя это из области фантастики и быдлокодинга), то он будет слушать 80 порт. Соответственно у него будут некие методы onOpen(), onClose(), onMessage(). Каждый из них – это обработчик открытия, закрытия соединения и получения сообщения. Кроме того, наш сервер умеет обрабатывать консольные сочетания клавиш (Ctrl+C), на которые забиндена shutdown_function().

А ещё он умеет ловить POSIX сигналы (SIGHUP) и обрабатывать их.

daemon

Проблема

Вроде бы, с первого взгляда всё нормально. Однако потом мы замечаем, что в некоторый момент времени наш обработчик sig_handler перестаёт вызываться, хотя мы и послали ему сигнал.

Как воспроизвести:
1. Запускаем скрипт, который устанавливает коллбеки с помощью pcntl_signal() на SIGTERM, а потом в бесконечном цикле слушает сокет
2. Делаем ему kill -s SIGTERM {pid}
3. Он живет и не умирает!
4. Делаем коннект к сокету, он открывает соединение, и только тут срабатывает коллбек из п.1, сигнал по которому уже давно послан в п.2

Чтобы проверить этот баг можно воспользоваться вот этим скриптом.

Решение

Довольно долго я колупался с этим, пока не узнал об одной особенности.

Это означает, что пока сокет находится в состоянии IDLE, т.е. ждёт коннектов – никакие сигналы не пройдут.
Именно поэтому баг воспроизводился периодически, то сигналы проходят, то нет. Сигналы обрабатываются только в активном режиме.

Ссылки

http://php.net/manual/en/function.socket-accept.php
http://grokbase.com/t/php/php-bugs/0236e2jbj8/bug-15906-pcntl-signal-does-not-work-while-waiting-with-socket-accept
http://highloadblog.ru/articles/9.html
http://pecl.php.net/package-changelog.php?package=libevent&release=0.0.4
http://phpclub.ru/talk/threads/pcntl_signal-%D0%B8-libevent.66402/
http://grokbase.com/t/php/php-bugs/0236e2jbj8/bug-15906-pcntl-signal-does-not-work-while-waiting-with-socket-accept

2 Comments

Leave a Reply to Slava Cancel reply