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);
fclose($in);
(I have simplified the second approach; in the real code I would check for errors, run fread
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, file_get_contents
and fread
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;
sleep(1);
}
if (!$in) {
throw new RuntimeException("The other process did not finish in the allotted time");
}
$result = fread($in, 8192);
fclose($in);
This uses the undocumented 'n'
flag to fopen
as shown in one of the comments on this question. It causes the fopen
call to fail immediately if it would block.
However, I don't like this solution for two reasons:
When fopen
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.)
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:
$f=fopen("my.fifo","rn");
$r=array($f);
$w=array();
$x=array();
stream_select($r,$w,$x,10);
This code waits for 10 seconds for someone else to write to the other end of the fifo.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With