From ae032c0b5d3fa0a9be7560d035341150e322923b Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 17 Apr 2013 17:32:14 +0200 Subject: [PATCH 1/8] Implemented EventLoop --- src/React/EventLoop/EventLoop.php | 218 ++++++++++++++++++ tests/React/Tests/EventLoop/EventLoopTest.php | 22 ++ 2 files changed, 240 insertions(+) create mode 100644 src/React/EventLoop/EventLoop.php create mode 100644 tests/React/Tests/EventLoop/EventLoopTest.php diff --git a/src/React/EventLoop/EventLoop.php b/src/React/EventLoop/EventLoop.php new file mode 100644 index 00000000..8b68a491 --- /dev/null +++ b/src/React/EventLoop/EventLoop.php @@ -0,0 +1,218 @@ +base = new \EventBase(); + $this->callback = $this->createLibeventCallback(); + $this->timers = new SplObjectStorage(); + } + + protected function createLibeventCallback() + { + $readCallbacks = &$this->readCallbacks; + $writeCallbacks = &$this->writeCallbacks; + + return function ($stream, $flags, $loop) use (&$readCallbacks, &$writeCallbacks) { + $id = (int) $stream; + + try { + if (($flags & \Event::READ) === \Event::READ && isset($readCallbacks[$id])) { + call_user_func($readCallbacks[$id], $stream, $loop); + } + + if (($flags & \Event::WRITE) === \Event::WRITE && isset($writeCallbacks[$id])) { + call_user_func($writeCallbacks[$id], $stream, $loop); + } + } catch (\Exception $ex) { + // If one of the callbacks throws an exception we must stop the loop + // otherwise libevent will swallow the exception and go berserk. + $loop->stop(); + + throw $ex; + } + }; + } + + public function addReadStream($stream, $listener) + { + $this->addStreamEvent($stream, \Event::READ, 'read', $listener); + } + + public function addWriteStream($stream, $listener) + { + $this->addStreamEvent($stream, \Event::WRITE, 'write', $listener); + } + + protected function addStreamEvent($stream, $eventClass, $type, $listener) + { + $id = (int) $stream; + + if (($existing = isset($this->events[$id]))) { + if (($this->flags[$id] & $eventClass) === $eventClass) { + return; + } + $event = $this->events[$id]; + $event->del(); + } else { + $event = new \Event($this->base, $stream, \Event::PERSIST, $this->callback, $this); + } + + $flags = isset($this->flags[$id]) ? $this->flags[$id] | $eventClass : $eventClass; + $event->set($this->base, $stream, $flags | \Event::PERSIST, $this->callback, $this); + + if (!$existing) { + // Set the base only if $event has been newly created or be ready for segfaults. + // event_base_set($event, $this->base); + } + + $event->add(); + + $this->events[$id] = $event; + $this->flags[$id] = $flags; + $this->{"{$type}Callbacks"}[$id] = $listener; + } + + public function removeReadStream($stream) + { + $this->removeStreamEvent($stream, \Event::READ, 'read'); + } + + public function removeWriteStream($stream) + { + $this->removeStreamEvent($stream, \Event::WRITE, 'write'); + } + + protected function removeStreamEvent($stream, $eventClass, $type) + { + $id = (int) $stream; + + if (isset($this->events[$id])) { + $flags = $this->flags[$id] & ~$eventClass; + + if ($flags === 0) { + // Remove if stream is not subscribed to any event at this point. + return $this->removeStream($stream); + } + + $event = $this->events[$id]; + + $event->del(); + $event->free(); + unset($this->{"{$type}Callbacks"}[$id]); + + $event = new \Event($this->base, $stream, $flags | \Event::PERSIST, $this->callback, $this); + $event->add(); + + $this->events[$id] = $event; + $this->flags[$id] = $flags; + } + } + + public function removeStream($stream) + { + $id = (int) $stream; + + if (isset($this->events[$id])) { + $event = $this->events[$id]; + + unset( + $this->events[$id], + $this->flags[$id], + $this->readCallbacks[$id], + $this->writeCallbacks[$id] + ); + + $event->del(); + $event->free(); + } + } + + protected function addTimerInternal($interval, $callback, $periodic = false) + { + if ($interval < self::MIN_TIMER_RESOLUTION) { + throw new \InvalidArgumentException('Timer events do not support sub-millisecond timeouts.'); + } + + $timer = new Timer($this, $interval, $callback, $periodic); + + $timers = $this->timers; + $timers->attach($timer, $resource); + + $callback = function () use ($timers, $timer, &$callback) { + if (isset($timers[$timer])) { + call_user_func($timer->getCallback(), $timer); + + if ($timer->isPeriodic() && isset($timers[$timer])) { + $timers[$timer]->add($timer->getInterval() * 1000000); + } else { + $timer->cancel(); + } + } + }; + + $resource = \Event::timer($this->base, $callback); + $resource->add($interval * 1000000); + + return $timer; + } + + public function addTimer($interval, $callback) + { + return $this->addTimerInternal($interval, $callback); + } + + public function addPeriodicTimer($interval, $callback) + { + return $this->addTimerInternal($interval, $callback, true); + } + + public function cancelTimer(TimerInterface $timer) + { + if (isset($this->timers[$timer])) { + $resource = $this->timers[$timer]; + $resource->del(); + $resource->free(); + + $this->timers->detach($timer); + } + } + + public function isTimerActive(TimerInterface $timer) + { + return $this->timers->contains($timer); + } + + public function tick() + { + $this->base->loop(\EventBase::LOOP_ONCE | \EventBase::LOOP_NONBLOCK); + } + + public function run() + { + $this->base->loop(); + } + + public function stop() + { + $this->base->stop(); + } +} diff --git a/tests/React/Tests/EventLoop/EventLoopTest.php b/tests/React/Tests/EventLoop/EventLoopTest.php new file mode 100644 index 00000000..c58e43df --- /dev/null +++ b/tests/React/Tests/EventLoop/EventLoopTest.php @@ -0,0 +1,22 @@ +markTestSkipped('libev tests skipped because ext-libev is not installed.'); + } + + return new EventLoop(); + } + + public function testLibEvConstructor() + { + $loop = new EventLoop(); + } +} From a63b12d8f922dbffea00308b513a58e1da828c3e Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 17 Apr 2013 17:37:45 +0200 Subject: [PATCH 2/8] Code cleanup --- src/React/EventLoop/EventLoop.php | 5 ----- tests/React/Tests/EventLoop/EventLoopTest.php | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/React/EventLoop/EventLoop.php b/src/React/EventLoop/EventLoop.php index 8b68a491..25a71016 100644 --- a/src/React/EventLoop/EventLoop.php +++ b/src/React/EventLoop/EventLoop.php @@ -79,11 +79,6 @@ protected function addStreamEvent($stream, $eventClass, $type, $listener) $flags = isset($this->flags[$id]) ? $this->flags[$id] | $eventClass : $eventClass; $event->set($this->base, $stream, $flags | \Event::PERSIST, $this->callback, $this); - if (!$existing) { - // Set the base only if $event has been newly created or be ready for segfaults. - // event_base_set($event, $this->base); - } - $event->add(); $this->events[$id] = $event; diff --git a/tests/React/Tests/EventLoop/EventLoopTest.php b/tests/React/Tests/EventLoop/EventLoopTest.php index c58e43df..45adfb67 100644 --- a/tests/React/Tests/EventLoop/EventLoopTest.php +++ b/tests/React/Tests/EventLoop/EventLoopTest.php @@ -9,13 +9,13 @@ class EventLoopTest extends AbstractLoopTest public function createLoop() { if (!class_exists('\EventBase')) { - $this->markTestSkipped('libev tests skipped because ext-libev is not installed.'); + $this->markTestSkipped('event tests skipped because pecl/event is not installed.'); } return new EventLoop(); } - public function testLibEvConstructor() + public function testEventConstructor() { $loop = new EventLoop(); } From c8d6e2a3d0f08008d596f15d9c0e79b9b4f77c4a Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 17 Apr 2013 17:57:31 +0200 Subject: [PATCH 3/8] Updated .travis.yml to match event --- .travis.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.travis.yml b/.travis.yml index 6ef5fe32..09a82aec 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,6 +18,12 @@ before_script: cd libevent-0.0.5 && phpize && ./configure && make && sudo make install; echo "extension=libevent.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"`; fi" + - sh -c " if [ \"\$(php --re event | grep 'does not exist')\" != '' ]; then + git clone https://bitbucket.org/osmanov/pecl-event.git; + cd pecl-event; + phpize && ./configure && make && sudo make install; + echo "extension=event.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"`; + fi" - composer self-update - composer install --dev --prefer-source From c8aef6ae9cad0b217d0dbb24024483f259ed65a5 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 22 Apr 2013 16:19:58 +0200 Subject: [PATCH 4/8] Fixed timer attach --- src/React/EventLoop/EventLoop.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/React/EventLoop/EventLoop.php b/src/React/EventLoop/EventLoop.php index 25a71016..1cb3b030 100644 --- a/src/React/EventLoop/EventLoop.php +++ b/src/React/EventLoop/EventLoop.php @@ -150,7 +150,6 @@ protected function addTimerInternal($interval, $callback, $periodic = false) $timer = new Timer($this, $interval, $callback, $periodic); $timers = $this->timers; - $timers->attach($timer, $resource); $callback = function () use ($timers, $timer, &$callback) { if (isset($timers[$timer])) { @@ -166,6 +165,7 @@ protected function addTimerInternal($interval, $callback, $periodic = false) $resource = \Event::timer($this->base, $callback); $resource->add($interval * 1000000); + $timers->attach($timer, $resource); return $timer; } From 8ae47afd20fedf018f62f3cc02bd0cd695d66a66 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 22 Apr 2013 17:19:32 +0200 Subject: [PATCH 5/8] Fixed timers behaviour --- src/React/EventLoop/EventLoop.php | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/React/EventLoop/EventLoop.php b/src/React/EventLoop/EventLoop.php index 1cb3b030..ced82c53 100644 --- a/src/React/EventLoop/EventLoop.php +++ b/src/React/EventLoop/EventLoop.php @@ -148,24 +148,29 @@ protected function addTimerInternal($interval, $callback, $periodic = false) } $timer = new Timer($this, $interval, $callback, $periodic); - + if ($periodic == true) + $flags = \Event::PERSIST; + $resource = new \Event($this->base, -1, $flags |= \Event::TIMEOUT, $callback); + $resource->add($interval); + + var_dump($resource); + $timers = $this->timers; - + $timers->attach($timer, $resource); + $callback = function () use ($timers, $timer, &$callback) { if (isset($timers[$timer])) { call_user_func($timer->getCallback(), $timer); if ($timer->isPeriodic() && isset($timers[$timer])) { - $timers[$timer]->add($timer->getInterval() * 1000000); + $timers[$timer]->add($timer->getInterval()); } else { $timer->cancel(); } } }; - $resource = \Event::timer($this->base, $callback); - $resource->add($interval * 1000000); - $timers->attach($timer, $resource); + return $timer; } From f6afd7fd1a3de486b901cb9abdd42005ee07c4bb Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 22 Apr 2013 18:21:45 +0200 Subject: [PATCH 6/8] Code cleanup --- src/React/EventLoop/EventLoop.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/React/EventLoop/EventLoop.php b/src/React/EventLoop/EventLoop.php index ced82c53..9c6af346 100644 --- a/src/React/EventLoop/EventLoop.php +++ b/src/React/EventLoop/EventLoop.php @@ -150,11 +150,9 @@ protected function addTimerInternal($interval, $callback, $periodic = false) $timer = new Timer($this, $interval, $callback, $periodic); if ($periodic == true) $flags = \Event::PERSIST; - $resource = new \Event($this->base, -1, $flags |= \Event::TIMEOUT, $callback); + $resource = new \Event($this->base, -1, $flags | \Event::TIMEOUT, $callback); $resource->add($interval); - var_dump($resource); - $timers = $this->timers; $timers->attach($timer, $resource); @@ -170,8 +168,6 @@ protected function addTimerInternal($interval, $callback, $periodic = false) } }; - - return $timer; } From d5ca1168c4bae28baa75626fbb9094b8d121aed3 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 22 Apr 2013 18:32:33 +0200 Subject: [PATCH 7/8] Fixed a warning --- src/React/EventLoop/EventLoop.php | 1 + .../Tests/EventLoop/AbstractLoopTest.php | 21 ++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/React/EventLoop/EventLoop.php b/src/React/EventLoop/EventLoop.php index 9c6af346..a95c6409 100644 --- a/src/React/EventLoop/EventLoop.php +++ b/src/React/EventLoop/EventLoop.php @@ -148,6 +148,7 @@ protected function addTimerInternal($interval, $callback, $periodic = false) } $timer = new Timer($this, $interval, $callback, $periodic); + $flags = 0; if ($periodic == true) $flags = \Event::PERSIST; $resource = new \Event($this->base, -1, $flags | \Event::TIMEOUT, $callback); diff --git a/tests/React/Tests/EventLoop/AbstractLoopTest.php b/tests/React/Tests/EventLoop/AbstractLoopTest.php index 71101c88..9bb149e7 100644 --- a/tests/React/Tests/EventLoop/AbstractLoopTest.php +++ b/tests/React/Tests/EventLoop/AbstractLoopTest.php @@ -11,13 +11,14 @@ abstract class AbstractLoopTest extends TestCase public function setUp() { $this->loop = $this->createLoop(); + touch("testfile"); } abstract public function createLoop(); public function testAddReadStream() { - $input = fopen('php://temp', 'r+'); + $input = fopen('testfile', 'r+'); $this->loop->addReadStream($input, $this->expectCallableExactly(2)); @@ -32,7 +33,7 @@ public function testAddReadStream() public function testAddWriteStream() { - $input = fopen('php://temp', 'r+'); + $input = fopen('testfile', 'r+'); $this->loop->addWriteStream($input, $this->expectCallableExactly(2)); $this->loop->tick(); @@ -41,7 +42,7 @@ public function testAddWriteStream() public function testRemoveReadStreamInstantly() { - $input = fopen('php://temp', 'r+'); + $input = fopen('testfile', 'r+'); $this->loop->addReadStream($input, $this->expectCallableNever()); $this->loop->removeReadStream($input); @@ -53,7 +54,7 @@ public function testRemoveReadStreamInstantly() public function testRemoveReadStreamAfterReading() { - $input = fopen('php://temp', 'r+'); + $input = fopen('testfile', 'r+'); $this->loop->addReadStream($input, $this->expectCallableOnce()); @@ -70,7 +71,7 @@ public function testRemoveReadStreamAfterReading() public function testRemoveWriteStreamInstantly() { - $input = fopen('php://temp', 'r+'); + $input = fopen('testfile', 'r+'); $this->loop->addWriteStream($input, $this->expectCallableNever()); $this->loop->removeWriteStream($input); @@ -79,7 +80,7 @@ public function testRemoveWriteStreamInstantly() public function testRemoveWriteStreamAfterWriting() { - $input = fopen('php://temp', 'r+'); + $input = fopen('testfile', 'r+'); $this->loop->addWriteStream($input, $this->expectCallableOnce()); $this->loop->tick(); @@ -90,7 +91,7 @@ public function testRemoveWriteStreamAfterWriting() public function testRemoveStreamInstantly() { - $input = fopen('php://temp', 'r+'); + $input = fopen('testfile', 'r+'); $this->loop->addReadStream($input, $this->expectCallableNever()); $this->loop->addWriteStream($input, $this->expectCallableNever()); @@ -103,7 +104,7 @@ public function testRemoveStreamInstantly() public function testRemoveStream() { - $input = fopen('php://temp', 'r+'); + $input = fopen('testfile', 'r+'); $this->loop->addReadStream($input, $this->expectCallableOnce()); $this->loop->addWriteStream($input, $this->expectCallableOnce()); @@ -128,7 +129,7 @@ public function emptyRunShouldSimplyReturn() /** @test */ public function runShouldReturnWhenNoMoreFds() { - $input = fopen('php://temp', 'r+'); + $input = fopen('testfile', 'r+'); $loop = $this->loop; $this->loop->addReadStream($input, function ($stream) use ($loop) { @@ -144,7 +145,7 @@ public function runShouldReturnWhenNoMoreFds() /** @test */ public function stopShouldStopRunningLoop() { - $input = fopen('php://temp', 'r+'); + $input = fopen('testfile', 'r+'); $loop = $this->loop; $this->loop->addReadStream($input, function ($stream) use ($loop) { From 5d47eeac240ba65d14f7c76aea2e6070e44fbb52 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 23 Apr 2013 09:54:37 +0200 Subject: [PATCH 8/8] Removed an unnecessary add --- src/React/EventLoop/EventLoop.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/React/EventLoop/EventLoop.php b/src/React/EventLoop/EventLoop.php index a95c6409..c077200c 100644 --- a/src/React/EventLoop/EventLoop.php +++ b/src/React/EventLoop/EventLoop.php @@ -161,9 +161,7 @@ protected function addTimerInternal($interval, $callback, $periodic = false) if (isset($timers[$timer])) { call_user_func($timer->getCallback(), $timer); - if ($timer->isPeriodic() && isset($timers[$timer])) { - $timers[$timer]->add($timer->getInterval()); - } else { + if (!$timer->isPeriodic() || isset($timers[$timer])) { $timer->cancel(); } } 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