Skip to content

StreamSelectLoop: Timers with very small intervals do not work correctly#48

@joshdifabio

Description

@joshdifabio

Within StreamSelectLoop::run() is the following code:

} elseif ($scheduledAt = $this->timers->getFirst()){// float$timeout = $scheduledAt - $this->timers->getTime(); // float - floatif ($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;.

[1] https://3v4l.org/FvioX

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions