Fix FPM timer event re-registration

Make sure that fpm_event_add calls inside a timer callback work by
unregistering the event from the queue before invoking its callback.

The read timeout in tester.inc is increased because the added test
needs two seconds (one for SIGTERM, one for SIGKILL) until the
reload succeeds, so we should wait longer than that for a response.
This commit is contained in:
Nikita Popov 2019-07-23 15:54:35 +02:00
parent 6913ec3282
commit 0ed6c37140
3 changed files with 72 additions and 12 deletions

View File

@ -433,17 +433,16 @@ void fpm_event_loop(int err) /* {{{ */
/* trigger timers */
q = fpm_event_queue_timer;
while (q) {
struct fpm_event_queue_s *next = q->next;
fpm_clock_get(&now);
if (q->ev) {
if (timercmp(&now, &q->ev->timeout, >) || timercmp(&now, &q->ev->timeout, ==)) {
fpm_event_fire(q->ev);
/* sanity check */
if (fpm_globals.parent_pid != getpid()) {
return;
}
if (q->ev->flags & FPM_EV_PERSIST) {
fpm_event_set_timeout(q->ev, now);
} else { /* delete the event */
struct fpm_event_s *ev = q->ev;
if (ev->flags & FPM_EV_PERSIST) {
fpm_event_set_timeout(ev, now);
} else {
/* Delete the event. Make sure this happens before it is fired,
* so that the event callback may register the same timer again. */
q2 = q;
if (q->prev) {
q->prev->next = q->next;
@ -457,13 +456,18 @@ void fpm_event_loop(int err) /* {{{ */
fpm_event_queue_timer->prev = NULL;
}
}
q = q->next;
free(q2);
continue;
}
fpm_event_fire(ev);
/* sanity check */
if (fpm_globals.parent_pid != getpid()) {
return;
}
}
}
q = q->next;
q = next;
}
}
}

View File

@ -0,0 +1,56 @@
--TEST--
If SIGQUIT and SIGTERM during reloading fail, SIGKILL should be sent
--SKIPIF--
<?php
include "skipif.inc";
if (!function_exists('pcntl_sigprocmask')) die('skip Requires pcntl_sigprocmask()');
?>
--FILE--
<?php
require_once "tester.inc";
$cfg = <<<EOT
[global]
error_log = {{FILE:LOG}}
pid = {{FILE:PID}}
process_control_timeout=1
[unconfined]
listen = {{ADDR}}
ping.path = /ping
ping.response = pong
pm = dynamic
pm.max_children = 5
pm.start_servers = 1
pm.min_spare_servers = 1
pm.max_spare_servers = 1
EOT;
$code = <<<EOT
<?php
pcntl_sigprocmask(SIG_BLOCK, [SIGQUIT, SIGTERM]);
EOT;
$tester = new FPM\Tester($cfg, $code);
$tester->start();
$tester->expectLogStartNotices();
$tester->request()->expectEmptyBody();
$tester->signal('USR2');
$tester->expectLogNotice('Reloading in progress ...');
$tester->expectLogNotice('reloading: .*');
$tester->expectLogNotice('using inherited socket fd=\d+, "127.0.0.1:\d+"');
$tester->expectLogStartNotices();
$tester->ping('{{ADDR}}');
$tester->terminate();
$tester->expectLogTerminatingNotices();
$tester->close();
?>
Done
--EXPECT--
Done
--CLEAN--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>

View File

@ -623,7 +623,7 @@ class Tester
$read = [$this->outDesc];
$write = null;
$except = null;
if (stream_select($read, $write, $except, 2 )) {
if (stream_select($read, $write, $except, 3)) {
return fgets($this->outDesc);
} else {
return null;