From 27c2faf1f8612b5451e2908d51f643560482817a Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Wed, 14 Jun 2023 21:06:00 +0200 Subject: [PATCH 1/8] Provide siginfo to signal callback if possible --- composer.json | 2 +- src/EventLoop.php | 4 +++- src/EventLoop/Driver.php | 4 +++- src/EventLoop/Driver/StreamSelectDriver.php | 22 ++++++++++++++++----- src/EventLoop/Driver/TracingDriver.php | 2 +- src/EventLoop/Internal/AbstractDriver.php | 6 ++++-- src/EventLoop/Internal/SignalCallback.php | 3 ++- test/Driver/StreamSelectDriverTest.php | 7 ++++++- 8 files changed, 37 insertions(+), 13 deletions(-) diff --git a/composer.json b/composer.json index 18e66e7..c69c780 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,7 @@ "ext-json": "*", "phpunit/phpunit": "^9", "jetbrains/phpstorm-stubs": "^2019.3", - "psalm/phar": "^4.7" + "psalm/phar": "^5" }, "autoload": { "psr-4": { diff --git a/src/EventLoop.php b/src/EventLoop.php index 347f588..effd906 100644 --- a/src/EventLoop.php +++ b/src/EventLoop.php @@ -201,8 +201,10 @@ public static function onWritable(mixed $stream, \Closure $closure): string * The created callback MUST immediately be marked as enabled, but only be activated (i.e. callback can be called) * right before the next tick. Callbacks MUST NOT be called in the tick they were enabled. * + * The third parameter of the callback MAY be contain an OS-specific siginfo structure, null otherwise. + * * @param int $signal The signal number to monitor. - * @param \Closure(string, int):void $closure The callback to execute. + * @param \Closure(string, int, mixed):void $closure The callback to execute. * * @return string A unique identifier that can be used to cancel, enable or disable the callback. * diff --git a/src/EventLoop/Driver.php b/src/EventLoop/Driver.php index 84e5e8a..75b0c55 100644 --- a/src/EventLoop/Driver.php +++ b/src/EventLoop/Driver.php @@ -165,8 +165,10 @@ public function onWritable(mixed $stream, \Closure $closure): string; * The created callback MUST immediately be marked as enabled, but only be activated (i.e. callback can be called) * right before the next tick. Callbacks MUST NOT be called in the tick they were enabled. * + * The third parameter of the callback MAY be contain an OS-specific siginfo structure, null otherwise. + * * @param int $signal The signal number to monitor. - * @param \Closure(string, int):void $closure The callback to execute. + * @param \Closure(string, int, mixed):void $closure The callback to execute. * * @return string A unique identifier that can be used to cancel, enable or disable the callback. * diff --git a/src/EventLoop/Driver/StreamSelectDriver.php b/src/EventLoop/Driver/StreamSelectDriver.php index 03b8a1b..97f0688 100644 --- a/src/EventLoop/Driver/StreamSelectDriver.php +++ b/src/EventLoop/Driver/StreamSelectDriver.php @@ -34,7 +34,7 @@ final class StreamSelectDriver extends AbstractDriver /** @var array> */ private array $signalCallbacks = []; - /** @var \SplQueue */ + /** @var \SplQueue */ private readonly \SplQueue $signalQueue; private bool $signalHandling; @@ -120,9 +120,21 @@ protected function dispatch(bool $blocking): void \pcntl_signal_dispatch(); while (!$this->signalQueue->isEmpty()) { - $signal = $this->signalQueue->dequeue(); + [$signal, $siginfo] = $this->signalQueue->dequeue(); foreach ($this->signalCallbacks[$signal] as $callback) { + if ($siginfo !== null) { + $callbackNew = new SignalCallback( + $callback->id, + $callback->closure, + $callback->signal, + $siginfo + ); + $callbackNew->invokable = &$callback->invokable; + $callbackNew->referenced = &$callback->referenced; + $callbackNew->enabled = &$callback->enabled; + $callback = $callbackNew; + } $this->enqueueCallback($callback); } @@ -304,7 +316,7 @@ private function selectStreams(array $read, array $write, float $timeout): void } if ($timeout > 0) { // Sleep until next timer expires. - /** @psalm-var positive-int $timeout */ + /** @psalm-suppress ArgumentTypeCoercion $timeout is always > 0, even if there is no psalm type to represent a positive float. */ \usleep((int) ($timeout * 1_000_000)); } } @@ -325,9 +337,9 @@ private function getTimeout(): float return $expiration > 0 ? $expiration : 0.0; } - private function handleSignal(int $signal): void + private function handleSignal(int $signal, mixed $siginfo): void { // Queue signals, so we don't suspend inside pcntl_signal_dispatch, which disables signals while it runs - $this->signalQueue->enqueue($signal); + $this->signalQueue->enqueue([$signal, $siginfo]); } } diff --git a/src/EventLoop/Driver/TracingDriver.php b/src/EventLoop/Driver/TracingDriver.php index d16ea32..61f2440 100644 --- a/src/EventLoop/Driver/TracingDriver.php +++ b/src/EventLoop/Driver/TracingDriver.php @@ -249,7 +249,7 @@ private function getCancelTrace(string $callbackId): string /** * Formats a stacktrace obtained via `debug_backtrace()`. * - * @param array $trace + * @param list, class?: class-string, file: string, function: string, line: int, object?: object, type?: string}> $trace * Output of `debug_backtrace()`. * * @return string Formatted stacktrace. diff --git a/src/EventLoop/Internal/AbstractDriver.php b/src/EventLoop/Internal/AbstractDriver.php index 78578bd..f4f8f3a 100644 --- a/src/EventLoop/Internal/AbstractDriver.php +++ b/src/EventLoop/Internal/AbstractDriver.php @@ -186,7 +186,7 @@ public function onWritable($stream, \Closure $closure): string public function onSignal(int $signal, \Closure $closure): string { - $signalCallback = new SignalCallback($this->nextId++, $closure, $signal); + $signalCallback = new SignalCallback($this->nextId++, $closure, $signal, null); $this->callbacks[$signalCallback->id] = $signalCallback; $this->enableQueue[$signalCallback->id] = $signalCallback; @@ -517,6 +517,7 @@ private function createLoopFiber(): void // Invoke microtasks if we have some $this->invokeCallbacks(); + /** @var bool $this->stopped */ while (!$this->stopped) { if ($this->interrupt) { $this->invokeInterrupt(); @@ -572,7 +573,8 @@ private function createCallbackFiber(): void ), $callback instanceof SignalCallback => ($callback->closure)( $callback->id, - $callback->signal + $callback->signal, + $callback->siginfo ), default => ($callback->closure)($callback->id), }; diff --git a/src/EventLoop/Internal/SignalCallback.php b/src/EventLoop/Internal/SignalCallback.php index 5a6c8bd..509abd2 100644 --- a/src/EventLoop/Internal/SignalCallback.php +++ b/src/EventLoop/Internal/SignalCallback.php @@ -10,7 +10,8 @@ final class SignalCallback extends DriverCallback public function __construct( string $id, \Closure $closure, - public readonly int $signal + public readonly int $signal, + public readonly mixed $siginfo ) { parent::__construct($id, $closure); } diff --git a/test/Driver/StreamSelectDriverTest.php b/test/Driver/StreamSelectDriverTest.php index 705919c..82b5e34 100644 --- a/test/Driver/StreamSelectDriverTest.php +++ b/test/Driver/StreamSelectDriverTest.php @@ -39,7 +39,12 @@ public function testAsyncSignals(): void try { $this->start(function (Driver $loop) use (&$invoked, &$callbackId) { - $callbackId = $loop->onSignal(SIGUSR1, function () use (&$invoked) { + $callbackId = $loop->onSignal(SIGUSR1, function ($cId, $sig, $siginfo) use (&$callbackId, &$invoked) { + $this->assertEquals($callbackId, $cId); + $this->assertEquals(SIGUSR1, $sig); + $this->assertEquals(SIGUSR1, $siginfo['signo']); + $this->assertEquals(\getmypid(), $siginfo['pid']); + $this->assertEquals(\getmyuid(), $siginfo['uid']); $invoked = true; }); From 772547e7789c56c020989ca2ff55e646b1ac1d8d Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Wed, 14 Jun 2023 21:09:52 +0200 Subject: [PATCH 2/8] Fix psalm --- psalm.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/psalm.xml b/psalm.xml index 7c63f8b..cdb9653 100644 --- a/psalm.xml +++ b/psalm.xml @@ -28,6 +28,13 @@ + + + + + + + From f7c61344e6729d54ac099542aa27bb2700842717 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Thu, 15 Jun 2023 13:18:05 +0200 Subject: [PATCH 3/8] Fix --- src/EventLoop/Driver/StreamSelectDriver.php | 13 +------------ src/EventLoop/Internal/SignalCallback.php | 2 +- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/src/EventLoop/Driver/StreamSelectDriver.php b/src/EventLoop/Driver/StreamSelectDriver.php index 97f0688..c6ac6f2 100644 --- a/src/EventLoop/Driver/StreamSelectDriver.php +++ b/src/EventLoop/Driver/StreamSelectDriver.php @@ -123,18 +123,7 @@ protected function dispatch(bool $blocking): void [$signal, $siginfo] = $this->signalQueue->dequeue(); foreach ($this->signalCallbacks[$signal] as $callback) { - if ($siginfo !== null) { - $callbackNew = new SignalCallback( - $callback->id, - $callback->closure, - $callback->signal, - $siginfo - ); - $callbackNew->invokable = &$callback->invokable; - $callbackNew->referenced = &$callback->referenced; - $callbackNew->enabled = &$callback->enabled; - $callback = $callbackNew; - } + $callback->siginfo = $siginfo; $this->enqueueCallback($callback); } diff --git a/src/EventLoop/Internal/SignalCallback.php b/src/EventLoop/Internal/SignalCallback.php index 509abd2..9971491 100644 --- a/src/EventLoop/Internal/SignalCallback.php +++ b/src/EventLoop/Internal/SignalCallback.php @@ -11,7 +11,7 @@ public function __construct( string $id, \Closure $closure, public readonly int $signal, - public readonly mixed $siginfo + public mixed $siginfo ) { parent::__construct($id, $closure); } From cc2acb593f9926dbd7ecb6ae8f188241376f0a12 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Sat, 22 Jul 2023 14:35:51 +0200 Subject: [PATCH 4/8] Add onSignalWithInfo --- src/EventLoop.php | 30 +++++++++++++++++-- src/EventLoop/Driver.php | 27 +++++++++++++++-- src/EventLoop/Driver/EvDriver.php | 9 ++++++ src/EventLoop/Driver/EventDriver.php | 9 ++++++ src/EventLoop/Driver/StreamSelectDriver.php | 21 +++++++++++-- src/EventLoop/Driver/TracingDriver.php | 10 +++++++ src/EventLoop/Driver/UvDriver.php | 9 ++++++ src/EventLoop/Internal/AbstractDriver.php | 16 +++++++++- src/EventLoop/Internal/SignalCallback.php | 3 +- .../Internal/SignalCallbackExtra.php | 18 +++++++++++ test/Driver/StreamSelectDriverTest.php | 6 ++-- 11 files changed, 143 insertions(+), 15 deletions(-) create mode 100644 src/EventLoop/Internal/SignalCallbackExtra.php diff --git a/src/EventLoop.php b/src/EventLoop.php index effd906..dc76d2d 100644 --- a/src/EventLoop.php +++ b/src/EventLoop.php @@ -201,10 +201,8 @@ public static function onWritable(mixed $stream, \Closure $closure): string * The created callback MUST immediately be marked as enabled, but only be activated (i.e. callback can be called) * right before the next tick. Callbacks MUST NOT be called in the tick they were enabled. * - * The third parameter of the callback MAY be contain an OS-specific siginfo structure, null otherwise. - * * @param int $signal The signal number to monitor. - * @param \Closure(string, int, mixed):void $closure The callback to execute. + * @param \Closure(string, int):void $closure The callback to execute. * * @return string A unique identifier that can be used to cancel, enable or disable the callback. * @@ -215,6 +213,32 @@ public static function onSignal(int $signal, \Closure $closure): string return self::getDriver()->onSignal($signal, $closure); } + /** + * Execute a callback when a signal is received, also processing eventual OS-specific signal info. + * + * Warning: Installing the same signal on different instances of this interface is deemed undefined behavior. + * Implementations MAY try to detect this, if possible, but are not required to. This is due to technical + * limitations of the signals being registered globally per process. + * + * Multiple callbacks on the same signal MAY be executed in any order. + * + * The created callback MUST immediately be marked as enabled, but only be activated (i.e. callback can be called) + * right before the next tick. Callbacks MUST NOT be called in the tick they were enabled. + * + * The third parameter of the callback MAY contain an OS-specific siginfo structure, or null otherwise. + * + * @param int $signal The signal number to monitor. + * @param \Closure(string, int, mixed):void $closure The callback to execute. + * + * @return string A unique identifier that can be used to cancel, enable or disable the callback. + * + * @throws UnsupportedFeatureException If signal handling with signal information is not supported. + */ + public static function onSignalWithInfo(int $signal, \Closure $closure): string + { + return self::getDriver()->onSignalWithInfo($signal, $closure); + } + /** * Enable a callback to be active starting in the next tick. * diff --git a/src/EventLoop/Driver.php b/src/EventLoop/Driver.php index 75b0c55..e0566cd 100644 --- a/src/EventLoop/Driver.php +++ b/src/EventLoop/Driver.php @@ -165,10 +165,8 @@ public function onWritable(mixed $stream, \Closure $closure): string; * The created callback MUST immediately be marked as enabled, but only be activated (i.e. callback can be called) * right before the next tick. Callbacks MUST NOT be called in the tick they were enabled. * - * The third parameter of the callback MAY be contain an OS-specific siginfo structure, null otherwise. - * * @param int $signal The signal number to monitor. - * @param \Closure(string, int, mixed):void $closure The callback to execute. + * @param \Closure(string, int):void $closure The callback to execute. * * @return string A unique identifier that can be used to cancel, enable or disable the callback. * @@ -176,6 +174,29 @@ public function onWritable(mixed $stream, \Closure $closure): string; */ public function onSignal(int $signal, \Closure $closure): string; + /** + * Execute a callback when a signal is received, also processing eventual OS-specific signal info. + * + * Warning: Installing the same signal on different instances of this interface is deemed undefined behavior. + * Implementations MAY try to detect this, if possible, but are not required to. This is due to technical + * limitations of the signals being registered globally per process. + * + * Multiple callbacks on the same signal MAY be executed in any order. + * + * The created callback MUST immediately be marked as enabled, but only be activated (i.e. callback can be called) + * right before the next tick. Callbacks MUST NOT be called in the tick they were enabled. + * + * The third parameter of the callback MAY contain an OS-specific siginfo structure, or null otherwise. + * + * @param int $signal The signal number to monitor. + * @param \Closure(string, int, mixed):void $closure The callback to execute. + * + * @return string A unique identifier that can be used to cancel, enable or disable the callback. + * + * @throws UnsupportedFeatureException If signal handling with signal information is not supported. + */ + public function onSignalWithInfo(int $signal, \Closure $closure): string; + /** * Enable a callback to be active starting in the next tick. * diff --git a/src/EventLoop/Driver/EvDriver.php b/src/EventLoop/Driver/EvDriver.php index e0a33a3..6b034b5 100644 --- a/src/EventLoop/Driver/EvDriver.php +++ b/src/EventLoop/Driver/EvDriver.php @@ -13,6 +13,7 @@ use Revolt\EventLoop\Internal\StreamReadableCallback; use Revolt\EventLoop\Internal\StreamWritableCallback; use Revolt\EventLoop\Internal\TimerCallback; +use Revolt\EventLoop\UnsupportedFeatureException; final class EvDriver extends AbstractDriver { @@ -70,6 +71,14 @@ public function __construct() }; } + /** + * @throws UnsupportedFeatureException Signal info handling is not supported with the Ev driver, please use onSignal instead. + */ + public function onSignalWithInfo(int $signal, \Closure $closure): string + { + throw new UnsupportedFeatureException("Signal info handling is not supported with the Ev driver, please use onSignal instead."); + } + /** * {@inheritdoc} */ diff --git a/src/EventLoop/Driver/EventDriver.php b/src/EventLoop/Driver/EventDriver.php index 869c3a9..9b8e9cc 100644 --- a/src/EventLoop/Driver/EventDriver.php +++ b/src/EventLoop/Driver/EventDriver.php @@ -13,6 +13,7 @@ use Revolt\EventLoop\Internal\StreamReadableCallback; use Revolt\EventLoop\Internal\StreamWritableCallback; use Revolt\EventLoop\Internal\TimerCallback; +use Revolt\EventLoop\UnsupportedFeatureException; final class EventDriver extends AbstractDriver { @@ -58,6 +59,14 @@ public function __construct() }; } + /** + * @throws UnsupportedFeatureException Signal info handling is not supported with the Event driver, please use onSignal instead. + */ + public function onSignalWithInfo(int $signal, \Closure $closure): string + { + throw new UnsupportedFeatureException("Signal info handling is not supported with the Event driver, please use onSignal instead."); + } + /** * {@inheritdoc} */ diff --git a/src/EventLoop/Driver/StreamSelectDriver.php b/src/EventLoop/Driver/StreamSelectDriver.php index c6ac6f2..a792b4c 100644 --- a/src/EventLoop/Driver/StreamSelectDriver.php +++ b/src/EventLoop/Driver/StreamSelectDriver.php @@ -9,6 +9,7 @@ use Revolt\EventLoop\Internal\AbstractDriver; use Revolt\EventLoop\Internal\DriverCallback; use Revolt\EventLoop\Internal\SignalCallback; +use Revolt\EventLoop\Internal\SignalCallbackExtra; use Revolt\EventLoop\Internal\StreamReadableCallback; use Revolt\EventLoop\Internal\StreamWritableCallback; use Revolt\EventLoop\Internal\TimerCallback; @@ -101,6 +102,18 @@ public function onSignal(int $signal, \Closure $closure): string return parent::onSignal($signal, $closure); } + /** + * @throws UnsupportedFeatureException If the pcntl extension is not available. + */ + public function onSignalWithInfo(int $signal, \Closure $closure): string + { + if (!$this->signalHandling) { + throw new UnsupportedFeatureException("Signal handling requires the pcntl extension"); + } + + return parent::onSignalWithInfo($signal, $closure); + } + public function getHandle(): mixed { return null; @@ -123,7 +136,9 @@ protected function dispatch(bool $blocking): void [$signal, $siginfo] = $this->signalQueue->dequeue(); foreach ($this->signalCallbacks[$signal] as $callback) { - $callback->siginfo = $siginfo; + if ($callback instanceof SignalCallbackExtra) { + $callback->siginfo = $siginfo; + } $this->enqueueCallback($callback); } @@ -161,7 +176,7 @@ protected function activate(array $callbacks): void $this->writeStreams[$streamId] = $callback->stream; } elseif ($callback instanceof TimerCallback) { $this->timerQueue->insert($callback); - } elseif ($callback instanceof SignalCallback) { + } elseif ($callback instanceof SignalCallback || $callback instanceof SignalCallbackExtra) { if (!isset($this->signalCallbacks[$callback->signal])) { \set_error_handler(static function (int $errno, string $errstr): bool { throw new UnsupportedFeatureException( @@ -204,7 +219,7 @@ protected function deactivate(DriverCallback $callback): void } } elseif ($callback instanceof TimerCallback) { $this->timerQueue->remove($callback); - } elseif ($callback instanceof SignalCallback) { + } elseif ($callback instanceof SignalCallback || $callback instanceof SignalCallbackExtra) { if (isset($this->signalCallbacks[$callback->signal])) { unset($this->signalCallbacks[$callback->signal][$callback->id]); diff --git a/src/EventLoop/Driver/TracingDriver.php b/src/EventLoop/Driver/TracingDriver.php index 61f2440..c25fad0 100644 --- a/src/EventLoop/Driver/TracingDriver.php +++ b/src/EventLoop/Driver/TracingDriver.php @@ -116,6 +116,16 @@ public function onSignal(int $signal, \Closure $closure): string return $id; } + public function onSignalWithInfo(int $signal, \Closure $closure): string + { + $id = $this->driver->onSignalWithInfo($signal, $closure); + + $this->creationTraces[$id] = $this->formatStacktrace(\debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS)); + $this->enabledCallbacks[$id] = true; + + return $id; + } + public function enable(string $callbackId): string { try { diff --git a/src/EventLoop/Driver/UvDriver.php b/src/EventLoop/Driver/UvDriver.php index 1ae72a4..f65c392 100644 --- a/src/EventLoop/Driver/UvDriver.php +++ b/src/EventLoop/Driver/UvDriver.php @@ -11,6 +11,7 @@ use Revolt\EventLoop\Internal\StreamReadableCallback; use Revolt\EventLoop\Internal\StreamWritableCallback; use Revolt\EventLoop\Internal\TimerCallback; +use Revolt\EventLoop\UnsupportedFeatureException; final class UvDriver extends AbstractDriver { @@ -80,6 +81,14 @@ public function __construct() }; } + /** + * @throws UnsupportedFeatureException Signal info handling is not supported with the UV driver, please use onSignal instead. + */ + public function onSignalWithInfo(int $signal, \Closure $closure): string + { + throw new UnsupportedFeatureException("Signal info handling is not supported with the UV driver, please use onSignal instead."); + } + /** * {@inheritdoc} */ diff --git a/src/EventLoop/Internal/AbstractDriver.php b/src/EventLoop/Internal/AbstractDriver.php index f4f8f3a..ea98a98 100644 --- a/src/EventLoop/Internal/AbstractDriver.php +++ b/src/EventLoop/Internal/AbstractDriver.php @@ -186,7 +186,17 @@ public function onWritable($stream, \Closure $closure): string public function onSignal(int $signal, \Closure $closure): string { - $signalCallback = new SignalCallback($this->nextId++, $closure, $signal, null); + $signalCallback = new SignalCallback($this->nextId++, $closure, $signal); + + $this->callbacks[$signalCallback->id] = $signalCallback; + $this->enableQueue[$signalCallback->id] = $signalCallback; + + return $signalCallback->id; + } + + public function onSignalWithInfo(int $signal, \Closure $closure): string + { + $signalCallback = new SignalCallbackExtra($this->nextId++, $closure, $signal, null); $this->callbacks[$signalCallback->id] = $signalCallback; $this->enableQueue[$signalCallback->id] = $signalCallback; @@ -572,6 +582,10 @@ private function createCallbackFiber(): void $callback->stream ), $callback instanceof SignalCallback => ($callback->closure)( + $callback->id, + $callback->signal + ), + $callback instanceof SignalCallbackExtra => ($callback->closure)( $callback->id, $callback->signal, $callback->siginfo diff --git a/src/EventLoop/Internal/SignalCallback.php b/src/EventLoop/Internal/SignalCallback.php index 9971491..5a6c8bd 100644 --- a/src/EventLoop/Internal/SignalCallback.php +++ b/src/EventLoop/Internal/SignalCallback.php @@ -10,8 +10,7 @@ final class SignalCallback extends DriverCallback public function __construct( string $id, \Closure $closure, - public readonly int $signal, - public mixed $siginfo + public readonly int $signal ) { parent::__construct($id, $closure); } diff --git a/src/EventLoop/Internal/SignalCallbackExtra.php b/src/EventLoop/Internal/SignalCallbackExtra.php new file mode 100644 index 0000000..ddff510 --- /dev/null +++ b/src/EventLoop/Internal/SignalCallbackExtra.php @@ -0,0 +1,18 @@ +start(function (Driver $loop) use (&$invoked, &$callbackId) { - $callbackId = $loop->onSignal(SIGUSR1, function ($cId, $sig, $siginfo) use (&$callbackId, &$invoked) { + $this->start(function (StreamSelectDriver $loop) use (&$invoked, &$callbackId) { + $callbackId = $loop->onSignalWithInfo(SIGUSR1, function ($cId, $sig, $siginfo) use (&$callbackId, &$invoked) { $this->assertEquals($callbackId, $cId); $this->assertEquals(SIGUSR1, $sig); $this->assertEquals(SIGUSR1, $siginfo['signo']); @@ -126,7 +126,7 @@ public function testSignalDuringStreamSelectIgnored(): void $sockets = \stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP); - $this->start(function (Driver $loop) use ($sockets, &$signalCallbackId) { + $this->start(function (StreamSelectDriver $loop) use ($sockets, &$signalCallbackId) { $socketCallbackIds = [ $loop->onReadable($sockets[0], function () { // nothing From 9ddd1f69c85310939ebe0527aea854180e0b096d Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Sat, 22 Jul 2023 18:02:00 +0200 Subject: [PATCH 5/8] Remove breaking change --- src/EventLoop.php | 26 -------------------------- src/EventLoop/Driver.php | 23 ----------------------- src/EventLoop/Driver/EvDriver.php | 9 --------- src/EventLoop/Driver/EventDriver.php | 9 --------- src/EventLoop/Driver/UvDriver.php | 9 --------- 5 files changed, 76 deletions(-) diff --git a/src/EventLoop.php b/src/EventLoop.php index dc76d2d..347f588 100644 --- a/src/EventLoop.php +++ b/src/EventLoop.php @@ -213,32 +213,6 @@ public static function onSignal(int $signal, \Closure $closure): string return self::getDriver()->onSignal($signal, $closure); } - /** - * Execute a callback when a signal is received, also processing eventual OS-specific signal info. - * - * Warning: Installing the same signal on different instances of this interface is deemed undefined behavior. - * Implementations MAY try to detect this, if possible, but are not required to. This is due to technical - * limitations of the signals being registered globally per process. - * - * Multiple callbacks on the same signal MAY be executed in any order. - * - * The created callback MUST immediately be marked as enabled, but only be activated (i.e. callback can be called) - * right before the next tick. Callbacks MUST NOT be called in the tick they were enabled. - * - * The third parameter of the callback MAY contain an OS-specific siginfo structure, or null otherwise. - * - * @param int $signal The signal number to monitor. - * @param \Closure(string, int, mixed):void $closure The callback to execute. - * - * @return string A unique identifier that can be used to cancel, enable or disable the callback. - * - * @throws UnsupportedFeatureException If signal handling with signal information is not supported. - */ - public static function onSignalWithInfo(int $signal, \Closure $closure): string - { - return self::getDriver()->onSignalWithInfo($signal, $closure); - } - /** * Enable a callback to be active starting in the next tick. * diff --git a/src/EventLoop/Driver.php b/src/EventLoop/Driver.php index e0566cd..84e5e8a 100644 --- a/src/EventLoop/Driver.php +++ b/src/EventLoop/Driver.php @@ -174,29 +174,6 @@ public function onWritable(mixed $stream, \Closure $closure): string; */ public function onSignal(int $signal, \Closure $closure): string; - /** - * Execute a callback when a signal is received, also processing eventual OS-specific signal info. - * - * Warning: Installing the same signal on different instances of this interface is deemed undefined behavior. - * Implementations MAY try to detect this, if possible, but are not required to. This is due to technical - * limitations of the signals being registered globally per process. - * - * Multiple callbacks on the same signal MAY be executed in any order. - * - * The created callback MUST immediately be marked as enabled, but only be activated (i.e. callback can be called) - * right before the next tick. Callbacks MUST NOT be called in the tick they were enabled. - * - * The third parameter of the callback MAY contain an OS-specific siginfo structure, or null otherwise. - * - * @param int $signal The signal number to monitor. - * @param \Closure(string, int, mixed):void $closure The callback to execute. - * - * @return string A unique identifier that can be used to cancel, enable or disable the callback. - * - * @throws UnsupportedFeatureException If signal handling with signal information is not supported. - */ - public function onSignalWithInfo(int $signal, \Closure $closure): string; - /** * Enable a callback to be active starting in the next tick. * diff --git a/src/EventLoop/Driver/EvDriver.php b/src/EventLoop/Driver/EvDriver.php index 6b034b5..e0a33a3 100644 --- a/src/EventLoop/Driver/EvDriver.php +++ b/src/EventLoop/Driver/EvDriver.php @@ -13,7 +13,6 @@ use Revolt\EventLoop\Internal\StreamReadableCallback; use Revolt\EventLoop\Internal\StreamWritableCallback; use Revolt\EventLoop\Internal\TimerCallback; -use Revolt\EventLoop\UnsupportedFeatureException; final class EvDriver extends AbstractDriver { @@ -71,14 +70,6 @@ public function __construct() }; } - /** - * @throws UnsupportedFeatureException Signal info handling is not supported with the Ev driver, please use onSignal instead. - */ - public function onSignalWithInfo(int $signal, \Closure $closure): string - { - throw new UnsupportedFeatureException("Signal info handling is not supported with the Ev driver, please use onSignal instead."); - } - /** * {@inheritdoc} */ diff --git a/src/EventLoop/Driver/EventDriver.php b/src/EventLoop/Driver/EventDriver.php index 9b8e9cc..869c3a9 100644 --- a/src/EventLoop/Driver/EventDriver.php +++ b/src/EventLoop/Driver/EventDriver.php @@ -13,7 +13,6 @@ use Revolt\EventLoop\Internal\StreamReadableCallback; use Revolt\EventLoop\Internal\StreamWritableCallback; use Revolt\EventLoop\Internal\TimerCallback; -use Revolt\EventLoop\UnsupportedFeatureException; final class EventDriver extends AbstractDriver { @@ -59,14 +58,6 @@ public function __construct() }; } - /** - * @throws UnsupportedFeatureException Signal info handling is not supported with the Event driver, please use onSignal instead. - */ - public function onSignalWithInfo(int $signal, \Closure $closure): string - { - throw new UnsupportedFeatureException("Signal info handling is not supported with the Event driver, please use onSignal instead."); - } - /** * {@inheritdoc} */ diff --git a/src/EventLoop/Driver/UvDriver.php b/src/EventLoop/Driver/UvDriver.php index f65c392..1ae72a4 100644 --- a/src/EventLoop/Driver/UvDriver.php +++ b/src/EventLoop/Driver/UvDriver.php @@ -11,7 +11,6 @@ use Revolt\EventLoop\Internal\StreamReadableCallback; use Revolt\EventLoop\Internal\StreamWritableCallback; use Revolt\EventLoop\Internal\TimerCallback; -use Revolt\EventLoop\UnsupportedFeatureException; final class UvDriver extends AbstractDriver { @@ -81,14 +80,6 @@ public function __construct() }; } - /** - * @throws UnsupportedFeatureException Signal info handling is not supported with the UV driver, please use onSignal instead. - */ - public function onSignalWithInfo(int $signal, \Closure $closure): string - { - throw new UnsupportedFeatureException("Signal info handling is not supported with the UV driver, please use onSignal instead."); - } - /** * {@inheritdoc} */ From 59f4b5f690220beb24bf0a76399e82c30a7cb576 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Sat, 22 Jul 2023 18:03:46 +0200 Subject: [PATCH 6/8] Fixup --- src/EventLoop/Driver/StreamSelectDriver.php | 2 +- src/EventLoop/Driver/TracingDriver.php | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/EventLoop/Driver/StreamSelectDriver.php b/src/EventLoop/Driver/StreamSelectDriver.php index a792b4c..e94c486 100644 --- a/src/EventLoop/Driver/StreamSelectDriver.php +++ b/src/EventLoop/Driver/StreamSelectDriver.php @@ -32,7 +32,7 @@ final class StreamSelectDriver extends AbstractDriver private readonly TimerQueue $timerQueue; - /** @var array> */ + /** @var array> */ private array $signalCallbacks = []; /** @var \SplQueue */ diff --git a/src/EventLoop/Driver/TracingDriver.php b/src/EventLoop/Driver/TracingDriver.php index c25fad0..61f2440 100644 --- a/src/EventLoop/Driver/TracingDriver.php +++ b/src/EventLoop/Driver/TracingDriver.php @@ -116,16 +116,6 @@ public function onSignal(int $signal, \Closure $closure): string return $id; } - public function onSignalWithInfo(int $signal, \Closure $closure): string - { - $id = $this->driver->onSignalWithInfo($signal, $closure); - - $this->creationTraces[$id] = $this->formatStacktrace(\debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS)); - $this->enabledCallbacks[$id] = true; - - return $id; - } - public function enable(string $callbackId): string { try { From 2d3259d2c4f0edd14f24ff1422c33b57013fe510 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Mon, 24 Jul 2023 08:43:30 +0200 Subject: [PATCH 7/8] Make it protected --- src/EventLoop/Internal/AbstractDriver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EventLoop/Internal/AbstractDriver.php b/src/EventLoop/Internal/AbstractDriver.php index ea98a98..a87c518 100644 --- a/src/EventLoop/Internal/AbstractDriver.php +++ b/src/EventLoop/Internal/AbstractDriver.php @@ -194,7 +194,7 @@ public function onSignal(int $signal, \Closure $closure): string return $signalCallback->id; } - public function onSignalWithInfo(int $signal, \Closure $closure): string + protected function onSignalWithInfo(int $signal, \Closure $closure): string { $signalCallback = new SignalCallbackExtra($this->nextId++, $closure, $signal, null); From 4c1522206b1396c2e2320027c12a6499e2968f68 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Mon, 24 Jul 2023 08:55:28 +0200 Subject: [PATCH 8/8] Fix psalm --- src/EventLoop/Driver/TracingDriver.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/EventLoop/Driver/TracingDriver.php b/src/EventLoop/Driver/TracingDriver.php index 61f2440..e4713e2 100644 --- a/src/EventLoop/Driver/TracingDriver.php +++ b/src/EventLoop/Driver/TracingDriver.php @@ -249,7 +249,7 @@ private function getCancelTrace(string $callbackId): string /** * Formats a stacktrace obtained via `debug_backtrace()`. * - * @param list, class?: class-string, file: string, function: string, line: int, object?: object, type?: string}> $trace + * @param list, class?: class-string, file?: string, function: string, line?: int, object?: object, type?: string}> $trace * Output of `debug_backtrace()`. * * @return string Formatted stacktrace. @@ -259,7 +259,7 @@ private function formatStacktrace(array $trace): string return \implode("\n", \array_map(static function ($e, $i) { $line = "#{$i} "; - if (isset($e["file"])) { + if (isset($e["file"]) && isset($e['line'])) { $line .= "{$e['file']}:{$e['line']} "; }