Skip to content

Commit

Permalink
Improve command-signals, adds unregister support (#547)
Browse files Browse the repository at this point in the history
* Improve command-signals, adds unregister support

* Refactor signal handling logic in SignalRegistry class

* Add pushSignalHandler method to SignalRegistry class

* Refactor untrap method to accept optional signo parameter

---------

Co-authored-by: Deeka Wong <[email protected]>
  • Loading branch information
huangdijia and huangdijia committed Feb 4, 2024
1 parent 877870b commit a0e259a
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 35 deletions.
1 change: 1 addition & 0 deletions .vscode/cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"Traceparent",
"ucsplit",
"undot",
"untrap",
"Wireable"
]
}
60 changes: 37 additions & 23 deletions src/command-signals/src/SignalRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,62 +19,76 @@

class SignalRegistry
{
/**
* @var array<int, callable[]>
*/
protected array $signalHandlers = [];

/**
* @var int[]
*/
protected array $handling = [];

protected bool $unregistered = false;

public function __construct(protected int $timeout = 1, protected int $concurrent = 0)
public function __construct(protected int $timeout = 1, protected int $concurrentLimit = 0)
{
}

/**
* @param int|int[] $signo
* @param callable(int $signo): void $signalHandler
*/
public function register(int|array $signo, callable $signalHandler): void
{
if (is_array($signo)) {
array_map(fn ($s) => $this->register((int) $s, $signalHandler), $signo);
array_map(fn ($s) => $this->pushSignalHandler((int) $s, $signalHandler), $signo);
return;
}

$this->signalHandlers[$signo] ??= [];
$this->signalHandlers[$signo][] = $signalHandler;

if ($this->isSignalHandling($signo)) {
return;
}
$this->pushSignalHandler($signo, $signalHandler);

$this->handleSignal($signo);
}

public function unregister(): void
/**
* @param int|int[]|null $signo
*/
public function unregister(int|array|null $signo = null): void
{
$this->unregistered = true;
match (true) {
// Unregister all signals
is_null($signo) => $this->signalHandlers = [],
// Unregister multiple signals
is_array($signo) => array_map(fn ($s) => isset($this->signalHandlers[$s]) && $this->signalHandlers[$s] = [], $signo),
// Unregister single signal
default => $this->signalHandlers[$signo] = [],
};
}

/**
* @param callable(int $signo): void $signalHandler
*/
protected function pushSignalHandler(int $signo, callable $signalHandler): void
{
$this->signalHandlers[$signo] ??= [];
$this->signalHandlers[$signo][] = $signalHandler;
}

protected function handleSignal(int $signo): void
{
if (isset($this->handling[$signo])) {
return;
}

$this->handling[$signo] = Coroutine::create(function () use ($signo) {
defer(fn () => posix_kill(posix_getpid(), $signo));

while (true) {
if (Signal::wait($signo, $this->timeout)) {
$callbacks = array_map(fn ($callback) => fn () => $callback($signo), $this->signalHandlers[$signo]);

return parallel($callbacks, $this->concurrent);
}
$callbacks = array_map(fn ($callback) => fn () => $callback($signo), $this->signalHandlers[$signo] ?? []);

if ($this->unregistered) {
break;
return parallel($callbacks, $this->concurrentLimit);
}
}
});
}

protected function isSignalHandling(int $signo): bool
{
return isset($this->handling[$signo]);
}
}
24 changes: 12 additions & 12 deletions src/command-signals/src/Traits/InteractsWithSignals.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,32 @@
namespace FriendsOfHyperf\CommandSignals\Traits;

use FriendsOfHyperf\CommandSignals\SignalRegistry;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use TypeError;

use function Hyperf\Coroutine\defer;
use function Hyperf\Support\make;

trait InteractsWithSignals
{
protected ?SignalRegistry $SignalRegistry = null;
protected ?SignalRegistry $signalRegistry = null;

/**
* Define a callback to be run when the given signal(s) occurs.
*
* @param callable(int $signal): void $callback
* @throws TypeError
* @throws NotFoundExceptionInterface
* @throws ContainerExceptionInterface
* @param int|int[] $signo
* @param callable(int $signo): void $callback
*/
protected function trap(array|int $signo, callable $callback): void
{
if (! $this->SignalRegistry) {
$this->SignalRegistry = make(SignalRegistry::class);
defer(fn () => $this->SignalRegistry->unregister());
if (! $this->signalRegistry) {
$this->signalRegistry = make(SignalRegistry::class);
defer(fn () => $this->signalRegistry->unregister());
}

$this->SignalRegistry->register($signo, $callback);
$this->signalRegistry->register($signo, $callback);
}

protected function untrap(array|int|null $signo = null): void
{
$this->signalRegistry?->unregister($signo);
}
}

0 comments on commit a0e259a

Please sign in to comment.