mirror of
https://github.com/danog/amp.git
synced 2024-11-30 04:29:08 +01:00
Add Loop::now()
This commit is contained in:
parent
96c2eeaa1f
commit
77a12c823b
12
lib/Loop.php
12
lib/Loop.php
@ -332,6 +332,18 @@ final class Loop
|
|||||||
self::$driver->unreference($watcherId);
|
self::$driver->unreference($watcherId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current loop time in millisecond increments. Note this value does not necessarily correlate to
|
||||||
|
* wall-clock time, rather the value returned is meant to be used in relative comparisons to prior values returned
|
||||||
|
* by this method (intervals, expiration calculations, etc.) and is only updated once per loop tick.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public static function now(): int
|
||||||
|
{
|
||||||
|
return self::$driver->now();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores information in the loop bound registry.
|
* Stores information in the loop bound registry.
|
||||||
*
|
*
|
||||||
|
@ -590,6 +590,20 @@ abstract class Driver
|
|||||||
($this->errorHandler)($exception);
|
($this->errorHandler)($exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current loop time in millisecond increments. Note this value does not necessarily correlate to
|
||||||
|
* wall-clock time, rather the value returned is meant to be used in relative comparisons to prior values returned
|
||||||
|
* by this method (intervals, expiration calculations, etc.) and is only updated once per loop tick.
|
||||||
|
*
|
||||||
|
* Extending classes should override this function to return a value cached once per loop tick.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function now(): int
|
||||||
|
{
|
||||||
|
return \microtime(true) * self::MILLISEC_PER_SEC;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the underlying loop handle.
|
* Get the underlying loop handle.
|
||||||
*
|
*
|
||||||
|
@ -11,22 +11,38 @@ class EvDriver extends Driver
|
|||||||
{
|
{
|
||||||
/** @var \EvSignal[]|null */
|
/** @var \EvSignal[]|null */
|
||||||
private static $activeSignals;
|
private static $activeSignals;
|
||||||
|
|
||||||
/** @var \EvLoop */
|
/** @var \EvLoop */
|
||||||
private $handle;
|
private $handle;
|
||||||
|
|
||||||
/** @var \EvWatcher[] */
|
/** @var \EvWatcher[] */
|
||||||
private $events = [];
|
private $events = [];
|
||||||
|
|
||||||
/** @var callable */
|
/** @var callable */
|
||||||
private $ioCallback;
|
private $ioCallback;
|
||||||
|
|
||||||
/** @var callable */
|
/** @var callable */
|
||||||
private $timerCallback;
|
private $timerCallback;
|
||||||
|
|
||||||
/** @var callable */
|
/** @var callable */
|
||||||
private $signalCallback;
|
private $signalCallback;
|
||||||
|
|
||||||
/** @var \EvSignal[] */
|
/** @var \EvSignal[] */
|
||||||
private $signals = [];
|
private $signals = [];
|
||||||
|
|
||||||
|
/** @var int Internal timestamp for now. */
|
||||||
|
private $now = 0;
|
||||||
|
|
||||||
|
/** @var bool */
|
||||||
|
private $nowUpdateNeeded = false;
|
||||||
|
|
||||||
|
/** @var int Loop time offset from microtime() */
|
||||||
|
private $nowOffset;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->handle = new \EvLoop;
|
$this->handle = new \EvLoop;
|
||||||
|
$this->nowOffset = (int) (\microtime(true) * self::MILLISEC_PER_SEC);
|
||||||
|
|
||||||
if (self::$activeSignals === null) {
|
if (self::$activeSignals === null) {
|
||||||
self::$activeSignals = &$this->signals;
|
self::$activeSignals = &$this->signals;
|
||||||
@ -179,6 +195,19 @@ class EvDriver extends Driver
|
|||||||
parent::stop();
|
parent::stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function now(): int
|
||||||
|
{
|
||||||
|
if ($this->nowUpdateNeeded) {
|
||||||
|
$this->now = (int) (\microtime(true) * self::MILLISEC_PER_SEC) - $this->nowOffset;
|
||||||
|
$this->nowUpdateNeeded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->now;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
@ -192,6 +221,7 @@ class EvDriver extends Driver
|
|||||||
*/
|
*/
|
||||||
protected function dispatch(bool $blocking)
|
protected function dispatch(bool $blocking)
|
||||||
{
|
{
|
||||||
|
$this->nowUpdateNeeded = true;
|
||||||
$this->handle->run($blocking ? \Ev::RUN_ONCE : \Ev::RUN_ONCE | \Ev::RUN_NOWAIT);
|
$this->handle->run($blocking ? \Ev::RUN_ONCE : \Ev::RUN_ONCE | \Ev::RUN_NOWAIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,25 +11,38 @@ class EventDriver extends Driver
|
|||||||
{
|
{
|
||||||
/** @var \Event[]|null */
|
/** @var \Event[]|null */
|
||||||
private static $activeSignals;
|
private static $activeSignals;
|
||||||
|
|
||||||
/** @var \EventBase */
|
/** @var \EventBase */
|
||||||
private $handle;
|
private $handle;
|
||||||
|
|
||||||
/** @var \Event[] */
|
/** @var \Event[] */
|
||||||
private $events = [];
|
private $events = [];
|
||||||
|
|
||||||
/** @var callable */
|
/** @var callable */
|
||||||
private $ioCallback;
|
private $ioCallback;
|
||||||
|
|
||||||
/** @var callable */
|
/** @var callable */
|
||||||
private $timerCallback;
|
private $timerCallback;
|
||||||
|
|
||||||
/** @var callable */
|
/** @var callable */
|
||||||
private $signalCallback;
|
private $signalCallback;
|
||||||
|
|
||||||
/** @var \Event[] */
|
/** @var \Event[] */
|
||||||
private $signals = [];
|
private $signals = [];
|
||||||
|
|
||||||
|
/** @var bool */
|
||||||
|
private $nowUpdateNeeded = false;
|
||||||
|
|
||||||
/** @var int Internal timestamp for now. */
|
/** @var int Internal timestamp for now. */
|
||||||
private $now;
|
private $now = 0;
|
||||||
|
|
||||||
|
/** @var int Loop time offset from microtime() */
|
||||||
|
private $nowOffset;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->handle = new \EventBase;
|
$this->handle = new \EventBase;
|
||||||
$this->now = (int) (\microtime(true) * self::MILLISEC_PER_SEC);
|
$this->nowOffset = (int) (\microtime(true) * self::MILLISEC_PER_SEC);
|
||||||
|
|
||||||
if (self::$activeSignals === null) {
|
if (self::$activeSignals === null) {
|
||||||
self::$activeSignals = &$this->signals;
|
self::$activeSignals = &$this->signals;
|
||||||
@ -184,6 +197,19 @@ class EventDriver extends Driver
|
|||||||
parent::stop();
|
parent::stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function now(): int
|
||||||
|
{
|
||||||
|
if ($this->nowUpdateNeeded) {
|
||||||
|
$this->now = (int) (\microtime(true) * self::MILLISEC_PER_SEC) - $this->nowOffset;
|
||||||
|
$this->nowUpdateNeeded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->now;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
@ -197,8 +223,8 @@ class EventDriver extends Driver
|
|||||||
*/
|
*/
|
||||||
protected function dispatch(bool $blocking)
|
protected function dispatch(bool $blocking)
|
||||||
{
|
{
|
||||||
|
$this->nowUpdateNeeded = true;
|
||||||
$this->handle->loop($blocking ? \EventBase::LOOP_ONCE : \EventBase::LOOP_ONCE | \EventBase::LOOP_NONBLOCK);
|
$this->handle->loop($blocking ? \EventBase::LOOP_ONCE : \EventBase::LOOP_ONCE | \EventBase::LOOP_NONBLOCK);
|
||||||
$this->now = (int) (\microtime(true) * self::MILLISEC_PER_SEC);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -206,7 +232,7 @@ class EventDriver extends Driver
|
|||||||
*/
|
*/
|
||||||
protected function activate(array $watchers)
|
protected function activate(array $watchers)
|
||||||
{
|
{
|
||||||
$now = (int) (\microtime(true) * self::MILLISEC_PER_SEC);
|
$now = (int) (\microtime(true) * self::MILLISEC_PER_SEC) - $this->nowOffset;
|
||||||
|
|
||||||
foreach ($watchers as $watcher) {
|
foreach ($watchers as $watcher) {
|
||||||
if (!isset($this->events[$id = $watcher->id])) {
|
if (!isset($this->events[$id = $watcher->id])) {
|
||||||
@ -262,7 +288,7 @@ class EventDriver extends Driver
|
|||||||
switch ($watcher->type) {
|
switch ($watcher->type) {
|
||||||
case Watcher::DELAY:
|
case Watcher::DELAY:
|
||||||
case Watcher::REPEAT:
|
case Watcher::REPEAT:
|
||||||
$interval = $watcher->value - ($now - $this->now);
|
$interval = $watcher->value - ($now - $this->now());
|
||||||
$this->events[$id]->add($interval > 0 ? $interval / self::MILLISEC_PER_SEC : 0);
|
$this->events[$id]->add($interval > 0 ? $interval / self::MILLISEC_PER_SEC : 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -30,8 +30,14 @@ class NativeDriver extends Driver
|
|||||||
/** @var \Amp\Loop\Watcher[][] */
|
/** @var \Amp\Loop\Watcher[][] */
|
||||||
private $signalWatchers = [];
|
private $signalWatchers = [];
|
||||||
|
|
||||||
|
/** @var bool */
|
||||||
|
private $nowUpdateNeeded = false;
|
||||||
|
|
||||||
/** @var int Internal timestamp for now. */
|
/** @var int Internal timestamp for now. */
|
||||||
private $now;
|
private $now = 0;
|
||||||
|
|
||||||
|
/** @var int Loop time offset from microtime() */
|
||||||
|
private $nowOffset;
|
||||||
|
|
||||||
/** @var bool */
|
/** @var bool */
|
||||||
private $signalHandling;
|
private $signalHandling;
|
||||||
@ -40,7 +46,7 @@ class NativeDriver extends Driver
|
|||||||
{
|
{
|
||||||
$this->timerQueue = new \SplPriorityQueue();
|
$this->timerQueue = new \SplPriorityQueue();
|
||||||
$this->signalHandling = \extension_loaded("pcntl");
|
$this->signalHandling = \extension_loaded("pcntl");
|
||||||
$this->now = (int) (\microtime(true) * self::MILLISEC_PER_SEC);
|
$this->nowOffset = (int) (\microtime(true) * self::MILLISEC_PER_SEC);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -57,6 +63,19 @@ class NativeDriver extends Driver
|
|||||||
return parent::onSignal($signo, $callback, $data);
|
return parent::onSignal($signo, $callback, $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function now(): int
|
||||||
|
{
|
||||||
|
if ($this->nowUpdateNeeded) {
|
||||||
|
$this->now = (int) (\microtime(true) * self::MILLISEC_PER_SEC) - $this->nowOffset;
|
||||||
|
$this->nowUpdateNeeded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->now;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
@ -67,14 +86,14 @@ class NativeDriver extends Driver
|
|||||||
|
|
||||||
protected function dispatch(bool $blocking)
|
protected function dispatch(bool $blocking)
|
||||||
{
|
{
|
||||||
|
$this->nowUpdateNeeded = true;
|
||||||
|
|
||||||
$this->selectStreams(
|
$this->selectStreams(
|
||||||
$this->readStreams,
|
$this->readStreams,
|
||||||
$this->writeStreams,
|
$this->writeStreams,
|
||||||
$blocking ? $this->getTimeout() : 0
|
$blocking ? $this->getTimeout() : 0
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->now = (int) (\microtime(true) * self::MILLISEC_PER_SEC);
|
|
||||||
|
|
||||||
if (!empty($this->timerExpires)) {
|
if (!empty($this->timerExpires)) {
|
||||||
$scheduleQueue = [];
|
$scheduleQueue = [];
|
||||||
|
|
||||||
@ -89,14 +108,14 @@ class NativeDriver extends Driver
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->timerExpires[$id] > $this->now) { // Timer at top of queue has not expired.
|
if ($this->timerExpires[$id] > $this->now()) { // Timer at top of queue has not expired.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->timerQueue->extract();
|
$this->timerQueue->extract();
|
||||||
|
|
||||||
if ($watcher->type & Watcher::REPEAT) {
|
if ($watcher->type & Watcher::REPEAT) {
|
||||||
$expiration = $this->now + $watcher->value;
|
$expiration = $this->now() + $watcher->value;
|
||||||
$this->timerExpires[$watcher->id] = $expiration;
|
$this->timerExpires[$watcher->id] = $expiration;
|
||||||
$scheduleQueue[] = [$watcher, $expiration];
|
$scheduleQueue[] = [$watcher, $expiration];
|
||||||
} else {
|
} else {
|
||||||
@ -283,7 +302,7 @@ class NativeDriver extends Driver
|
|||||||
|
|
||||||
case Watcher::DELAY:
|
case Watcher::DELAY:
|
||||||
case Watcher::REPEAT:
|
case Watcher::REPEAT:
|
||||||
$expiration = $this->now + $watcher->value;
|
$expiration = $this->now() + $watcher->value;
|
||||||
$this->timerExpires[$watcher->id] = $expiration;
|
$this->timerExpires[$watcher->id] = $expiration;
|
||||||
$this->timerQueue->insert([$watcher, $expiration], -$expiration);
|
$this->timerQueue->insert([$watcher, $expiration], -$expiration);
|
||||||
break;
|
break;
|
||||||
|
@ -166,6 +166,14 @@ class UvDriver extends Driver
|
|||||||
return \extension_loaded("uv");
|
return \extension_loaded("uv");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function now(): int
|
||||||
|
{
|
||||||
|
return \uv_now($this->handle);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
@ -1555,6 +1555,23 @@ abstract class DriverTest extends TestCase
|
|||||||
$this->assertNotSame(0, $j);
|
$this->assertNotSame(0, $j);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testNow()
|
||||||
|
{
|
||||||
|
$now = $this->loop->now();
|
||||||
|
$this->loop->delay(100, function () use ($now) {
|
||||||
|
$now += 100;
|
||||||
|
$new = $this->loop->now();
|
||||||
|
|
||||||
|
// Allow a few milliseconds of inaccuracy.
|
||||||
|
$this->assertGreaterThanOrEqual($now - 5, $new);
|
||||||
|
$this->assertLessThanOrEqual($now + 5, $new);
|
||||||
|
|
||||||
|
// Same time should be returned from later call.
|
||||||
|
$this->assertSame($new, $this->loop->now());
|
||||||
|
});
|
||||||
|
$this->loop->run();
|
||||||
|
}
|
||||||
|
|
||||||
public function testBug163ConsecutiveDelayed()
|
public function testBug163ConsecutiveDelayed()
|
||||||
{
|
{
|
||||||
$emits = 3;
|
$emits = 3;
|
||||||
|
@ -46,12 +46,30 @@ class LoopTest extends TestCase
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testNow()
|
||||||
|
{
|
||||||
|
Loop::run(function () {
|
||||||
|
$now = Loop::now();
|
||||||
|
Loop::delay(100, function () use ($now) {
|
||||||
|
$now += 100;
|
||||||
|
$new = Loop::now();
|
||||||
|
|
||||||
|
// Allow a few milliseconds of inaccuracy.
|
||||||
|
$this->assertGreaterThanOrEqual($now - 5, $new);
|
||||||
|
$this->assertLessThanOrEqual($now + 5, $new);
|
||||||
|
|
||||||
|
// Same time should be returned from later call.
|
||||||
|
$this->assertSame($new, Loop::now());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public function testGet()
|
public function testGet()
|
||||||
{
|
{
|
||||||
$this->assertInstanceOf(Loop\Driver::class, Loop::get());
|
$this->assertInstanceOf(Loop\Driver::class, Loop::get());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetInto()
|
public function testGetInfo()
|
||||||
{
|
{
|
||||||
$this->assertSame(Loop::get()->getInfo(), Loop::getInfo());
|
$this->assertSame(Loop::get()->getInfo(), Loop::getInfo());
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user