Req #33781 [Opn]: add "stream_set_timeout" support for pipes
Edit report at https://bugs.php.net/bug.php?id=33781&edit=1
ID: 33781
Updated by: bukka@php.net
Reported by: vesely at tana dot it
Summary: add "stream_set_timeout" support for pipes
Status: Open
Type: Feature/Change Request
Package: Streams related
Operating System: *
PHP Version: *
-Assigned To:
+Assigned To: bukka
Block user comment: N
Private report: N
New Comment:
For the record I just created an issue that should address this problem once implemented: https://github.com/php/php-src/issues/10171 .
Previous Comments:
------------------------------------------------------------------------
[2005-07-21 08:55:03] vesely at tana dot it
I have a couple of notes on that example. The function
attempts to fread 8Kbytes, whilst a positive response from
stream_select only guarantees that 1 byte can be read w/o
blocking. Further, 60 secs is really eons.
I'm using this:
function guarded_fgets($pipes)
{
$line = "";
$time = 2;
$c = "c";
while ($time >= 0 && ord($c) != 10)
{
$read = array($pipes[1]);
while (!feof($read[0]) &&
($n = stream_select($read, $w = NULL, $e = NULL, $time)) !== FALSE &&
$n > 0 &&
strlen($c = fgetc($read[0])) > 0 &&
ord($c) != 10)
$line .= $c;
--$time;
}
if (ord($c) != 10)
{
// error handling
}
return $line;
}
------------------------------------------------------------------------
[2005-07-20 14:54:54] wez@php.net
The simple solution is to NOT use fgets() when you're on a deadline, because of the greedy read
semantic.
Use fread() instead, in conjunction with stream_select() (which is what is used internally to
implement the timeout).
Here's an example from our test suite:
function system_with_timeout($commandline)
{
$data = "";
$proc = proc_open($commandline, array(
0 => array('pipe', 'r'),
1 => array('pipe', 'w'),
2 => array('pipe', 'w')
), $pipes, null, null, array("suppress_errors" => true));
if (!$proc)
return false;
fclose($pipes[0]);
while (true) {
/* hide errors from interrupted syscalls */
$r = $pipes;
$w = null;
$e = null;
$n = @stream_select($r, $w, $e, 60);
if ($n === 0) {
/* timed out */
$data .= "\n ** ERROR: process timed out **\n";
proc_terminate($proc);
return $data;
} else if ($n > 0) {
$line = fread($pipes[1], 8192);
if (strlen($line) == 0) {
/* EOF */
break;
}
$data .= $line;
}
}
$stat = proc_get_status($proc);
if ($stat['signaled']) {
$data .= "\nTermsig=".$stat['stopsig'];
}
$code = proc_close($proc);
return $data;
}
------------------------------------------------------------------------
[2005-07-20 09:40:21] vesely at tana dot it
Description:
------------
When using the Apache module it is not possible
to use pcntl_alarm (why?). OTOH stream_set_timeout
does not work on the "pipe" in descriptorspec.
Of course, if the program really doesn't know _if_
there is any data to read, it should use stream_select
and fgetc in a loop. Frequently enough, the program
knows what it is doing and wants to sleep on fgets
until data is available on the pipe. If anything goes
wrong here, the two processes may hang waiting for
each other. Meanwhile, the user angrily reloads the
page knocking out yet another Apache's child...
"NEEDED: a timeout for stdout pipe, otherwise a fgets on
$pipes[1] can lag forever...)" has been for years in
http://www.php.net/manual/en/function.proc-open.php#20866
The global script timeout is quite hard to manage from
an included function. stream_select and fgets is good
but is not bullet-proof. A request for a timer already
exists (bug #9676), hence I guess I should ask for some
"guarded-pipe" where the select-fgetc loop is coded in C
using a time limit that can be set via stream_set_timeout.
Is that cool?
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=33781&edit=1
Thread (4 messages)