clue/reactphp-block

Busy Loop Discussion

CMCDragonkai opened this issue · 5 comments

Currently block relies on a while call to check if a flag has changed in order to implement async blocking. This may result in a busy loop wasting CPU cycles.

There are alternatives to doing this. One can use a flock and block on the lock, this makes use of the OS locking mechanism to prevent busy looping. Another way is to use one of the process control extensions: http://php.net/manual/en/refs.fileprocess.process.php

While the above 2 work. The former needs a lock file (which is not always readily available), the latter requires installing extensions which is not always available.

A third way is to use http://php.net/manual/en/function.stream-select.php however this ideally should be paired up with the unix pipe call. But there doesn't seem to be a PHP wrapper for the anonymous pipe call, a discussion was formed here: https://bugs.php.net/bug.php?id=60896

However an alternative is real sockets which is provided by native PHP: http://php.net/manual/en/function.socket-create-pair.php
and http://php.net/manual/en/function.socket-select.php

So combining the 2 together, we can create a non-busy loop for the async to sync conversion.

Here's an example:

$sockets = [];
socket_create_pair(AF_UNIX, SOCK_STREAM, 0, $sockets);
socket_write($sockets[1], 1, 1); // this one would be in the async then
$r = [$sockets[0]];
$w = null;
$e = null;
socket_select($r, $w, $e, null, null);
socket_close($sockets[0]);
socket_close($sockets[1]);

On my computer, it shows 0% CPU usage. I believe should work on all OSes. However the PHP docs doesn't mention whether AF_UNIX is available for Windows, so AF_INET can also be used. Also the docs doesn't seem to mention how the AF_UNIX socket is stored on the filesystem, it seems automatic.

If $loop->run(); blocks entirely, then the while loop shouldn't be a problem, in which case the above is unnecessary.

clue commented

Thanks for your in-depth analysis! 👍

Currently block relies on a while call to check if a flag has changed in order to implement async blocking. This may result in a busy loop wasting CPU cycles.

I may be missing something obvious, but the problem you're having appears a bit unclear to me, i.e. what are we actually tying to fix here and when does this happen? :-)

My tests show that loop->run() does in fact block so it's not necessary to perform the tricks I stated above.

clue commented

Yeah, this would be expected behavior 👍 I understand the code may suggest otherwise and we may want to look into clearing things up here.

Other than that, does this solve this issue or do you feel there's something that needs to be done here?