Austin Austin - 1 year ago 109
PHP Question

How to set a timeout on fopen with named pipes

I am trying to do some IPC in PHP using named pipes. I have a named pipe created via

$pipePath = __DIR__ . '/pipe';
posix_mkfifo($pipePath, 0600);

There is another process that should write to that pipe after finishing some computation. I can wait for it to finish and read the result with something like:

$result = file_get_contents($pipePath);

or the more verbose

$in = fopen($pipePath, 'r');
$result = fread($in, 8192);

(I have simplified the second approach; in the real code I would check for errors, run
in a loop in case the result is > 8192 bytes, etc.)

However, while the other process should finish, I don't trust it to be successful, so I want to have a timeout on trying to read the result. After waiting for some time, I want to give up and report an error saying it crashed, etc. With the two approaches given, the PHP code will hang forever (or for a very long time) waiting for something to write to the pipe. Specifically,
will hang.

The only solution I was able to come up with is something like this:

$timeout = 10; //seconds
for ($i = 0; $i < $timeout; $i++) {
$in = @fopen($pipePath, 'rn');
if ($in) break;
if (!$in) {
throw new RuntimeException("The other process did not finish in the allotted time");
$result = fread($in, 8192);

This uses the undocumented
flag to
as shown in one of the comments on this question. It causes the
call to fail immediately if it would block.

However, I don't like this solution for two reasons:

  1. It does unnecessary work by checking the pipe every second.

  2. If the computation in the other process takes 1.01 seconds to complete, this will wait a full 2 seconds to get the result. For some things that I want to do, this is enough wasted time that it is worth trying to fix the issue.

is called on a URL, I have the ability to add a context parameter the specifies a timeout value. However, this doesn't seem to work for this, nor does setting the default socket timeout.

Is there a better way to do this with pipes? If not, I may consider switching to Unix sockets, but those are not so easy to support in the other process so I would rather not do this.

(FYI, I am only concerned with Linux; no need to have something that works on Windows or anything else, in case this matters.)

Answer Source

I found one way to do this...

First, I didn't know about the n flag, this was very useful info!

However, it is not exactly true that the function fails if it would block. It still returns a file handle. We can use the file handle and pass it to the stream_select function to wait for data to become available.

Something like this:


This code waits for 10 seconds for someone else to write to the other end of the fifo.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download