From 3d3f6d6b8b846f3b7f8c0b05629ce3a848620ea0 Mon Sep 17 00:00:00 2001 From: CismonX Date: Fri, 18 Aug 2017 23:44:26 +0800 Subject: [PATCH] archive --- .gitignore | 3 + LICENSE | 21 ++++++ README.md | 35 ++++++++++ composer.json | 15 +++++ src/Uv.php | 176 ++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 250 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 composer.json create mode 100644 src/Uv.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ff7f293 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +.idea/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fab3f18 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 CismonX + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..c8e3340 --- /dev/null +++ b/README.md @@ -0,0 +1,35 @@ +## Workerman-Uv + +### 简介 + +[libuv](http://libuv.org/) 是一个事件驱动的异步 I/O 库,最初是为 Node.js 开发的。随后也在 Python、Julia 等语言中被应用。 + +[php-uv](https://pecl.php.net/package/uv) 是对 libuv 的封装,使其可以在 PHP 中被应用。 + +本项目将 php-uv 的 event-loop 应用于 Workerman,从而可以在基于 Workerman 的项目中利用 php-uv 提供的特性。 + +### 使用说明 + +1. 使用包管理器安装 libuv 和 libuv-devel(可能需要手动添加源)。 + +2. 使用 pecl 安装 php-uv(也可以从 pecl 官网或 GitHub 仓库下载源码后手动编译)。 + +3. 使用 composer 加载`Workerman\\Events\\Uv`。 + +```bash +composer require cismonx/workerman-uv +``` + +4. 在项目中使用 `Workerman\Events\Uv` 提供的 event-loop。如下: + +```php +Worker::$eventLoopClass = '\\Workerman\\Events\\Uv'; +``` + +### 注意 + +1. 使用 libuv 的 event-loop 后,Workerman 的子进程处理 SIGINT 事件时会 exit 2 (no such file or directory),这个问题待解决。 + +2. 如果需要使用 libuv 的多线程特性,需要线程安全(ZTS)的 PHP。 + +3. php-uv 目前处于 Beta 阶段,其稳定性不能保证。请避免将其应用于生产环境。 diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..9634585 --- /dev/null +++ b/composer.json @@ -0,0 +1,15 @@ +{ + "name" : "cismonx/workerman-uv", + "description" : "Libuv event loop for Workerman.", + "license" : "MIT", + "type" : "library", + "require" : { + "workerman/workerman": "^3.4", + "ext-uv": "*" + }, + "autoload" : { + "psr-4" : { + "Workerman\\Events\\" : "src/" + } + } +} diff --git a/src/Uv.php b/src/Uv.php new file mode 100644 index 0000000..0111ded --- /dev/null +++ b/src/Uv.php @@ -0,0 +1,176 @@ + + */ +namespace Workerman\Events; +use Workerman\Worker; + +class Uv implements EventInterface { + + /** + * Libuv event loop. + * @var resource + */ + protected $_loop; + + /** + * Read & write events. + * @var array + */ + protected $_allEvents = []; + + /** + * Signals. + * @var array + */ + protected $_eventSignal = []; + + /** + * Timers. + * @var array + */ + protected $_eventTimer = []; + + /** + * Timer id counter. + * @var int + */ + protected static $_timerId = 1; + + /** + * Identifies a socket with both read and write events registered. + */ + const EV_RW = 3; + + /** + * Constructor. + */ + public function __construct() { + $this->_loop = uv_default_loop(); + } + + /** + * {@inheritdoc} + */ + public function add($fd, $flag, $func, $args = null) { + switch ($flag) { + case self::EV_READ: + case self::EV_WRITE: + $fd_key = intval($fd); + //uv_poll_init() can only be called once for a same file descriptor. + if (!isset($this->_allEvents[$fd_key])) + $this->_allEvents[$fd_key][0] = uv_poll_init($this->_loop, $fd); + $event = $this->_allEvents[$fd_key][0]; + $this->_allEvents[$fd_key][$flag] = $func; + if (isset($this->_allEvents[$fd_key][self::EV_RW - $flag])) + $flag = self::EV_RW; + //Call uv_poll_start() with both existing flags. + uv_poll_start($event, $flag, function ($poll, $stat, $ev, $conn) use ($func) { + $func($conn); + }); + break; + case self::EV_SIGNAL: + $fd_key = intval($fd); + $event = uv_signal_init(); + uv_signal_start($event, function ($ev, $signal) use ($func) { + $func($signal); + }, $fd_key); + $this->_eventSignal[$fd_key] = $event; + break; + case self::EV_TIMER: + case self::EV_TIMER_ONCE: + $event = uv_timer_init(); + $param = [$func, (array)$args, $flag, self::$_timerId]; + $interval = $fd * 1000; + uv_timer_start($event, $interval, $interval, \Closure::bind(function () use ($param) { + $timer_id = $param[3]; + if ($param[2] === self::EV_TIMER_ONCE) { + uv_timer_stop($this->_eventTimer[$timer_id]); + unset($this->_eventTimer[$timer_id]); + } + try { + call_user_func_array($param[0], $param[1]); + } catch (\Exception $e) { + Worker::log($e); + exit(250); + } catch (\Error $e) { + Worker::log($e); + exit(250); + } + }, $this, __CLASS__)); + $this->_eventTimer[self::$_timerId] = $event; + return self::$_timerId++; + default: + break; + } + return false; + } + + /** + * {@inheritdoc} + */ + public function del($fd, $flag) { + switch ($flag) { + case self::EV_READ: + case self::EV_WRITE: + $fd_key = intval($fd); + if (isset($this->_allEvents[$fd_key][$flag])) { + unset($this->_allEvents[$fd_key][$flag]); + if (isset($this->_allEvents[$fd_key][self::EV_RW - $flag])) { + $func = $this->_allEvents[$fd_key][self::EV_RW - $flag]; + //Call uv_poll_start() with the remaining flag instead of call uv_poll_stop(). + uv_poll_start($this->_allEvents[$fd_key][0], self::EV_RW - $flag, + function ($poll, $stat, $ev, $conn) use ($func) { + $func($conn); + } + ); + } else + uv_poll_stop($this->_allEvents[$fd_key][0]); + } + break; + case self::EV_SIGNAL: + $fd_key = intval($fd); + if (isset($this->_eventSignal[$fd_key])) { + uv_signal_stop($this->_eventSignal[$fd_key]); + unset($this->_eventSignal[$fd_key]); + } + break; + case self::EV_TIMER: + case self::EV_TIMER_ONCE: + if (isset($this->_eventTimer[$fd])) { + uv_timer_stop($this->_eventTimer[$fd]); + unset($this->_eventTimer[$fd]); + } + break; + } + } + + /** + * {@inheritdoc} + */ + public function loop() { + uv_run(); + } + + /** + * {@inheritdoc} + */ + public function clearAllTimer() { + foreach ($this->_eventTimer as $event) + uv_timer_stop($event); + $this->_eventTimer = []; + } + + /** + * {@inheritdoc} + */ + public function destroy() { + foreach ($this->_eventSignal as $event) + uv_signal_stop($event); + } + + public function getTimerCount() { + return count($this->_eventTimer); + } +} \ No newline at end of file