Skip to content

Commit 57fcfed

Browse files
Merge pull request #27 from PHPFastCGI/shutdown
Added flag shutdown functionality
2 parents 1bcd065 + d07a625 commit 57fcfed

File tree

7 files changed

+97
-7
lines changed

7 files changed

+97
-7
lines changed

src/Command/DaemonRunCommand.php

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ class DaemonRunCommand extends Command
2727
*/
2828
private $driverContainer;
2929

30+
/**
31+
* @var DaemonInterface
32+
*/
33+
private $daemon;
34+
3035
/**
3136
* Constructor.
3237
*
@@ -39,6 +44,7 @@ public function __construct(KernelInterface $kernel, DriverContainerInterface $d
3944
{
4045
$this->kernel = $kernel;
4146
$this->driverContainer = $driverContainer;
47+
$this->daemon = null;
4248

4349
$name = $name ?: self::DEFAULT_NAME;
4450
$description = $description ?: self::DEFAULT_DESCRIPTION;
@@ -97,15 +103,27 @@ protected function execute(InputInterface $input, OutputInterface $output)
97103

98104
if (null !== $port) {
99105
// If we have the port, create a TCP daemon
100-
$daemon = $daemonFactory->createTcpDaemon($this->kernel, $daemonOptions, $host ?: 'localhost', $port);
106+
$this->daemon = $daemonFactory->createTcpDaemon($this->kernel, $daemonOptions, $host ?: 'localhost', $port);
101107
} elseif (null !== $host) {
102108
// If we have the host but not the port, we cant create a TCP daemon - throw exception
103109
throw new \InvalidArgumentException('TCP port option must be set if host option is set');
104110
} else {
105111
// With no host or port, listen on FCGI_LISTENSOCK_FILENO (default)
106-
$daemon = $daemonFactory->createDaemon($this->kernel, $daemonOptions, $fd);
112+
$this->daemon = $daemonFactory->createDaemon($this->kernel, $daemonOptions, $fd);
113+
}
114+
115+
$this->daemon->run();
116+
}
117+
118+
/**
119+
* Flag the daemon for shutdown.
120+
*/
121+
public function flagShutdown()
122+
{
123+
if (null === $this->daemon) {
124+
throw new \RuntimeException('There is no daemon running');
107125
}
108126

109-
$daemon->run();
127+
$this->daemon->flagShutdown();
110128
}
111129
}

src/DaemonInterface.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,10 @@ interface DaemonInterface
4747
* @throws \Exception On fatal error
4848
*/
4949
public function run();
50+
51+
/**
52+
* Flag the daemon for shutting down. This will stop it from accepting
53+
* requests.
54+
*/
55+
public function flagShutdown();
5056
}

src/DaemonTrait.php

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99

1010
trait DaemonTrait
1111
{
12+
/**
13+
* @var bool
14+
*/
15+
private $isShutdown = false;
16+
1217
/**
1318
* @var int
1419
*/
@@ -24,6 +29,14 @@ trait DaemonTrait
2429
*/
2530
private $memoryLimit;
2631

32+
/**
33+
* Flags the daemon for shutting down.
34+
*/
35+
public function flagShutdown()
36+
{
37+
$this->isShutdown = true;
38+
}
39+
2740
/**
2841
* Loads to configuration from the daemon options and installs signal
2942
* handlers.
@@ -76,12 +89,17 @@ private function installSignalHandlers()
7689

7790
/**
7891
* Checks the current PHP process against the limits specified in a daemon
79-
* options object.
92+
* options object. This function will also throw an exception if the daemon
93+
* has been flagged for shutdown.
8094
*
8195
* @throws ShutdownException When limits in the daemon options are exceeded
8296
*/
8397
private function checkDaemonLimits()
8498
{
99+
if ($this->isShutdown) {
100+
throw new ShutdownException('Daemon flagged for shutdown');
101+
}
102+
85103
pcntl_signal_dispatch();
86104

87105
if (DaemonOptions::NO_LIMIT !== $this->requestLimit) {

src/Exception/ShutdownException.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@
88
* They may be triggered by a SIGINT or by exceeding limits specified in the
99
* daemon configuration (such as memory and request limits).
1010
*/
11-
class ShutdownException extends \RuntimeException
11+
class ShutdownException extends DaemonException
1212
{
1313
}

test/Command/DaemonRunCommandTest.php

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,35 @@ public function testPortOptionOnly()
129129
$this->assertEquals('getFactory', $context['driverContainer']->getDelegatedCalls()[0][0]);
130130
}
131131

132+
/**
133+
* Test flag shutdown.
134+
*/
135+
public function testShutdown()
136+
{
137+
$expectations = ['driver' => 'userland'];
138+
139+
$context = $this->createTestingContext($expectations);
140+
141+
$context['command']->run(new ArrayInput([]), new NullOutput());
142+
$context['command']->flagShutdown();
143+
144+
$this->assertEquals('run', $context['daemon']->getDelegatedCalls()[0][0]);
145+
$this->assertEquals('flagShutdown', $context['daemon']->getDelegatedCalls()[1][0]);
146+
$this->assertEquals('createDaemon', $context['daemonFactory']->getDelegatedCalls()[0][0]);
147+
$this->assertEquals('getFactory', $context['driverContainer']->getDelegatedCalls()[0][0]);
148+
}
149+
150+
/**
151+
* Test flag shutdown with no daemon.
152+
*
153+
* @expectedException \RuntimeException
154+
*/
155+
public function testShutdownNoDaemon()
156+
{
157+
$command = new DaemonRunCommand(new MockKernel(), new MockDriverContainer());
158+
$command->flagShutdown();
159+
}
160+
132161
/**
133162
* Construct mock objects set to test for expected values of different
134163
* parameters when specified.
@@ -146,7 +175,7 @@ private function createTestingContext(array $expectations)
146175
};
147176

148177
$mockKernel = new MockKernel();
149-
$mockDaemon = new MockDaemon(['run' => false]);
178+
$mockDaemon = new MockDaemon(['run' => false, 'flagShutdown' => false]);
150179

151180
$mockDaemonFactory = new MockDaemonFactory([
152181
'createTcpDaemon' => function ($kernel, $options, $host, $port) use ($assertExpected, $mockKernel, $mockDaemon) {

test/Driver/Userland/UserlandDaemonTest.php

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,21 @@
1919
class UserlandDaemonTest extends \PHPUnit_Framework_TestCase
2020
{
2121
/**
22-
* Tests that the daemon shuts down after reaching its request .
22+
* Tests that the daemon shuts down after being flagged for shutdown.
23+
*/
24+
public function testFlagShutdown()
25+
{
26+
// Set a memory limit to make sure that it isn't breached (added 10MB on top of peak usage)
27+
$context = $this->createTestingContext(1, memory_get_peak_usage() + (10 * 1024 * 1024));
28+
29+
$context['daemon']->flagShutdown();
30+
$context['daemon']->run();
31+
32+
$this->assertEquals('Daemon flagged for shutdown', $context['logger']->getMessages()[0]['message']);
33+
}
34+
35+
/**
36+
* Tests that the daemon shuts down after reaching its request.
2337
*/
2438
public function testRequestLimit()
2539
{

test/Helper/Mocker/MockDaemon.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,9 @@ public function run()
1212
{
1313
return $this->delegateCall('run', func_get_args());
1414
}
15+
16+
public function flagShutdown()
17+
{
18+
return $this->delegateCall('flagShutdown', func_get_args());
19+
}
1520
}

0 commit comments

Comments
 (0)