2016-06-08 16:12:42 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace Amp\Loop;
|
|
|
|
|
2017-03-10 23:03:41 +01:00
|
|
|
use Amp\Coroutine;
|
|
|
|
use Amp\Promise;
|
2017-03-11 07:13:03 +01:00
|
|
|
use React\Promise\PromiseInterface as ReactPromise;
|
2017-03-15 17:12:49 +01:00
|
|
|
use function Amp\Promise\rethrow;
|
2016-06-08 16:12:42 +02:00
|
|
|
|
2017-03-12 11:21:44 +01:00
|
|
|
class EvDriver extends Driver {
|
2017-03-10 21:58:46 +01:00
|
|
|
/** @var \EvSignal[]|null */
|
|
|
|
private static $activeSignals;
|
2017-02-17 05:36:32 +01:00
|
|
|
/** @var \EvLoop */
|
2016-06-08 16:12:42 +02:00
|
|
|
private $handle;
|
2017-02-17 05:36:32 +01:00
|
|
|
/** @var \EvWatcher[] */
|
2016-06-08 16:12:42 +02:00
|
|
|
private $events = [];
|
2017-02-17 05:36:32 +01:00
|
|
|
/** @var callable */
|
2016-06-08 16:12:42 +02:00
|
|
|
private $ioCallback;
|
2017-02-17 05:36:32 +01:00
|
|
|
/** @var callable */
|
2016-06-08 16:12:42 +02:00
|
|
|
private $timerCallback;
|
2017-02-17 05:36:32 +01:00
|
|
|
/** @var callable */
|
2016-06-08 16:12:42 +02:00
|
|
|
private $signalCallback;
|
2017-02-17 05:36:32 +01:00
|
|
|
/** @var \EvSignal[] */
|
2017-01-06 00:32:03 +01:00
|
|
|
private $signals = [];
|
2017-03-10 19:19:32 +01:00
|
|
|
|
2016-06-08 16:12:42 +02:00
|
|
|
public function __construct() {
|
|
|
|
$this->handle = new \EvLoop;
|
2017-03-10 19:19:32 +01:00
|
|
|
|
2017-01-06 00:32:03 +01:00
|
|
|
if (self::$activeSignals === null) {
|
|
|
|
self::$activeSignals = &$this->signals;
|
|
|
|
}
|
2016-06-08 19:08:00 +02:00
|
|
|
|
2016-06-08 16:12:42 +02:00
|
|
|
$this->ioCallback = function (\EvIO $event) {
|
2017-04-18 16:38:16 +02:00
|
|
|
/** @var \Amp\Loop\Watcher $watcher */
|
2016-06-08 16:12:42 +02:00
|
|
|
$watcher = $event->data;
|
|
|
|
|
2017-03-14 06:20:05 +01:00
|
|
|
try {
|
|
|
|
$result = ($watcher->callback)($watcher->id, $watcher->value, $watcher->data);
|
2017-03-10 23:03:41 +01:00
|
|
|
|
2017-03-14 06:20:05 +01:00
|
|
|
if ($result === null) {
|
|
|
|
return;
|
|
|
|
}
|
2017-03-12 18:03:13 +01:00
|
|
|
|
2017-03-14 06:20:05 +01:00
|
|
|
if ($result instanceof \Generator) {
|
|
|
|
$result = new Coroutine($result);
|
|
|
|
}
|
2017-03-10 23:03:41 +01:00
|
|
|
|
2017-03-14 06:20:05 +01:00
|
|
|
if ($result instanceof Promise || $result instanceof ReactPromise) {
|
|
|
|
rethrow($result);
|
|
|
|
}
|
|
|
|
} catch (\Throwable $exception) {
|
|
|
|
$this->error($exception);
|
2017-03-10 23:03:41 +01:00
|
|
|
}
|
2016-06-08 16:12:42 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
$this->timerCallback = function (\EvTimer $event) {
|
2017-04-18 16:38:16 +02:00
|
|
|
/** @var \Amp\Loop\Watcher $watcher */
|
2016-06-08 16:12:42 +02:00
|
|
|
$watcher = $event->data;
|
|
|
|
|
|
|
|
if ($watcher->type & Watcher::DELAY) {
|
|
|
|
$this->cancel($watcher->id);
|
2017-05-23 20:17:34 +02:00
|
|
|
} elseif ($watcher->value === 0) {
|
2017-05-23 19:46:23 +02:00
|
|
|
// Disable and re-enable so it's not executed repeatedly in the same tick
|
|
|
|
// See https://github.com/amphp/amp/issues/131
|
|
|
|
$this->disable($watcher->id);
|
|
|
|
$this->enable($watcher->id);
|
|
|
|
}
|
|
|
|
|
2017-03-14 06:20:05 +01:00
|
|
|
try {
|
|
|
|
$result = ($watcher->callback)($watcher->id, $watcher->data);
|
2017-03-10 23:03:41 +01:00
|
|
|
|
2017-03-14 06:20:05 +01:00
|
|
|
if ($result === null) {
|
|
|
|
return;
|
|
|
|
}
|
2017-03-12 18:03:13 +01:00
|
|
|
|
2017-03-14 06:20:05 +01:00
|
|
|
if ($result instanceof \Generator) {
|
|
|
|
$result = new Coroutine($result);
|
|
|
|
}
|
2017-03-10 23:03:41 +01:00
|
|
|
|
2017-03-14 06:20:05 +01:00
|
|
|
if ($result instanceof Promise || $result instanceof ReactPromise) {
|
|
|
|
rethrow($result);
|
|
|
|
}
|
|
|
|
} catch (\Throwable $exception) {
|
|
|
|
$this->error($exception);
|
2017-03-10 23:03:41 +01:00
|
|
|
}
|
2016-06-08 16:12:42 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
$this->signalCallback = function (\EvSignal $event) {
|
2017-04-18 16:38:16 +02:00
|
|
|
/** @var \Amp\Loop\Watcher $watcher */
|
2016-06-08 16:12:42 +02:00
|
|
|
$watcher = $event->data;
|
|
|
|
|
2017-03-14 06:20:05 +01:00
|
|
|
try {
|
2017-03-15 08:33:04 +01:00
|
|
|
$result = ($watcher->callback)($watcher->id, $watcher->value, $watcher->data);
|
|
|
|
|
2017-03-14 06:20:05 +01:00
|
|
|
if ($result === null) {
|
|
|
|
return;
|
|
|
|
}
|
2017-03-12 18:03:13 +01:00
|
|
|
|
2017-03-14 06:20:05 +01:00
|
|
|
if ($result instanceof \Generator) {
|
|
|
|
$result = new Coroutine($result);
|
|
|
|
}
|
2017-03-10 23:03:41 +01:00
|
|
|
|
2017-03-14 06:20:05 +01:00
|
|
|
if ($result instanceof Promise || $result instanceof ReactPromise) {
|
|
|
|
rethrow($result);
|
|
|
|
}
|
|
|
|
} catch (\Throwable $exception) {
|
|
|
|
$this->error($exception);
|
2017-03-10 23:03:41 +01:00
|
|
|
}
|
2016-06-08 16:12:42 +02:00
|
|
|
};
|
|
|
|
}
|
2017-01-06 00:38:33 +01:00
|
|
|
|
2017-03-10 21:58:46 +01:00
|
|
|
/**
|
|
|
|
* {@inheritdoc}
|
|
|
|
*/
|
|
|
|
public function cancel(string $watcherId) {
|
|
|
|
parent::cancel($watcherId);
|
|
|
|
unset($this->events[$watcherId]);
|
|
|
|
}
|
|
|
|
|
2017-03-12 11:25:21 +01:00
|
|
|
public static function isSupported(): bool {
|
2017-03-10 21:58:46 +01:00
|
|
|
return \extension_loaded("ev");
|
|
|
|
}
|
|
|
|
|
2017-01-06 00:38:33 +01:00
|
|
|
public function __destruct() {
|
|
|
|
foreach ($this->events as $event) {
|
|
|
|
$event->stop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-06 00:32:03 +01:00
|
|
|
/**
|
|
|
|
* {@inheritdoc}
|
|
|
|
*/
|
|
|
|
public function run() {
|
|
|
|
$active = self::$activeSignals;
|
2017-03-10 19:19:32 +01:00
|
|
|
|
2017-01-06 00:32:03 +01:00
|
|
|
foreach ($active as $event) {
|
|
|
|
$event->stop();
|
|
|
|
}
|
2017-03-10 19:19:32 +01:00
|
|
|
|
2017-01-06 00:32:03 +01:00
|
|
|
self::$activeSignals = &$this->signals;
|
2017-03-10 19:19:32 +01:00
|
|
|
|
2017-01-06 00:32:03 +01:00
|
|
|
foreach ($this->signals as $event) {
|
|
|
|
$event->start();
|
|
|
|
}
|
2017-03-10 19:19:32 +01:00
|
|
|
|
2017-01-06 00:32:03 +01:00
|
|
|
try {
|
|
|
|
parent::run();
|
|
|
|
} finally {
|
|
|
|
foreach ($this->signals as $event) {
|
|
|
|
$event->stop();
|
|
|
|
}
|
2017-03-10 19:19:32 +01:00
|
|
|
|
2017-01-06 00:32:03 +01:00
|
|
|
self::$activeSignals = &$active;
|
2017-03-10 19:19:32 +01:00
|
|
|
|
2017-01-06 00:32:03 +01:00
|
|
|
foreach ($active as $event) {
|
|
|
|
$event->start();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-06-08 16:12:42 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* {@inheritdoc}
|
|
|
|
*/
|
|
|
|
public function stop() {
|
|
|
|
$this->handle->stop();
|
|
|
|
parent::stop();
|
|
|
|
}
|
2016-06-08 23:03:37 +02:00
|
|
|
|
2017-03-10 21:58:46 +01:00
|
|
|
/**
|
|
|
|
* {@inheritdoc}
|
|
|
|
*/
|
2017-03-10 22:46:12 +01:00
|
|
|
public function getHandle(): \EvLoop {
|
2017-03-10 21:58:46 +01:00
|
|
|
return $this->handle;
|
|
|
|
}
|
|
|
|
|
2016-06-08 19:08:00 +02:00
|
|
|
/**
|
|
|
|
* {@inheritdoc}
|
|
|
|
*/
|
2017-03-10 22:46:12 +01:00
|
|
|
protected function dispatch(bool $blocking) {
|
2016-06-08 16:12:42 +02:00
|
|
|
$this->handle->run($blocking ? \Ev::RUN_ONCE : \Ev::RUN_ONCE | \Ev::RUN_NOWAIT);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* {@inheritdoc}
|
|
|
|
*/
|
|
|
|
protected function activate(array $watchers) {
|
|
|
|
foreach ($watchers as $watcher) {
|
|
|
|
if (!isset($this->events[$id = $watcher->id])) {
|
|
|
|
switch ($watcher->type) {
|
|
|
|
case Watcher::READABLE:
|
|
|
|
$this->events[$id] = $this->handle->io($watcher->value, \Ev::READ, $this->ioCallback, $watcher);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Watcher::WRITABLE:
|
|
|
|
$this->events[$id] = $this->handle->io($watcher->value, \Ev::WRITE, $this->ioCallback, $watcher);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Watcher::DELAY:
|
|
|
|
case Watcher::REPEAT:
|
|
|
|
$interval = $watcher->value / self::MILLISEC_PER_SEC;
|
|
|
|
$this->events[$id] = $this->handle->timer(
|
|
|
|
$interval,
|
2016-06-08 19:08:00 +02:00
|
|
|
$watcher->type & Watcher::REPEAT ? $interval : 0,
|
2016-06-08 16:12:42 +02:00
|
|
|
$this->timerCallback,
|
|
|
|
$watcher
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Watcher::SIGNAL:
|
|
|
|
$this->events[$id] = $this->handle->signal($watcher->value, $this->signalCallback, $watcher);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2017-03-14 17:44:19 +01:00
|
|
|
// @codeCoverageIgnoreStart
|
|
|
|
throw new \Error("Unknown watcher type");
|
|
|
|
// @codeCoverageIgnoreEnd
|
2016-06-08 16:12:42 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$this->events[$id]->start();
|
|
|
|
}
|
2017-01-06 00:32:03 +01:00
|
|
|
|
|
|
|
if ($watcher->type === Watcher::SIGNAL) {
|
|
|
|
$this->signals[$id] = $this->events[$id];
|
|
|
|
}
|
2016-06-08 16:12:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* {@inheritdoc}
|
|
|
|
*/
|
2017-01-05 19:39:10 +01:00
|
|
|
protected function deactivate(Watcher $watcher) {
|
2016-06-08 16:12:42 +02:00
|
|
|
if (isset($this->events[$id = $watcher->id])) {
|
|
|
|
$this->events[$id]->stop();
|
2017-01-06 00:32:03 +01:00
|
|
|
if ($watcher->type === Watcher::SIGNAL) {
|
|
|
|
unset($this->signals[$id]);
|
|
|
|
}
|
2016-06-08 16:12:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|