Skip to content

Commit 65c0a9b

Browse files
committed
Reworked signal handling to keep the loop running as long as there are any signal handlers registered to the loop
1 parent cba4891 commit 65c0a9b

File tree

8 files changed

+52
-10
lines changed

8 files changed

+52
-10
lines changed

examples/04-signals.php

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,4 @@
1010
$loop->stop();
1111
});
1212

13-
// This timer is here to keep the loop alive, signaling by itself doesn't do that
14-
$loop->addPeriodicTimer(1, function () {
15-
echo 'Timer tick', PHP_EOL;
16-
});
17-
1813
$loop->run();

src/ExtEventLoop.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public function __construct(EventBaseConfig $config = null)
3535
$this->timerEvents = new SplObjectStorage();
3636

3737
$this->signals = new SignalsHandler(
38+
$this,
3839
function ($signal) {
3940
$this->signalEvents[$signal] = Event::signal($this->eventBase, $signal, $f = function () use ($signal, &$f) {
4041
$this->signals->call($signal);

src/LibEvLoop.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public function __construct()
3333
$this->timerEvents = new SplObjectStorage();
3434

3535
$this->signals = new SignalsHandler(
36+
$this,
3637
function ($signal) {
3738
$this->signalEvents[$signal] = new SignalEvent($f = function () use ($signal, &$f) {
3839
$this->signals->call($signal);

src/LibEventLoop.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public function __construct()
3737
$this->timerEvents = new SplObjectStorage();
3838

3939
$this->signals = new SignalsHandler(
40+
$this,
4041
function ($signal) {
4142
$this->signalEvents[$signal] = event_new();
4243
event_set($this->signalEvents[$signal], $signal, EV_PERSIST | EV_SIGNAL, $f = function () use ($signal, &$f) {

src/SignalsHandler.php

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,22 @@
22

33
namespace React\EventLoop;
44

5+
use React\EventLoop\Timer\TimerInterface;
6+
57
/**
68
* @internal
79
*/
810
final class SignalsHandler
911
{
12+
private $loop;
13+
private $timer;
1014
private $signals = [];
1115
private $on;
1216
private $off;
1317

14-
public function __construct(callable $on, callable $off)
18+
public function __construct(LoopInterface $loop, callable $on, callable $off)
1519
{
20+
$this->loop = $loop;
1621
$this->on = $on;
1722
$this->off = $off;
1823
}
@@ -27,6 +32,10 @@ public function __destruct()
2732

2833
public function add($signal, callable $listener)
2934
{
35+
if (count($this->signals) == 0 && $this->timer === null) {
36+
$this->timer = $this->loop->addPeriodicTimer(1, function () {});
37+
}
38+
3039
if (!isset($this->signals[$signal])) {
3140
$this->signals[$signal] = [];
3241

@@ -56,6 +65,11 @@ public function remove($signal, callable $listener)
5665
$off = $this->off;
5766
$off($signal);
5867
}
68+
69+
if (count($this->signals) == 0 && $this->timer instanceof TimerInterface) {
70+
$this->loop->cancelTimer($this->timer);
71+
$this->timer = null;
72+
}
5973
}
6074

6175
public function call($signal)

src/StreamSelectLoop.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public function __construct()
3131
$this->timers = new Timers();
3232
$this->pcntl = extension_loaded('pcntl');
3333
$this->signals = new SignalsHandler(
34+
$this,
3435
function ($signal) {
3536
\pcntl_signal($signal, $f = function ($signal) use (&$f) {
3637
$this->signals->call($signal);

tests/AbstractLoopTest.php

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -453,12 +453,39 @@ public function testSignalMultipleUsagesForTheSameListener()
453453
$this->assertSame(1, $funcCallCount);
454454
}
455455

456-
public function testSignalsDontKeepTheLoopRunning()
456+
public function testSignalsKeepTheLoopRunning()
457457
{
458-
$this->loop->addSignal(SIGUSR1, function () {});
459-
$this->loop->addTimer(0.1, function () {});
458+
$function = function () {};
459+
$this->loop->addSignal(SIGUSR1, $function);
460+
$this->loop->addTimer(1.5, function () use ($function) {
461+
$this->loop->removeSignal(SIGUSR1, $function);
462+
$this->loop->stop();
463+
});
464+
465+
$this->assertRunSlowerThan(1.5);
466+
}
467+
468+
public function testSignalsKeepTheLoopRunningAndRemovingItStopsTheLoop()
469+
{
470+
$function = function () {};
471+
$this->loop->addSignal(SIGUSR1, $function);
472+
$this->loop->addTimer(1.5, function () use ($function) {
473+
$this->loop->removeSignal(SIGUSR1, $function);
474+
});
475+
476+
$this->assertRunFasterThan(1.6);
477+
}
478+
479+
private function assertRunSlowerThan($minInterval)
480+
{
481+
$start = microtime(true);
482+
483+
$this->loop->run();
484+
485+
$end = microtime(true);
486+
$interval = $end - $start;
460487

461-
$this->assertRunFasterThan(0.2);
488+
$this->assertLessThan($interval, $minInterval);
462489
}
463490

464491
private function assertRunFasterThan($maxInterval)

tests/SignalsHandlerTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace React\Tests\EventLoop;
44

5+
use React\EventLoop\Factory;
56
use React\EventLoop\SignalsHandler;
67

78
final class SignalsHandlerTest extends TestCase
@@ -15,6 +16,7 @@ public function testEmittedEventsAndCallHandling()
1516
$callCount++;
1617
};
1718
$signals = new SignalsHandler(
19+
Factory::create(),
1820
function () use (&$onCount) {
1921
$onCount++;
2022
},

0 commit comments

Comments
 (0)