-
-
Notifications
You must be signed in to change notification settings - Fork 131
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
| protected function sleep($seconds, $nanoseconds = 0) | ||
| { | ||
| 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 $secondsor $nanosecondsbeing null here.
| if ($time == PHP_INT_MAX) { | ||
| return 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.
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) { |
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) { |
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.
| * @param array $read | ||
| * @param array $write | ||
| * @param array $except | ||
| * @param int|null $seconds |
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
| protected function streamSelect(array &$read, array &$write, $timeout) | ||
| { | ||
| $seconds = $timeout === null ? null : self::getSeconds($timeout); | ||
| $microseconds = $timeout === null ? 0 : self::getMicroseconds($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 ifs 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.
| * (float)PHP_INT_MAX == PHP_INT_MAX => true | ||
| * (int)(float)PHP_INT_MAX == PHP_INT_MAX => false | ||
| * (int)(float)PHP_INT_MAX == PHP_INT_MIN => 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.
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 trueThere 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.
jsor
left a comment
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... |
clue
left a comment
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?
| * | ||
| * @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. |
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
StreamSelectLoopimplementation restricts using time intervals more thanPHP_INT_MAXmicroseconds. For 32-bit systems it's only 2148 seconds.Suggested solution
StreamSelectLoop$timeouttype fromint(in microseconds) tofloat(in seconds);usleepwithtime_nanosleep, causeusleeptakesintargument in microseconds, and we can't keep more thanPHP_INT_MAXmicroseconds. Buttime_nanosleeptakes 2 arguments:intamount of seconds andintamount of nanoseconds, that allowsStreamSelectLoopto handlePHP_INT_MAXseconds intervals;stream_selectandtime_nanosleepmethods calls fromStreamSelectLoop::streamSelect(...)to test timeout values.