Thomas Weinert avatar Thomas Weinert committed 50f9f28

Implemented support for multiple event loop implementations, choosen by a priority list
Implemented event loop wrapper implementation for ReactPHP

Comments (0)

Files changed (4)

src/Carica/Io/Event/Loop/Factory.php

 
   class Factory {
 
+    const USE_STREAMSELECT = 'streamselect';
+    const USE_LIBEVENT = 'libevent';
+    const USE_REACT = 'react';
+
     /**
      * @var Event\Loop
      */
     private static $_globalLoop = NULL;
 
-    private static $_useLibevent = FALSE;
+    /**
+     * @var null|int
+     */
+    private static $_useImplementation = NULL;
+
+    private static $_priority = array();
+
+    private static $_defaultPriority = array(
+      // self::USE_REACT,
+      // self::USE_LIBEVENT,
+      self::USE_STREAMSELECT
+    );
 
     /**
      * Create a event loop
      *
+     * @param array $priority
      * @return Event\Loop
      */
-    public static function create() {
-      if (self::useLibevent()) {
+    public static function create(array $priority = NULL) {
+      switch (self::getImplementation($priority)) {
+      case self::USE_REACT :
+        return new React();
+      case self::USE_LIBEVENT :
         return new Libevent(event_base_new());
-      } else {
+      default :
         return new StreamSelect();
       }
     }
 
     /**
-     * Getter/Setter for libevent usage, if TRUE is provided as an argument it will
-     * only activate the use if the extension is installed.
+     * Determine the implementation that should be used
      *
-     * @param string $use
-     * @return boolean
+     * @param array $priority
+     * @return string
      */
-    public static function useLibevent($use = NULL) {
-      if (isset($use)) {
-        self::$_useLibevent = $use ? NULL : FALSE;
+    public static function getImplementation(array $priority = NULL) {
+      $priority = self::getPriority($priority);
+      if (NULL === self::$_useImplementation ||
+          !in_array(self::$_useImplementation, $priority)) {
+        foreach ($priority as $implementation) {
+          switch ($implementation) {
+          case self::USE_REACT :
+            if (interface_exists('\\React\\EventLoop\\LoopInterface')) {
+              return self::$_useImplementation = self::USE_REACT;
+            }
+            break;
+          case self::USE_LIBEVENT :
+            if (extension_loaded('libevent')) {
+              return self::$_useImplementation = self::USE_LIBEVENT;
+            }
+            break;
+          }
+        }
+        self::$_useImplementation = self::USE_STREAMSELECT;
       }
-      if (NULL === self::$_useLibevent) {
-        self::$_useLibevent = extension_loaded('libevent');
+      return self::$_useImplementation;
+    }
+
+    private function getPriority($priority) {
+      if (NULL == self::$_priority) {
+        self::$_priority = self::$_defaultPriority;
+      }
+      if (NULL === $priority) {
+        $priority = self::$_priority;
+      } else {
+        self::$_priority = $priority;
       }
-      return self::$_useLibevent;
+      return $priority;
     }
 
     /**
      * Return a global event loop instance, create it if it does not exists yet.
      *
+     * @param null $priority
      * @return Event\Loop
      */
-    public static function get() {
+    public static function get($priority = NULL) {
       if (is_null(self::$_globalLoop)) {
-        self::$_globalLoop = self::create();
+        self::$_globalLoop = self::create($priority);
       }
       return self::$_globalLoop;
     }
      * @return Event\Loop
      */
     public static function reset() {
-      self::$_useLibevent = NULL;
+      self::$_priority = self::$_defaultPriority;
+      self::$_useImplementation = NULL;
       self::$_globalLoop = NULL;
     }
 

src/Carica/Io/Event/Loop/React.php

+<?php
+
+namespace Carica\Io\Event\Loop {
+
+  use Carica\Io as Io;
+  use React\EventLoop as ReactEventLoop;
+
+  class React implements Io\Event\Loop {
+
+    /**
+     * @var ReactEventLoop\LoopInterface
+     */
+    private $_loop = NULL;
+
+    public function loop(ReactEventLoop\LoopInterface $loop = NULL) {
+      if (isset($loop)) {
+        $this->_loop = $loop;
+      } elseif (NULL === $this->_loop) {
+        $this->_loop = ReactEventLoop\Factory::create();
+      }
+      return $this->_loop;
+    }
+
+    public function setTimeout(Callable $callback, $milliseconds) {
+      $timer = $this->loop()->addTimer($milliseconds / 1000, $callback);
+      return new React\Identifier(React\Identifier::TYPE_TIMEOUT, $timer);
+    }
+
+    public function setInterval(Callable $callback, $milliseconds) {
+      $timer = $this->loop()->addPeriodicTimer($milliseconds / 1000, $callback);
+      return new React\Identifier(React\Identifier::TYPE_INTERVAL, $timer);
+    }
+
+    public function setStreamReader(Callable $callback, $stream) {
+      $this->loop()->addReadStream($stream, $callback);
+      return new React\Identifier(React\Identifier::TYPE_STREAMREADER, $stream);
+    }
+
+    public function remove($listener) {
+      if ($listener instanceOf React\Identifier) {
+        switch ($listener->getType()) {
+        case React\Identifier::TYPE_TIMEOUT :
+        case React\Identifier::TYPE_INTERVAL :
+          $this->loop()->cancelTimer($listener->getData());
+          break;
+        case React\Identifier::TYPE_STREAMREADER :
+          $this->loop()->removeReadStream($listener->getData());
+          break;
+        default :
+          throw new \LogicException('Unknwon listener identifier type');
+        }
+      } else {
+        throw new \LogicException('Listener is not a valid identifer');
+      }
+    }
+
+    public function run(Io\Deferred\Promise $for = NULL) {
+      $loop = $this->loop();
+      if (isset($for) &&
+          $for->state() === Io\Deferred::STATE_PENDING) {
+        $for->always(
+          function () use ($loop) {
+            $loop->stop();
+          }
+        );
+      }
+      $loop->run();
+    }
+
+    public function stop() {
+      $this->loop()->stop();
+    }
+
+    public function count() {
+      return -1;
+    }
+  }
+}

src/Carica/Io/Event/Loop/React/Identifier.php

+<?php
+
+namespace Carica\Io\Event\Loop\React {
+
+  class Identifier {
+
+    const TYPE_TIMEOUT = 1;
+    const TYPE_INTERVAL = 2;
+    const TYPE_STREAMREADER = 3;
+
+    private $_type = 0;
+    private $_data = NULL;
+
+    public function __construct($type, $data) {
+      $this->_type = $type;
+      $this->_data = $data;
+    }
+
+    public function getType() {
+      return $this->_type;
+    }
+
+    public function getData() {
+      return $this->_data;
+    }
+  }
+}

tests/Carica/Io/Event/Loop/FactoryTest.php

     }
 
     public function testCreateStreamSelectExplicit() {
-      Factory::useLibevent(FALSE);
-      $loop = Factory::create();
+      $loop = Factory::create(array());
       $this->assertInstanceOf('Carica\Io\Event\Loop\StreamSelect', $loop);
     }
 
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.