From b325a88cc8ddd14594fa6fbc92cc73f7bf97ae6f Mon Sep 17 00:00:00 2001 From: Ivan Kalita Date: Mon, 15 Jan 2018 23:48:11 +0300 Subject: [PATCH 1/4] Implement ExtEvLoop. ExtEvLoop implements event loop based on PECL ev extension. --- README.md | 10 ++ src/ExtEvLoop.php | 255 ++++++++++++++++++++++++++++++ src/Factory.php | 2 + tests/ExtEvLoopTest.php | 17 ++ tests/Timer/AbstractTimerTest.php | 4 + tests/Timer/ExtEvTimerTest.php | 17 ++ travis-init.sh | 3 + 7 files changed, 308 insertions(+) create mode 100644 src/ExtEvLoop.php create mode 100644 tests/ExtEvLoopTest.php create mode 100644 tests/Timer/ExtEvTimerTest.php diff --git a/README.md b/README.md index 7a30ec15..b2a47887 100644 --- a/README.md +++ b/README.md @@ -201,6 +201,16 @@ It supports the same backends as libevent. This loop is known to work with PHP 5.4 through PHP 7+. +#### ExtEvLoop + +An `ext-ev` based event loop. + +This loop uses the [`ev` PECL extension](https://pecl.php.net/package/ev), that +provides an interface to `libev` library. + +This loop is known to work with PHP 5.4 through PHP 7+. + + #### ExtLibeventLoop An `ext-libevent` based event loop. diff --git a/src/ExtEvLoop.php b/src/ExtEvLoop.php new file mode 100644 index 00000000..2be45c34 --- /dev/null +++ b/src/ExtEvLoop.php @@ -0,0 +1,255 @@ +loop = new EvLoop(); + $this->futureTickQueue = new FutureTickQueue(); + $this->timers = new SplObjectStorage(); + $this->signals = new SignalsHandler(); + } + + public function addReadStream($stream, $listener) + { + $key = (int)$stream; + + if (isset($this->readStreams[$key])) { + return; + } + + $callback = $this->getStreamListenerClosure($stream, $listener); + $event = $this->loop->io($stream, Ev::READ, $callback); + $this->readStreams[$key] = $event; + } + + /** + * @param resource $stream + * @param callable $listener + * + * @return \Closure + */ + private function getStreamListenerClosure($stream, $listener) + { + return function () use ($stream, $listener) { + call_user_func($listener, $stream); + }; + } + + public function addWriteStream($stream, $listener) + { + $key = (int)$stream; + + if (isset($this->writeStreams[$key])) { + return; + } + + $callback = $this->getStreamListenerClosure($stream, $listener); + $event = $this->loop->io($stream, Ev::WRITE, $callback); + $this->writeStreams[$key] = $event; + } + + public function removeReadStream($stream) + { + $key = (int)$stream; + + if (!isset($this->readStreams[$key])) { + return; + } + + $this->readStreams[$key]->stop(); + unset($this->readStreams[$key]); + } + + public function removeWriteStream($stream) + { + $key = (int)$stream; + + if (!isset($this->writeStreams[$key])) { + return; + } + + $this->writeStreams[$key]->stop(); + unset($this->writeStreams[$key]); + } + + public function addTimer($interval, $callback) + { + $timer = new Timer($interval, $callback, false); + + $callback = function () use ($timer) { + call_user_func($timer->getCallback(), $timer); + + if ($this->isTimerActive($timer)) { + $this->cancelTimer($timer); + } + }; + + $event = $this->loop->timer($timer->getInterval(), 0.0, $callback); + $this->timers->attach($timer, $event); + + return $timer; + } + + public function addPeriodicTimer($interval, $callback) + { + $timer = new Timer($interval, $callback, true); + + $callback = function () use ($timer) { + call_user_func($timer->getCallback(), $timer); + }; + + $event = $this->loop->timer($interval, $interval, $callback); + $this->timers->attach($timer, $event); + + return $timer; + } + + public function cancelTimer(TimerInterface $timer) + { + if (!isset($this->timers[$timer])) { + return; + } + + $event = $this->timers[$timer]; + $event->stop(); + $this->timers->detach($timer); + } + + public function isTimerActive(TimerInterface $timer) + { + return $this->timers->contains($timer); + } + + public function futureTick($listener) + { + $this->futureTickQueue->add($listener); + } + + public function run() + { + $this->running = true; + + while ($this->running) { + $this->futureTickQueue->tick(); + + $hasPendingCallbacks = !$this->futureTickQueue->isEmpty(); + $wasJustStopped = !$this->running; + $nothingLeftToDo = !$this->readStreams + && !$this->writeStreams + && !$this->timers->count() + && $this->signals->isEmpty(); + + $flags = Ev::RUN_ONCE; + if ($wasJustStopped || $hasPendingCallbacks) { + $flags |= Ev::RUN_NOWAIT; + } elseif ($nothingLeftToDo) { + break; + } + + $this->loop->run($flags); + } + } + + public function stop() + { + $this->running = false; + } + + public function __destruct() + { + /** @var TimerInterface $timer */ + foreach ($this->timers as $timer) { + $this->cancelTimer($timer); + } + + foreach ($this->readStreams as $key => $stream) { + $this->removeReadStream($key); + } + + foreach ($this->writeStreams as $key => $stream) { + $this->removeWriteStream($key); + } + } + + public function addSignal($signal, $listener) + { + $this->signals->add($signal, $listener); + + if (!isset($this->signalEvents[$signal])) { + $this->signalEvents[$signal] = $this->loop->signal($signal, function() use ($signal) { + $this->signals->call($signal); + }); + } + } + + public function removeSignal($signal, $listener) + { + $this->signals->remove($signal, $listener); + + if (isset($this->signalEvents[$signal])) { + $this->signalEvents[$signal]->stop(); + unset($this->signalEvents[$signal]); + } + } +} diff --git a/src/Factory.php b/src/Factory.php index 1a56877d..b46fc074 100644 --- a/src/Factory.php +++ b/src/Factory.php @@ -26,6 +26,8 @@ public static function create() // @codeCoverageIgnoreStart if (class_exists('libev\EventLoop', false)) { return new ExtLibevLoop(); + } elseif (class_exists('EvLoop', false)) { + return new ExtEvLoop(); } elseif (class_exists('EventBase', false)) { return new ExtEventLoop(); } elseif (function_exists('event_base_new') && PHP_VERSION_ID < 70000) { diff --git a/tests/ExtEvLoopTest.php b/tests/ExtEvLoopTest.php new file mode 100644 index 00000000..ab41c9f3 --- /dev/null +++ b/tests/ExtEvLoopTest.php @@ -0,0 +1,17 @@ +markTestSkipped('ExtEvLoop tests skipped because ext-ev extension is not installed.'); + } + + return new ExtEvLoop(); + } +} diff --git a/tests/Timer/AbstractTimerTest.php b/tests/Timer/AbstractTimerTest.php index 15202e41..294e683f 100644 --- a/tests/Timer/AbstractTimerTest.php +++ b/tests/Timer/AbstractTimerTest.php @@ -2,10 +2,14 @@ namespace React\Tests\EventLoop\Timer; +use React\EventLoop\LoopInterface; use React\Tests\EventLoop\TestCase; abstract class AbstractTimerTest extends TestCase { + /** + * @return LoopInterface + */ abstract public function createLoop(); public function testAddTimerReturnsNonPeriodicTimerInstance() diff --git a/tests/Timer/ExtEvTimerTest.php b/tests/Timer/ExtEvTimerTest.php new file mode 100644 index 00000000..bfa91861 --- /dev/null +++ b/tests/Timer/ExtEvTimerTest.php @@ -0,0 +1,17 @@ +markTestSkipped('ExtEvLoop tests skipped because ext-ev extension is not installed.'); + } + + return new ExtEvLoop(); + } +} diff --git a/travis-init.sh b/travis-init.sh index 06f853fe..c835d0e6 100755 --- a/travis-init.sh +++ b/travis-init.sh @@ -10,6 +10,9 @@ if [[ "$TRAVIS_PHP_VERSION" != "hhvm" && echo "yes" | pecl install event fi + # install 'ev' PHP extension + echo "yes" | pecl install ev + # install 'libevent' PHP extension (does not support php 7) if [[ "$TRAVIS_PHP_VERSION" != "7.0" && "$TRAVIS_PHP_VERSION" != "7.1" && From 43dc6aca1000f2b1fb0f3769dc7820a4b1822bbe Mon Sep 17 00:00:00 2001 From: Ivan Kalita Date: Wed, 28 Feb 2018 20:52:15 +0300 Subject: [PATCH 2/4] Fix travis: skip ev extension installation if PHP version is 5.3. --- travis-init.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/travis-init.sh b/travis-init.sh index c835d0e6..29ce884a 100755 --- a/travis-init.sh +++ b/travis-init.sh @@ -5,14 +5,12 @@ set -o pipefail if [[ "$TRAVIS_PHP_VERSION" != "hhvm" && "$TRAVIS_PHP_VERSION" != "hhvm-nightly" ]]; then - # install 'event' PHP extension + # install 'event' and 'ev' PHP extension if [[ "$TRAVIS_PHP_VERSION" != "5.3" ]]; then echo "yes" | pecl install event + echo "yes" | pecl install ev fi - # install 'ev' PHP extension - echo "yes" | pecl install ev - # install 'libevent' PHP extension (does not support php 7) if [[ "$TRAVIS_PHP_VERSION" != "7.0" && "$TRAVIS_PHP_VERSION" != "7.1" && From 882597b2c966a2bf236628942e37495830766c88 Mon Sep 17 00:00:00 2001 From: Ivan Kalita Date: Fri, 6 Apr 2018 18:28:34 +0300 Subject: [PATCH 3/4] Remove ExtEvLoop::isTimerActive. --- src/ExtEvLoop.php | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/ExtEvLoop.php b/src/ExtEvLoop.php index 2be45c34..74db6d02 100644 --- a/src/ExtEvLoop.php +++ b/src/ExtEvLoop.php @@ -137,11 +137,13 @@ public function addTimer($interval, $callback) { $timer = new Timer($interval, $callback, false); - $callback = function () use ($timer) { + $that = $this; + $timers = $this->timers; + $callback = function () use ($timer, $timers, $that) { call_user_func($timer->getCallback(), $timer); - if ($this->isTimerActive($timer)) { - $this->cancelTimer($timer); + if ($timers->contains($timer)) { + $that->cancelTimer($timer); } }; @@ -176,11 +178,6 @@ public function cancelTimer(TimerInterface $timer) $this->timers->detach($timer); } - public function isTimerActive(TimerInterface $timer) - { - return $this->timers->contains($timer); - } - public function futureTick($listener) { $this->futureTickQueue->add($listener); From cedf94181bb35774a593b2543b1294ead750e268 Mon Sep 17 00:00:00 2001 From: Ivan Kalita Date: Fri, 6 Apr 2018 18:37:13 +0300 Subject: [PATCH 4/4] Fix README: add ExtEvLoop to TOC. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b2a47887..65f4695f 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ For the code of the current stable 0.4.x release, checkout the * [ExtEventLoop](#exteventloop) * [ExtLibeventLoop](#extlibeventloop) * [ExtLibevLoop](#extlibevloop) + * [ExtEvLoop](#extevloop) * [LoopInterface](#loopinterface) * [addTimer()](#addtimer) * [addPeriodicTimer()](#addperiodictimer) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy