-
-
Notifications
You must be signed in to change notification settings - Fork 132
Closed
Description
Within StreamSelectLoop::run()
is the following code:
} elseif ($scheduledAt = $this->timers->getFirst()) { // float
$timeout = $scheduledAt - $this->timers->getTime(); // float - float
if ($timeout < 0) {
$timeout = 0;
} else {
$timeout *= self::MICROSECONDS_PER_SECOND; // float * int
}
}
In the case of a periodic timer with an interval equal to Timer::MIN_INTERVAL
(0.000001), the above code, in conjunction with the Timers
class, essentially does something like the following:
$currentTime = microtime(true);
$ourTimerInterval = 0.000001;
$nextTimerScheduledAt = $currentTime + $ourTimerInterval;
// $timeout is for stream_select()/usleep() call
$timeout = $nextTimerScheduledAt - $currentTime; // This is NOT equal to 0.000001 due to float error [1]
$timeout *= 1000000; // This looks like it should be (int)1, but it is actually approximately (float)0.95
$timeout
is then passed to stream_select()
and usleep()
, both of which expect int
and so cast (float)0.95
to (int)0
, not waiting at all when the timer was intended to delay for 1 microsecond. Furthermore, the fix in #37 is bypassed when this issue occurs as (float)0.95
casts to boolean true
.
A quick fix would be to apply rounding to $timeout *= self::MICROSECONDS_PER_SECOND;
.