-
-
Notifications
You must be signed in to change notification settings - Fork 132
StreamSelectLoop: Int overflow for big timer intervals #96
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
StreamSelectLoop: Int overflow for big timer intervals #96
Conversation
31a613a
to
a4bf806
Compare
{ | ||
if ($seconds > 0 || $nanoseconds > 0) { | ||
time_nanosleep($seconds, $nanoseconds); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to see explicit handling of $seconds
or $nanoseconds
being null
here.
src/StreamSelectLoop.php
Outdated
return PHP_INT_MAX; | ||
} | ||
|
||
return $time === null ? null : intval(floor($time)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor nitpicking: for consistency with the other 2 methods, i'd like to an early return for $time === null
:
if ($time === null) {
return null;
}
// ...
return intval(floor($time));
a4bf806
to
08c709b
Compare
src/StreamSelectLoop.php
Outdated
private static function getMicroseconds($time) | ||
{ | ||
if ($time === null) { | ||
return null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should probably return 0
. stream_select
accepts null
only for the $seconds
parameter.
src/StreamSelectLoop.php
Outdated
private static function getNanoseconds($time) | ||
{ | ||
if ($time === null) { | ||
return null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should probably return 0
, see above.
src/StreamSelectLoop.php
Outdated
* @param array $write | ||
* @param array $except | ||
* @param int|null $seconds | ||
* @param int|null $microseconds |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should only accept int
, see also above.
@jsor, thank you for the review. I think that previous implementation was not explicit, cause |
c78624d
to
4347fa3
Compare
src/StreamSelectLoop.php
Outdated
* | ||
* @return integer|false The total number of streams that are ready for read/write. | ||
* Can return false if stream_select() is interrupted by a signal. | ||
*/ | ||
protected function streamSelect(array &$read, array &$write, $timeout) | ||
{ | ||
$seconds = $timeout === null ? null : self::getSeconds($timeout); | ||
$microseconds = $timeout === null ? 0 : self::getMicroseconds($timeout); | ||
$nanoseconds = $timeout === null ? 0 : self::getNanoseconds($timeout); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Calculation of $microseconds
and $nanoseconds
can probably be moved into the if
s so they are only calculated when needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
src/StreamSelectLoop.php
Outdated
* (int)(float)PHP_INT_MAX == PHP_INT_MAX => false | ||
* (int)(float)PHP_INT_MAX == PHP_INT_MIN => true | ||
*/ | ||
if ($time == PHP_INT_MAX) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Strict comparision (===
)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jsor, $time
is float, so strict comparison will always return false
.
(float) PHP_INT_MAX === PHP_INT_MAX; //is false
(float) PHP_INT_MAX == PHP_INT_MAX; //is true
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 (maybe add a comment that loose comparision is intentional)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
ping @clue |
@clue, can you please describe, why did you mark this PR as BC Break? Imo, only internal representation of time changed and no public interfaces modified... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for filing this PR! These are some high quality changes and I'd actually like to get this in to solve this rare issue 👍
The issue only affects 32 bit systems, yet this PR proposes some major changes to the whole StreamSelectLoop
, which contains some very performance-critical code.
Does it make sense to perform some benchmarks to check its impact?
* Emulate a stream_select() implementation that does not break when passed | ||
* empty stream arrays. | ||
* | ||
* @param array &$read An array of read streams to select upon. | ||
* @param array &$write An array of write streams to select upon. | ||
* @param integer|null $timeout Activity timeout in microseconds, or null to wait forever. | ||
* @param float|null $timeout Activity timeout in seconds, or null to wait forever. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a rather subtle BC break as consumers could possibly rely on this protected
API. Not saying this is an issue with your proposed change, it's more that we screwed up could have done better with defining our external API.
The problem
As described here, current
StreamSelectLoop
implementation restricts using time intervals more thanPHP_INT_MAX
microseconds. For 32-bit systems it's only 2148 seconds.Suggested solution
StreamSelectLoop
$timeout
type fromint
(in microseconds) tofloat
(in seconds);usleep
withtime_nanosleep
, causeusleep
takesint
argument in microseconds, and we can't keep more thanPHP_INT_MAX
microseconds. Buttime_nanosleep
takes 2 arguments:int
amount of seconds andint
amount of nanoseconds, that allowsStreamSelectLoop
to handlePHP_INT_MAX
seconds intervals;stream_select
andtime_nanosleep
methods calls fromStreamSelectLoop::streamSelect(...)
to test timeout values.