diff --git a/src/StreamSelectLoop.php b/src/StreamSelectLoop.php index af46ea23..9b678e7a 100644 --- a/src/StreamSelectLoop.php +++ b/src/StreamSelectLoop.php @@ -186,11 +186,11 @@ public function run() if ($timeout < 0) { $timeout = 0; } else { - /* - * round() needed to correct float error: - * https://github.com/reactphp/event-loop/issues/48 - */ - $timeout = round($timeout * self::MICROSECONDS_PER_SECOND); + // Convert float seconds to int microseconds. + // Ensure we do not exceed maximum integer size, which may + // cause the loop to tick once every ~35min on 32bit systems. + $timeout *= self::MICROSECONDS_PER_SECOND; + $timeout = $timeout > PHP_INT_MAX ? PHP_INT_MAX : (int)$timeout; } // The only possible event is stream activity, so wait forever ... @@ -213,6 +213,8 @@ public function stop() /** * Wait/check for stream activity, or until the next timer is due. + * + * @param integer|null $timeout Activity timeout in microseconds, or null to wait forever. */ private function waitForStreamActivity($timeout) { diff --git a/tests/AbstractLoopTest.php b/tests/AbstractLoopTest.php index 6e338cc6..7ef881a3 100644 --- a/tests/AbstractLoopTest.php +++ b/tests/AbstractLoopTest.php @@ -480,6 +480,23 @@ public function testSignalsKeepTheLoopRunningAndRemovingItStopsTheLoop() $this->assertRunFasterThan(1.6); } + public function testTimerIntervalCanBeFarInFuture() + { + // get only one part of the pair to ensure the other side will close immediately + list($stream) = $this->createSocketPair(); + + // start a timer very far in the future + $timer = $this->loop->addTimer(PHP_INT_MAX, function () { }); + + // remove stream and timer when the stream is readable (closes) + $this->loop->addReadStream($stream, function ($stream) use ($timer) { + $this->loop->removeReadStream($stream); + $this->loop->cancelTimer($timer); + }); + + $this->assertRunFasterThan($this->tickTimeout); + } + private function assertRunSlowerThan($minInterval) { $start = microtime(true); diff --git a/tests/StreamSelectLoopTest.php b/tests/StreamSelectLoopTest.php index 1c5cff98..87bbbe69 100644 --- a/tests/StreamSelectLoopTest.php +++ b/tests/StreamSelectLoopTest.php @@ -4,7 +4,6 @@ use React\EventLoop\LoopInterface; use React\EventLoop\StreamSelectLoop; -use React\EventLoop\Timer\Timer; class StreamSelectLoopTest extends AbstractLoopTest { @@ -144,36 +143,4 @@ protected function forkSendSignal($signal) die(); } } - - /** - * https://github.com/reactphp/event-loop/issues/48 - * - * Tests that timer with very small interval uses at least 1 microsecond - * timeout. - */ - public function testSmallTimerInterval() - { - /** @var StreamSelectLoop|\PHPUnit_Framework_MockObject_MockObject $loop */ - $loop = $this->getMockBuilder('React\EventLoop\StreamSelectLoop') - ->setMethods(['streamSelect']) - ->getMock(); - $loop - ->expects($this->at(0)) - ->method('streamSelect') - ->with([], [], 1); - $loop - ->expects($this->at(1)) - ->method('streamSelect') - ->with([], [], 0); - - $callsCount = 0; - $loop->addPeriodicTimer(Timer::MIN_INTERVAL, function() use (&$loop, &$callsCount) { - $callsCount++; - if ($callsCount == 2) { - $loop->stop(); - } - }); - - $loop->run(); - } }