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:
- It does unnecessary work by checking the pipe every second.
- 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.
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.)