]> Sergey Matveev's repositories - public-inbox.git/commitdiff
daemon: avoid race when quitting workers
authorEric Wong <e@80x24.org>
Sun, 24 Nov 2019 00:22:30 +0000 (00:22 +0000)
committerEric Wong <e@80x24.org>
Sun, 24 Nov 2019 21:35:19 +0000 (21:35 +0000)
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

index 90f111370e9a23b1a740adc7e486add82d977f44..0e3b95d27e6431e09251ab89da5217d224b4cd44 100644 (file)
@@ -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;