Req #33781 [Opn]: add "stream_set_timeout" support for pipes

From: Date: Tue, 27 Dec 2022 18:10:39 +0000
Subject: Req #33781 [Opn]: add "stream_set_timeout" support for pipes
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-243260@lists.php.net to get a copy of this message
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)

« previous php.bugs (#243260) next »