From 8576a48b2344905229737fde45498c80a1171ca5 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sun, 24 Nov 2019 00:22:30 +0000 Subject: daemon: avoid race when quitting workers While the master process has a self-pipe to avoid missing signals, worker processes lack that aside from a pipe to detect master death. That pipe doesn't exist when there's no master process, so it's possible DS::close never finishes because it never woke up from epoll_wait. So create a pipe on the worker_quit signal and force it into epoll/kevent so it wakes up right away. --- lib/PublicInbox/Daemon.pm | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'lib/PublicInbox/Daemon.pm') diff --git a/lib/PublicInbox/Daemon.pm b/lib/PublicInbox/Daemon.pm index 90f11137..0e3b95d2 100644 --- a/lib/PublicInbox/Daemon.pm +++ b/lib/PublicInbox/Daemon.pm @@ -252,6 +252,11 @@ sub daemonize () { } } +sub shrink_pipes { + if ($^O eq 'linux') { # 1031: F_SETPIPE_SZ, 4096: page size + fcntl($_, 1031, 4096) for @_; + } +} sub worker_quit { # killing again terminates immediately: @@ -260,6 +265,17 @@ sub worker_quit { $_->close foreach @listeners; # call PublicInbox::DS::close @listeners = (); + # create a lazy self-pipe which kicks us out of the EventLoop + # so DS::PostEventLoop can fire + if (pipe(my ($r, $w))) { + shrink_pipes($w); + + # shrink_pipes == noop + PublicInbox::ParentPipe->new($r, *shrink_pipes); + close $w; # wake up from the event loop + } else { + warn "E: pipe failed ($!), quit unreliable\n"; + } my $proc_name; my $warn = 0; # drop idle connections and try to quit gracefully @@ -468,10 +484,7 @@ sub unlink_pid_file_safe_ish ($$) { sub master_loop { pipe(my ($p0, $p1)) or die "failed to create parent-pipe: $!"; pipe(my ($r, $w)) or die "failed to create self-pipe: $!"; - - if ($^O eq 'linux') { # 1031: F_SETPIPE_SZ = 1031 - fcntl($_, 1031, 4096) for ($w, $p1); - } + shrink_pipes($w, $p1); IO::Handle::blocking($w, 0); my $set_workers = $worker_processes; -- cgit v1.2.3-24-ge0c7