ソースを参照

Merge pull request #978 from luzrain/events

Error handling implemented in all event classes
walkor 2 年 前
コミット
535680f5d9
9 ファイル変更230 行追加237 行削除
  1. 5 0
      phpstan.neon.dist
  2. 29 23
      src/Events/Ev.php
  3. 38 54
      src/Events/Event.php
  4. 29 14
      src/Events/EventInterface.php
  5. 14 18
      src/Events/Revolt.php
  6. 47 50
      src/Events/Select.php
  7. 32 37
      src/Events/Swoole.php
  8. 31 35
      src/Events/Swow.php
  9. 5 6
      src/Worker.php

+ 5 - 0
phpstan.neon.dist

@@ -25,6 +25,11 @@ parameters:
 	            - '#Constant STREAM_POLLNONE not found.#'
 	            - '#Constant STREAM_POLLOUT not found.#'
 	            - '#Property Workerman\\Events\\Swow::.* has unknown class Swow\\Coroutine as its type.#'
+	    -
+	        path: src/Events/Event.php
+	        reportUnmatched: false
+	        messages:
+	            - '#Call to an undefined method EventBase::+.#'
 	    - path: src/Timer.php
 	      message: '#Call to static method getSuspension\(\) on an unknown class Revolt\\EventLoop.#'
 	    - path: src/Worker.php

+ 29 - 23
src/Events/Ev.php

@@ -15,10 +15,6 @@ declare(strict_types=1);
 
 namespace Workerman\Events;
 
-use EvIo;
-use EvSignal;
-use EvTimer;
-
 /**
  * Ev eventloop
  */
@@ -27,28 +23,28 @@ class Ev implements EventInterface
     /**
      * All listeners for read event.
      *
-     * @var array
+     * @var array<int, \EvIo>
      */
     protected array $readEvents = [];
 
     /**
      * All listeners for write event.
      *
-     * @var array
+     * @var array<int, \EvIo>
      */
     protected array $writeEvents = [];
 
     /**
      * Event listeners of signal.
      *
-     * @var array
+     * @var array<int, \EvSignal>
      */
     protected array $eventSignal = [];
 
     /**
      * All timer event listeners.
      *
-     * @var array
+     * @var array<int, \EvTimer>
      */
     protected array $eventTimer = [];
 
@@ -70,9 +66,9 @@ class Ev implements EventInterface
     public function delay(float $delay, callable $func, array $args = []): int
     {
         $timerId = self::$timerId;
-        $event = new EvTimer($delay, 0, function () use ($func, $args, $timerId) {
+        $event = new \EvTimer($delay, 0, function () use ($func, $args, $timerId) {
             unset($this->eventTimer[$timerId]);
-            $func(...$args);
+            $this->safeCall($func, $args);
         });
         $this->eventTimer[self::$timerId] = $event;
         return self::$timerId++;
@@ -104,9 +100,7 @@ class Ev implements EventInterface
      */
     public function repeat(float $interval, callable $func, array $args = []): int
     {
-        $event = new EvTimer($interval, $interval, function () use ($func, $args) {
-            $func(...$args);
-        });
+        $event = new \EvTimer($interval, $interval, fn () => $this->safeCall($func, $args));
         $this->eventTimer[self::$timerId] = $event;
         return self::$timerId++;
     }
@@ -117,9 +111,7 @@ class Ev implements EventInterface
     public function onReadable($stream, callable $func): void
     {
         $fdKey = (int)$stream;
-        $event = new EvIo($stream, \Ev::READ, function () use ($func, $stream) {
-            $func($stream);
-        });
+        $event = new \EvIo($stream, \Ev::READ, fn () => $this->safeCall($func, [$stream]));
         $this->readEvents[$fdKey] = $event;
     }
 
@@ -143,10 +135,8 @@ class Ev implements EventInterface
     public function onWritable($stream, callable $func): void
     {
         $fdKey = (int)$stream;
-        $event = new EvIo($stream, \Ev::WRITE, function () use ($func, $stream) {
-            $func($stream);
-        });
-        $this->readEvents[$fdKey] = $event;
+        $event = new \EvIo($stream, \Ev::WRITE, fn () => $this->safeCall($func, [$stream]));
+        $this->writeEvents[$fdKey] = $event;
     }
 
     /**
@@ -168,9 +158,7 @@ class Ev implements EventInterface
      */
     public function onSignal(int $signal, callable $func): void
     {
-        $event = new EvSignal($signal, function () use ($func, $signal) {
-            $func($signal);
-        });
+        $event = new \EvSignal($signal, fn () => $this->safeCall($func, [$signal]));
         $this->eventSignal[$signal] = $event;
     }
 
@@ -237,4 +225,22 @@ class Ev implements EventInterface
     {
         return $this->errorHandler;
     }
+
+    /**
+     * @param callable $func
+     * @param array $args
+     * @return void
+     */
+    private function safeCall(callable $func, array $args = []): void
+    {
+        try {
+            $func(...$args);
+        } catch (\Throwable $e) {
+            if ($this->errorHandler === null) {
+                echo $e;
+            } else {
+                ($this->errorHandler)($e);
+            }
+        }
+    }
 }

+ 38 - 54
src/Events/Event.php

@@ -16,12 +16,6 @@ declare(strict_types=1);
 
 namespace Workerman\Events;
 
-use EventBase;
-use RuntimeException;
-use Throwable;
-use function class_exists;
-use function count;
-
 /**
  * libevent eventloop
  */
@@ -29,43 +23,49 @@ class Event implements EventInterface
 {
     /**
      * Event base.
-     * @var EventBase
+     *
+     * @var \EventBase
      */
-    protected EventBase $eventBase;
+    protected \EventBase $eventBase;
 
     /**
      * All listeners for read event.
-     * @var array
+     *
+     * @var array<int, \Event>
      */
     protected array $readEvents = [];
 
     /**
      * All listeners for write event.
-     * @var array
+     *
+     * @var array<int, \Event>
      */
     protected array $writeEvents = [];
 
     /**
      * Event listeners of signal.
-     * @var array
+     *
+     * @var array<int, \Event>
      */
     protected array $eventSignal = [];
 
     /**
      * All timer event listeners.
-     * [func, args, event, flag, time_interval]
-     * @var array
+     *
+     * @var array<int, \Event>
      */
     protected array $eventTimer = [];
 
     /**
      * Timer id.
+     *
      * @var int
      */
     protected int $timerId = 0;
 
     /**
      * Event class name.
+     *
      * @var string
      */
     protected string $eventClassName = '';
@@ -77,17 +77,16 @@ class Event implements EventInterface
 
     /**
      * Construct.
-     * @return void
      */
     public function __construct()
     {
-        if (class_exists('\\\\Event', false)) {
+        if (\class_exists('\\\\Event', false)) {
             $className = '\\\\Event';
         } else {
             $className = '\Event';
         }
         $this->eventClassName = $className;
-        if (class_exists('\\\\EventBase', false)) {
+        if (\class_exists('\\\\EventBase', false)) {
             $className = '\\\\EventBase';
         } else {
             $className = '\EventBase';
@@ -104,14 +103,10 @@ class Event implements EventInterface
         $timerId = $this->timerId++;
         $event = new $className($this->eventBase, -1, $className::TIMEOUT, function () use ($func, $args, $timerId) {
             unset($this->eventTimer[$timerId]);
-            try {
-                $func(...$args);
-            } catch (Throwable $e) {
-                $this->error($e);
-            }
+            $this->safeCall($func, $args);
         });
         if (!$event->addTimer($delay)) {
-            throw new RuntimeException("Event::addTimer($delay) failed");
+            throw new \RuntimeException("Event::addTimer($delay) failed");
         }
         $this->eventTimer[$timerId] = $event;
         return $timerId;
@@ -145,15 +140,9 @@ class Event implements EventInterface
     {
         $className = $this->eventClassName;
         $timerId = $this->timerId++;
-        $event = new $className($this->eventBase, -1, $className::TIMEOUT | $className::PERSIST, function () use ($func, $args) {
-            try {
-                $func(...$args);
-            } catch (Throwable $e) {
-                $this->error($e);
-            }
-        });
+        $event = new $className($this->eventBase, -1, $className::TIMEOUT | $className::PERSIST, fn () => $this->safeCall($func, $args));
         if (!$event->addTimer($interval)) {
-            throw new RuntimeException("Event::addTimer($interval) failed");
+            throw new \RuntimeException("Event::addTimer($interval) failed");
         }
         $this->eventTimer[$timerId] = $event;
         return $timerId;
@@ -166,12 +155,10 @@ class Event implements EventInterface
     {
         $className = $this->eventClassName;
         $fdKey = (int)$stream;
-        $event = new $this->eventClassName($this->eventBase, $stream, $className::READ | $className::PERSIST, $func, $stream);
-        // @phpstan-ignore-next-line Negated boolean expression is always false.
-        if (!$event || !$event->add()) {
-            return;
+        $event = new $className($this->eventBase, $stream, $className::READ | $className::PERSIST, fn () => $this->safeCall($func, [$stream]));
+        if ($event->add()) {
+            $this->readEvents[$fdKey] = $event;
         }
-        $this->readEvents[$fdKey] = $event;
     }
 
     /**
@@ -195,12 +182,10 @@ class Event implements EventInterface
     {
         $className = $this->eventClassName;
         $fdKey = (int)$stream;
-        $event = new $this->eventClassName($this->eventBase, $stream, $className::WRITE | $className::PERSIST, $func, $stream);
-        // @phpstan-ignore-next-line Negated boolean expression is always false.
-        if (!$event || !$event->add()) {
-            return;
+        $event = new $className($this->eventBase, $stream, $className::WRITE | $className::PERSIST, fn () => $this->safeCall($func, [$stream]));
+        if ($event->add()) {
+            $this->writeEvents[$fdKey] = $event;
         }
-        $this->writeEvents[$fdKey] = $event;
     }
 
     /**
@@ -224,11 +209,10 @@ class Event implements EventInterface
     {
         $className = $this->eventClassName;
         $fdKey = $signal;
-        $event = $className::signal($this->eventBase, $signal, $func);
-        if (!$event || !$event->add()) {
-            return;
+        $event = $className::signal($this->eventBase, $signal, fn () => $this->safeCall($func, [$signal]));
+        if ($event->add()) {
+            $this->eventSignal[$fdKey] = $event;
         }
-        $this->eventSignal[$fdKey] = $event;
     }
 
     /**
@@ -277,7 +261,7 @@ class Event implements EventInterface
      */
     public function getTimerCount(): int
     {
-        return count($this->eventTimer);
+        return \count($this->eventTimer);
     }
 
     /**
@@ -297,20 +281,20 @@ class Event implements EventInterface
     }
 
     /**
-     * @param Throwable $e
+     * @param callable $func
+     * @param array $args
      * @return void
-     * @throws Throwable
      */
-    public function error(Throwable $e): void
+    private function safeCall(callable $func, array $args = []): void
     {
         try {
-            if (!$this->errorHandler) {
-                throw new $e;
+            $func(...$args);
+        } catch (\Throwable $e) {
+            if ($this->errorHandler === null) {
+                echo $e;
+            } else {
+                ($this->errorHandler)($e);
             }
-            ($this->errorHandler)($e);
-        } catch (Throwable $e) {
-            // Cannot trigger an exception in the Event callback, otherwise it will cause an infinite loop
-            echo $e;
         }
     }
 }

+ 29 - 14
src/Events/EventInterface.php

@@ -11,23 +11,26 @@
  * @link      http://www.workerman.net/
  * @license   http://www.opensource.org/licenses/mit-license.php MIT License
  */
-namespace Workerman\Events;
 
-use Throwable;
+declare(strict_types=1);
+
+namespace Workerman\Events;
 
 interface EventInterface
 {
     /**
      * Delay the execution of a callback.
+     *
      * @param float $delay
-     * @param callable $func
-     * @param mixed[] $args
+     * @param callable(mixed...): void $func
+     * @param array $args
      * @return int
      */
     public function delay(float $delay, callable $func, array $args = []): int;
 
     /**
      * Delete a delay timer.
+     *
      * @param int $timerId
      * @return bool
      */
@@ -35,15 +38,17 @@ interface EventInterface
 
     /**
      * Repeatedly execute a callback.
+     *
      * @param float $interval
-     * @param callable $func
-     * @param mixed[] $args
+     * @param callable(mixed...): void $func
+     * @param array $args
      * @return int
      */
     public function repeat(float $interval, callable $func, array $args = []): int;
 
     /**
      * Delete a repeat timer.
+     *
      * @param int $timerId
      * @return bool
      */
@@ -51,14 +56,16 @@ interface EventInterface
 
     /**
      * Execute a callback when a stream resource becomes readable or is closed for reading.
+     *
      * @param resource $stream
-     * @param callable $func
+     * @param callable(resource): void $func
      * @return void
      */
     public function onReadable($stream, callable $func): void;
 
     /**
      * Cancel a callback of stream readable.
+     *
      * @param resource $stream
      * @return bool
      */
@@ -66,14 +73,16 @@ interface EventInterface
 
     /**
      * Execute a callback when a stream resource becomes writable or is closed for writing.
+     *
      * @param resource $stream
-     * @param callable $func
+     * @param callable(resource): void $func
      * @return void
      */
     public function onWritable($stream, callable $func): void;
 
     /**
      * Cancel a callback of stream writable.
+     *
      * @param resource $stream
      * @return bool
      */
@@ -81,15 +90,16 @@ interface EventInterface
 
     /**
      * Execute a callback when a signal is received.
+     *
      * @param int $signal
-     * @param callable $func
+     * @param callable(int): void $func
      * @return void
-     * @throws Throwable
      */
     public function onSignal(int $signal, callable $func): void;
 
     /**
      * Cancel a callback of signal.
+     *
      * @param int $signal
      * @return bool
      */
@@ -97,39 +107,44 @@ interface EventInterface
 
     /**
      * Delete all timer.
+     *
      * @return void
      */
     public function deleteAllTimer(): void;
 
     /**
      * Run the event loop.
+     *
      * @return void
-     * @throws Throwable
      */
     public function run(): void;
 
     /**
      * Stop event loop.
+     *
      * @return void
      */
     public function stop(): void;
 
     /**
      * Get Timer count.
+     *
      * @return int
      */
     public function getTimerCount(): int;
 
     /**
-     * Set error handler
-     * @param callable $errorHandler
+     * Set error handler.
+     *
+     * @param callable(\Throwable): void $errorHandler
      * @return void
      */
     public function setErrorHandler(callable $errorHandler): void;
 
     /**
      * Get error handler
-     * @return ?callable(Throwable)
+     *
+     * @return null|callable (\Throwable): void
      */
     public function getErrorHandler(): ?callable;
 }

+ 14 - 18
src/Events/Revolt.php

@@ -34,30 +34,35 @@ class Revolt implements EventInterface
 
     /**
      * All listeners for read event.
-     * @var array
+     *
+     * @var array<int, string>
      */
     protected array $readEvents = [];
 
     /**
      * All listeners for write event.
-     * @var array
+     *
+     * @var array<int, string>
      */
     protected array $writeEvents = [];
 
     /**
      * Event listeners of signal.
-     * @var array
+     *
+     * @var array<int, string>
      */
     protected array $eventSignal = [];
 
     /**
      * Event listeners of timer.
-     * @var array
+     *
+     * @var array<int, string>
      */
     protected array $eventTimer = [];
 
     /**
      * Timer id.
+     *
      * @var int
      */
     protected int $timerId = 1;
@@ -71,7 +76,7 @@ class Revolt implements EventInterface
     }
 
     /**
-     * Get driver
+     * Get driver.
      *
      * @return Driver
      */
@@ -123,10 +128,7 @@ class Revolt implements EventInterface
     public function repeat(float $interval, callable $func, array $args = []): int
     {
         $timerId = $this->timerId++;
-        $closure = function () use ($func, $args) {
-            $func(...$args);
-        };
-        $cbId = $this->driver->repeat($interval, $closure);
+        $cbId = $this->driver->repeat($interval, static fn () => $func(...$args));
         $this->eventTimer[$timerId] = $cbId;
         return $timerId;
     }
@@ -142,9 +144,7 @@ class Revolt implements EventInterface
             unset($this->readEvents[$fdKey]);
         }
 
-        $this->readEvents[$fdKey] = $this->driver->onReadable($stream, function () use ($stream, $func) {
-            $func($stream);
-        });
+        $this->readEvents[$fdKey] = $this->driver->onReadable($stream, static fn () => $func($stream));
     }
 
     /**
@@ -171,9 +171,7 @@ class Revolt implements EventInterface
             $this->driver->cancel($this->writeEvents[$fdKey]);
             unset($this->writeEvents[$fdKey]);
         }
-        $this->writeEvents[$fdKey] = $this->driver->onWritable($stream, function () use ($stream, $func) {
-            $func($stream);
-        });
+        $this->writeEvents[$fdKey] = $this->driver->onWritable($stream, static fn () => $func($stream));
     }
 
     /**
@@ -200,9 +198,7 @@ class Revolt implements EventInterface
             $this->driver->cancel($this->eventSignal[$fdKey]);
             unset($this->eventSignal[$fdKey]);
         }
-        $this->eventSignal[$fdKey] = $this->driver->onSignal($signal, function () use ($signal, $func) {
-            $func($signal);
-        });
+        $this->eventSignal[$fdKey] = $this->driver->onSignal($signal, static fn () => $func($signal));
     }
 
     /**

+ 47 - 50
src/Events/Select.php

@@ -16,8 +16,6 @@ declare(strict_types=1);
 
 namespace Workerman\Events;
 
-use SplPriorityQueue;
-use Throwable;
 use function count;
 use function max;
 use function microtime;
@@ -32,6 +30,7 @@ class Select implements EventInterface
 {
     /**
      * Running.
+     *
      * @var bool
      */
     protected bool $running = true;
@@ -39,47 +38,47 @@ class Select implements EventInterface
     /**
      * All listeners for read/write event.
      *
-     * @var array
+     * @var array<int, callable>
      */
     protected array $readEvents = [];
 
     /**
      * All listeners for read/write event.
      *
-     * @var array
+     * @var array<int, callable>
      */
     protected array $writeEvents = [];
 
     /**
-     * @var array
+     * @var array<int, callable>
      */
     protected array $exceptEvents = [];
 
     /**
      * Event listeners of signal.
      *
-     * @var array
+     * @var array<int, callable>
      */
     protected array $signalEvents = [];
 
     /**
      * Fds waiting for read event.
      *
-     * @var array
+     * @var array<int, resource>
      */
     protected array $readFds = [];
 
     /**
      * Fds waiting for write event.
      *
-     * @var array
+     * @var array<int, resource>
      */
     protected array $writeFds = [];
 
     /**
      * Fds waiting for except event.
      *
-     * @var array
+     * @var array<int, resource>
      */
     protected array $exceptFds = [];
 
@@ -87,9 +86,9 @@ class Select implements EventInterface
      * Timer scheduler.
      * {['data':timer_id, 'priority':run_timestamp], ..}
      *
-     * @var SplPriorityQueue
+     * @var \SplPriorityQueue
      */
-    protected SplPriorityQueue $scheduler;
+    protected \SplPriorityQueue $scheduler;
 
     /**
      * All timer event listeners.
@@ -116,16 +115,15 @@ class Select implements EventInterface
     /**
      * @var ?callable
      */
-    protected $errorHandler;
+    protected $errorHandler = null;
 
     /**
      * Construct.
      */
     public function __construct()
     {
-        // Init SplPriorityQueue.
-        $this->scheduler = new SplPriorityQueue();
-        $this->scheduler->setExtractFlags(SplPriorityQueue::EXTR_BOTH);
+        $this->scheduler = new \SplPriorityQueue();
+        $this->scheduler->setExtractFlags(\SplPriorityQueue::EXTR_BOTH);
     }
 
     /**
@@ -242,10 +240,11 @@ class Select implements EventInterface
 
     /**
      * On except.
+     *
      * @param resource $stream
-     * @param $func
+     * @param callable $func
      */
-    public function onExcept($stream, $func): void
+    public function onExcept($stream, callable $func): void
     {
         $fdKey = (int)$stream;
         $this->exceptEvents[$fdKey] = $func;
@@ -254,6 +253,7 @@ class Select implements EventInterface
 
     /**
      * Off except.
+     *
      * @param resource $stream
      * @return bool
      */
@@ -276,7 +276,7 @@ class Select implements EventInterface
             return;
         }
         $this->signalEvents[$signal] = $func;
-        pcntl_signal($signal, $this->signalHandler(...));
+        pcntl_signal($signal, fn () => $this->safeCall($this->signalEvents[$signal], [$signal]));
     }
 
     /**
@@ -296,20 +296,10 @@ class Select implements EventInterface
     }
 
     /**
-     * Signal handler.
-     *
-     * @param int $signal
-     */
-    public function signalHandler(int $signal): void
-    {
-        $this->signalEvents[$signal]($signal);
-    }
-
-    /**
      * Tick for timer.
      *
      * @return void
-     * @throws Throwable
+     * @throws \Throwable
      */
     protected function tick(): void
     {
@@ -320,6 +310,7 @@ class Select implements EventInterface
             $nextRunTime = -$schedulerData['priority'];
             $timeNow = microtime(true);
             $this->selectTimeout = (int)(($nextRunTime - $timeNow) * 1000000);
+
             if ($this->selectTimeout <= 0) {
                 $this->scheduler->extract();
 
@@ -335,12 +326,7 @@ class Select implements EventInterface
                 } else {
                     unset($this->eventTimer[$timerId]);
                 }
-                try {
-                    $taskData[0](...$taskData[1]);
-                } catch (Throwable $e) {
-                    $this->error($e);
-                    continue;
-                }
+                $this->safeCall($taskData[0], $taskData[1]);
             } else {
                 break;
             }
@@ -363,8 +349,8 @@ class Select implements EventInterface
      */
     public function deleteAllTimer(): void
     {
-        $this->scheduler = new SplPriorityQueue();
-        $this->scheduler->setExtractFlags(SplPriorityQueue::EXTR_BOTH);
+        $this->scheduler = new \SplPriorityQueue();
+        $this->scheduler->setExtractFlags(\SplPriorityQueue::EXTR_BOTH);
         $this->eventTimer = [];
     }
 
@@ -377,11 +363,12 @@ class Select implements EventInterface
             $read = $this->readFds;
             $write = $this->writeFds;
             $except = $this->exceptFds;
-            if ($read || $write || $except) {
+            if (!empty($read) || !empty($write) || !empty($except)) {
                 // Waiting read/write/signal/timeout events.
                 try {
                     @stream_select($read, $write, $except, 0, $this->selectTimeout);
-                } catch (Throwable) {
+                } catch (\Throwable) {
+                    // do nothing
                 }
             } else {
                 $this->selectTimeout >= 1 && usleep($this->selectTimeout);
@@ -394,21 +381,21 @@ class Select implements EventInterface
             foreach ($read as $fd) {
                 $fdKey = (int)$fd;
                 if (isset($this->readEvents[$fdKey])) {
-                    $this->readEvents[$fdKey]($fd);
+                    $this->safeCall($this->readEvents[$fdKey], [$fd]);
                 }
             }
 
             foreach ($write as $fd) {
                 $fdKey = (int)$fd;
                 if (isset($this->writeEvents[$fdKey])) {
-                    $this->writeEvents[$fdKey]($fd);
+                    $this->safeCall($this->writeEvents[$fdKey], [$fd]);
                 }
             }
 
             foreach ($except as $fd) {
                 $fdKey = (int)$fd;
                 if (isset($this->exceptEvents[$fdKey])) {
-                    $this->exceptEvents[$fdKey]($fd);
+                    $this->safeCall($this->exceptEvents[$fdKey], [$fd]);
                 }
             }
 
@@ -429,8 +416,13 @@ class Select implements EventInterface
         foreach ($this->signalEvents as $signal => $item) {
             $this->offsignal($signal);
         }
-        $this->readFds = $this->writeFds = $this->exceptFds = $this->readEvents
-            = $this->writeEvents = $this->exceptEvents = $this->signalEvents = [];
+        $this->readFds = [];
+        $this->writeFds = [];
+        $this->exceptFds = [];
+        $this->readEvents = [];
+        $this->writeEvents = [];
+        $this->exceptEvents = [];
+        $this->signalEvents = [];
     }
 
     /**
@@ -458,15 +450,20 @@ class Select implements EventInterface
     }
 
     /**
-     * @param Throwable $e
+     * @param callable $func
+     * @param array $args
      * @return void
-     * @throws Throwable
      */
-    public function error(Throwable $e): void
+    private function safeCall(callable $func, array $args = []): void
     {
-        if (!$this->errorHandler) {
-            throw new $e;
+        try {
+            $func(...$args);
+        } catch (\Throwable $e) {
+            if ($this->errorHandler === null) {
+                echo $e;
+            } else {
+                ($this->errorHandler)($e);
+            }
         }
-        ($this->errorHandler)($e);
     }
 }

+ 32 - 37
src/Events/Swoole.php

@@ -18,25 +18,27 @@ namespace Workerman\Events;
 use Swoole\Event;
 use Swoole\Process;
 use Swoole\Timer;
-use Throwable;
 
 class Swoole implements EventInterface
 {
     /**
      * All listeners for read timer
-     * @var array
+     *
+     * @var array<int, int>
      */
     protected array $eventTimer = [];
 
     /**
      * All listeners for read event.
-     * @var array
+     *
+     * @var array<int, resource>
      */
     protected array $readEvents = [];
 
     /**
      * All listeners for write event.
-     * @var array
+     *
+     * @var array<int, resource>
      */
     protected array $writeEvents = [];
 
@@ -47,7 +49,6 @@ class Swoole implements EventInterface
 
     /**
      * {@inheritdoc}
-     * @throws Throwable
      */
     public function delay(float $delay, callable $func, array $args = []): int
     {
@@ -55,11 +56,7 @@ class Swoole implements EventInterface
         $t = max($t, 1);
         $timerId = Timer::after($t, function () use ($func, $args, &$timerId) {
             unset($this->eventTimer[$timerId]);
-            try {
-                $func(...$args);
-            } catch (Throwable $e) {
-                $this->error($e);
-            }
+            $this->safeCall($func, $args);
         });
         $this->eventTimer[$timerId] = $timerId;
         return $timerId;
@@ -71,9 +68,9 @@ class Swoole implements EventInterface
     public function offDelay(int $timerId): bool
     {
         if (isset($this->eventTimer[$timerId])) {
-            $res = Timer::clear($timerId);
+            Timer::clear($timerId);
             unset($this->eventTimer[$timerId]);
-            return $res;
+            return true;
         }
         return false;
     }
@@ -88,18 +85,13 @@ class Swoole implements EventInterface
 
     /**
      * {@inheritdoc}
-     * @throws Throwable
      */
     public function repeat(float $interval, callable $func, array $args = []): int
     {
         $t = (int)($interval * 1000);
         $t = max($t, 1);
         $timerId = Timer::tick($t, function () use ($func, $args) {
-            try {
-                $func(...$args);
-            } catch (Throwable $e) {
-                $this->error($e);
-            }
+            $this->safeCall($func, $args);
         });
         $this->eventTimer[$timerId] = $timerId;
         return $timerId;
@@ -112,14 +104,13 @@ class Swoole implements EventInterface
     {
         $fd = (int)$stream;
         if (!isset($this->readEvents[$fd]) && !isset($this->writeEvents[$fd])) {
-            Event::add($stream, $func, null, SWOOLE_EVENT_READ);
+            Event::add($stream, fn () => $this->safeCall($func, [$stream]), null, SWOOLE_EVENT_READ);
+        } elseif (isset($this->writeEvents[$fd])) {
+            Event::set($stream, fn () => $this->safeCall($func, [$stream]), null, SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE);
         } else {
-            if (isset($this->writeEvents[$fd])) {
-                Event::set($stream, $func, null, SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE);
-            } else {
-                Event::set($stream, $func, null, SWOOLE_EVENT_READ);
-            }
+            Event::set($stream, fn () => $this->safeCall($func, [$stream]), null, SWOOLE_EVENT_READ);
         }
+
         $this->readEvents[$fd] = $stream;
     }
 
@@ -148,14 +139,13 @@ class Swoole implements EventInterface
     {
         $fd = (int)$stream;
         if (!isset($this->readEvents[$fd]) && !isset($this->writeEvents[$fd])) {
-            Event::add($stream, null, $func, SWOOLE_EVENT_WRITE);
+            Event::add($stream, null, fn () => $this->safeCall($func, [$stream]), SWOOLE_EVENT_WRITE);
+        } elseif (isset($this->readEvents[$fd])) {
+            Event::set($stream, null, fn () => $this->safeCall($func, [$stream]), SWOOLE_EVENT_WRITE | SWOOLE_EVENT_READ);
         } else {
-            if (isset($this->readEvents[$fd])) {
-                Event::set($stream, null, $func, SWOOLE_EVENT_WRITE | SWOOLE_EVENT_READ);
-            } else {
-                Event::set($stream, null, $func, SWOOLE_EVENT_WRITE);
-            }
+            Event::set($stream, null, fn () => $this->safeCall($func, [$stream]), SWOOLE_EVENT_WRITE);
         }
+
         $this->writeEvents[$fd] = $stream;
     }
 
@@ -182,7 +172,7 @@ class Swoole implements EventInterface
      */
     public function onSignal(int $signal, callable $func): void
     {
-        Process::signal($signal, $func);
+        Process::signal($signal, fn () => $this->safeCall($func, [$signal]));
     }
 
     /**
@@ -252,15 +242,20 @@ class Swoole implements EventInterface
     }
 
     /**
-     * @param Throwable $e
+     * @param callable $func
+     * @param array $args
      * @return void
-     * @throws Throwable
      */
-    public function error(Throwable $e): void
+    private function safeCall(callable $func, array $args = []): void
     {
-        if (!$this->errorHandler) {
-            throw new $e;
+        try {
+            $func(...$args);
+        } catch (\Throwable $e) {
+            if ($this->errorHandler === null) {
+                echo $e;
+            } else {
+                ($this->errorHandler)($e);
+            }
         }
-        ($this->errorHandler)($e);
     }
 }

+ 31 - 35
src/Events/Swow.php

@@ -4,36 +4,38 @@ declare(strict_types=1);
 
 namespace Workerman\Events;
 
-use RuntimeException;
 use Swow\Coroutine;
 use Swow\Signal;
 use Swow\SignalException;
-use Throwable;
 use function Swow\Sync\waitAll;
 
 class Swow implements EventInterface
 {
     /**
-     * All listeners for read timer
-     * @var array
+     * All listeners for read timer.
+     *
+     * @var array<int, int>
      */
     protected array $eventTimer = [];
 
     /**
      * All listeners for read event.
-     * @var array<Coroutine>
+     *
+     * @var array<int, Coroutine>
      */
     protected array $readEvents = [];
 
     /**
      * All listeners for write event.
-     * @var array<Coroutine>
+     *
+     * @var array<int, Coroutine>
      */
     protected array $writeEvents = [];
 
     /**
      * All listeners for signal.
-     * @var array<Coroutine>
+     *
+     * @var array<int, Coroutine>
      */
     protected array $signalListener = [];
 
@@ -54,21 +56,15 @@ class Swow implements EventInterface
 
     /**
      * {@inheritdoc}
-     * @throws Throwable
      */
     public function delay(float $delay, callable $func, array $args = []): int
     {
         $t = (int)($delay * 1000);
         $t = max($t, 1);
-        $that = $this;
-        $coroutine = Coroutine::run(function () use ($t, $func, $args, $that): void {
+        $coroutine = Coroutine::run(function () use ($t, $func, $args): void {
             msleep($t);
             unset($this->eventTimer[Coroutine::getCurrent()->getId()]);
-            try {
-                $func(...$args);
-            } catch (Throwable $e) {
-                $that->error($e);
-            }
+            $this->safeCall($func, $args);
         });
         $timerId = $coroutine->getId();
         $this->eventTimer[$timerId] = $timerId;
@@ -77,22 +73,16 @@ class Swow implements EventInterface
 
     /**
      * {@inheritdoc}
-     * @throws Throwable
      */
     public function repeat(float $interval, callable $func, array $args = []): int
     {
         $t = (int)($interval * 1000);
         $t = max($t, 1);
-        $that = $this;
-        $coroutine = Coroutine::run(static function () use ($t, $func, $args, $that): void {
+        $coroutine = Coroutine::run(function () use ($t, $func, $args): void {
             // @phpstan-ignore-next-line While loop condition is always true.
             while (true) {
                 msleep($t);
-                try {
-                    $func(...$args);
-                } catch (Throwable $e) {
-                    $that->error($e);
-                }
+                $this->safeCall($func, $args);
             }
         });
         $timerId = $coroutine->getId();
@@ -156,14 +146,14 @@ class Swow implements EventInterface
                         break;
                     }
                     if ($rEvent !== STREAM_POLLNONE) {
-                        $func($stream);
+                        $this->safeCall($func, [$stream]);
                     }
                     if ($rEvent !== STREAM_POLLIN) {
                         $this->offReadable($stream);
                         break;
                     }
                 }
-            } catch (RuntimeException) {
+            } catch (\RuntimeException) {
                 $this->offReadable($stream);
             }
         });
@@ -201,14 +191,14 @@ class Swow implements EventInterface
                         break;
                     }
                     if ($rEvent !== STREAM_POLLNONE) {
-                        $func($stream);
+                        $this->safeCall($func, [$stream]);
                     }
                     if ($rEvent !== STREAM_POLLOUT) {
                         $this->offWritable($stream);
                         break;
                     }
                 }
-            } catch (RuntimeException) {
+            } catch (\RuntimeException) {
                 $this->offWritable($stream);
             }
         });
@@ -241,8 +231,9 @@ class Swow implements EventInterface
                         $this->signalListener[$signal] !== Coroutine::getCurrent()) {
                         break;
                     }
-                    $func($signal);
+                    $this->safeCall($func, [$signal]);
                 } catch (SignalException) {
+                    // do nothing
                 }
             }
         });
@@ -295,15 +286,20 @@ class Swow implements EventInterface
     }
 
     /**
-     * @param Throwable $e
+     * @param callable $func
+     * @param array $args
      * @return void
-     * @throws Throwable
      */
-    public function error(Throwable $e): void
+    private function safeCall(callable $func, array $args = []): void
     {
-        if (!$this->errorHandler) {
-            throw new $e;
+        try {
+            $func(...$args);
+        } catch (\Throwable $e) {
+            if ($this->errorHandler === null) {
+                echo $e;
+            } else {
+                ($this->errorHandler)($e);
+            }
         }
-        ($this->errorHandler)($e);
     }
-}
+}

+ 5 - 6
src/Worker.php

@@ -2531,16 +2531,16 @@ class Worker
      * For udp package.
      *
      * @param resource $socket
-     * @return bool
+     * @return void
      * @throws Throwable
      */
-    protected function acceptUdpConnection(mixed $socket): bool
+    protected function acceptUdpConnection(mixed $socket): void
     {
         set_error_handler(static fn (): bool => true);
         $recvBuffer = stream_socket_recvfrom($socket, UdpConnection::MAX_UDP_PACKAGE_SIZE, 0, $remoteAddress);
         restore_error_handler();
         if (false === $recvBuffer || empty($remoteAddress)) {
-            return false;
+            return;
         }
         // UdpConnection.
         $connection = new UdpConnection($socket, $remoteAddress);
@@ -2556,7 +2556,7 @@ class Worker
                         while ($recvBuffer !== '') {
                             $len = $parser::input($recvBuffer, $connection);
                             if ($len === 0) {
-                                return true;
+                                return;
                             }
                             $package = substr($recvBuffer, 0, $len);
                             $recvBuffer = substr($recvBuffer, $len);
@@ -2570,7 +2570,7 @@ class Worker
                         $data = $parser::decode($recvBuffer, $connection);
                         // Discard bad packets.
                         if ($data === false) {
-                            return true;
+                            return;
                         }
                         $messageCallback($connection, $data);
                     }
@@ -2582,7 +2582,6 @@ class Worker
                 static::stopAll(250, $e);
             }
         }
-        return true;
     }
 
     /**