Я пытаюсь сделать некоторые IPC в PHP, используя именованные каналы. У меня есть именованный канал, созданный с помощью
$pipePath = __DIR__ . '/pipe';
posix_mkfifo($pipePath, 0600);
Есть еще один процесс, который должен записать в этот канал после завершения вычислений. Я могу дождаться его окончания и прочитать результат примерно так:
$result = file_get_contents($pipePath);
или более многословный
$in = fopen($pipePath, 'r');
$result = fread($in, 8192);
fclose($in);
(Я упростил второй подход; в реальном коде я бы проверил на ошибки, запустите fread
в цикле, если результат> 8192 байт и т. д.)
Однако пока другой процесс должен В завершение, я не верю, что это будет успешным, поэтому я хочу иметь тайм-аут при попытке прочитать результат. После некоторого ожидания я хочу сдаться и сообщить об ошибке, сообщив, что произошел сбой, и т. Д. При двух указанных подходах код PHP будет зависать вечно (или для очень давно) жду что нибудь написать в трубу. В частности, file_get_contents
а также fread
будет висеть
Единственное решение, которое мне удалось найти, это что-то вроде этого:
$timeout = 10; //seconds
for ($i = 0; $i < $timeout; $i++) {
$in = @fopen($pipePath, 'rn');
if ($in) break;
sleep(1);
}
if (!$in) {
throw new RuntimeException("The other process did not finish in the allotted time");
}
$result = fread($in, 8192);
fclose($in);
Это использует недокументированное 'n'
флаг для fopen
как показано в одном из комментариев на этот вопрос. Это вызывает fopen
вызов немедленно прервется, если он заблокируется.
Однако мне не нравится это решение по двум причинам:
когда fopen
вызывается по URL, у меня есть возможность добавить параметр контекста, который указывает значение времени ожидания. Тем не менее, это, похоже, не работает для этого и не устанавливает время ожидания сокета по умолчанию.
Есть ли лучший способ сделать это с трубами? Если нет, я мог бы рассмотреть возможность перехода на сокеты Unix, но их не так просто поддерживать в другом процессе, поэтому я бы не стал этого делать.
(К вашему сведению, меня интересует только Linux; нет необходимости иметь что-то, что работает на Windows или что-то еще, в случае, если это имеет значение.)
Я нашел один способ сделать это …
Во-первых, я не знал о n
флаг, это была очень полезная информация!
Тем не менее, это не совсем верно, что функция не работает, если она будет блокировать. Он по-прежнему возвращает дескриптор файла. Мы можем использовать дескриптор файла и передать его stream_select
функция ожидания, когда данные станут доступны.
Что-то вроде этого:
$f=fopen("my.fifo","rn");
$r=array($f);
$w=array();
$x=array();
stream_select($r,$w,$x,10);
Этот код ждет 10 секунд, пока кто-то еще не напишет на другом конце fifo.
Других решений пока нет …