From 0ed6c3714087b254c49185568df96a31df76b2ce Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 23 Jul 2019 15:54:35 +0200 Subject: [PATCH] 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. --- sapi/fpm/fpm/fpm_events.c | 26 +++++---- .../reload-uses-sigkill-as-last-measure.phpt | 56 +++++++++++++++++++ sapi/fpm/tests/tester.inc | 2 +- 3 files changed, 72 insertions(+), 12 deletions(-) create mode 100644 sapi/fpm/tests/reload-uses-sigkill-as-last-measure.phpt diff --git a/sapi/fpm/fpm/fpm_events.c b/sapi/fpm/fpm/fpm_events.c index 81db9f8867..86592050c6 100644 --- a/sapi/fpm/fpm/fpm_events.c +++ b/sapi/fpm/fpm/fpm_events.c @@ -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; } } } diff --git a/sapi/fpm/tests/reload-uses-sigkill-as-last-measure.phpt b/sapi/fpm/tests/reload-uses-sigkill-as-last-measure.phpt new file mode 100644 index 0000000000..25c2cf25e2 --- /dev/null +++ b/sapi/fpm/tests/reload-uses-sigkill-as-last-measure.phpt @@ -0,0 +1,56 @@ +--TEST-- +If SIGQUIT and SIGTERM during reloading fail, SIGKILL should be sent +--SKIPIF-- + +--FILE-- +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-- + diff --git a/sapi/fpm/tests/tester.inc b/sapi/fpm/tests/tester.inc index 12fe3dc2de..8d17db2e60 100644 --- a/sapi/fpm/tests/tester.inc +++ b/sapi/fpm/tests/tester.inc @@ -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; -- 2.40.0