about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <e@80x24.org>2023-10-04 03:49:16 +0000
committerEric Wong <e@80x24.org>2023-10-04 17:46:32 +0000
commit4b3e8437ecf20493e601a30cbbb8ea1ce73deb82 (patch)
tree299baebcf4b47a3c9e382acb61a3d9757f7c183d
parent529b30867c07ad95bba396cc80b044315fbc4df0 (diff)
downloadpublic-inbox-4b3e8437ecf20493e601a30cbbb8ea1ce73deb82.tar.gz
Compared to Danga::Socket, our @post_loop_do API is designed to
make it easier to avoid anonymous subs (and their potential for
leaks in buggy old versions of Perl).
-rw-r--r--lib/PublicInbox/Daemon.pm38
-rw-r--r--lib/PublicInbox/LEI.pm49
-rw-r--r--lib/PublicInbox/Watch.pm4
3 files changed, 48 insertions, 43 deletions
diff --git a/lib/PublicInbox/Daemon.pm b/lib/PublicInbox/Daemon.pm
index e5755981..a4c99cca 100644
--- a/lib/PublicInbox/Daemon.pm
+++ b/lib/PublicInbox/Daemon.pm
@@ -353,31 +353,33 @@ EOF
         bless { pid => $$, pid_file => \$pid_file }, __PACKAGE__;
 }
 
+sub has_busy_clients { # post_loop_do CB
+        my ($state) = @_;
+        my $now = now();
+        my $n = PublicInbox::DS::close_non_busy();
+        if ($n) {
+                if ($state->{-w} < now()) {
+                        warn "$$ quitting, $n client(s) left\n";
+                        $state->{-w} = now() + 5;
+                }
+                unless (defined $state->{0}) {
+                        $state->{0} = (split(/\s+/, $0))[0];
+                        $state->{0} =~ s!\A.*?([^/]+)\z!$1!;
+                }
+                $0 = "$state->{0} quitting, $n client(s) left";
+        }
+        $n; # true: loop continues, false: loop breaks
+}
+
 sub worker_quit { # $_[0] = signal name or number (unused)
         # killing again terminates immediately:
         exit unless @listeners;
 
         $_->close foreach @listeners; # call PublicInbox::DS::close
         @listeners = ();
-        my $proc_name;
-        my $warn = 0;
+
         # drop idle connections and try to quit gracefully
-        @PublicInbox::DS::post_loop_do = (sub {
-                my $now = now();
-                my $n = PublicInbox::DS::close_non_busy();
-                if ($n) {
-                        if (($warn + 5) < now()) {
-                                warn "$$ quitting, $n client(s) left\n";
-                                $warn = now();
-                        }
-                        unless (defined $proc_name) {
-                                $proc_name = (split(/\s+/, $0))[0];
-                                $proc_name =~ s!\A.*?([^/]+)\z!$1!;
-                        }
-                        $0 = "$proc_name quitting, $n client(s) left";
-                }
-                $n; # true: loop continues, false: loop breaks
-        });
+        @PublicInbox::DS::post_loop_do = (\&has_busy_clients, { -w => 0 })
 }
 
 sub reopen_logs {
diff --git a/lib/PublicInbox/LEI.pm b/lib/PublicInbox/LEI.pm
index afed84c1..74a7f5b9 100644
--- a/lib/PublicInbox/LEI.pm
+++ b/lib/PublicInbox/LEI.pm
@@ -1271,13 +1271,30 @@ sub dir_idle_handler ($) { # PublicInbox::DirIdle callback
         }
 }
 
-sub drop_all_stores () {
-        for my $cfg (values %PATH2CFG) {
-                my $sto = delete($cfg->{-lei_store}) // next;
-                eval { $sto->wq_io_do('done') };
-                warn "E: $@ (dropping store for $cfg->{-f})" if $@;
-                $sto->wq_close;
+sub can_stay_alive { # PublicInbox::DS::post_loop_do cb
+        my ($path, $dev_ino_expect) = @_;
+        if (my @st = defined($$path) ? stat($$path) : ()) {
+                if ($dev_ino_expect ne pack('dd', $st[0], $st[1])) {
+                        warn "$$path dev/ino changed, quitting\n";
+                        $$path = undef;
+                }
+        } elsif (defined($$path)) { # ENOENT is common
+                warn "stat($$path): $!, quitting ...\n" if $! != ENOENT;
+                undef $$path;
+                $quit->();
         }
+        return 1 if defined($$path);
+        my $n = PublicInbox::DS::close_non_busy() or do {
+                # drop stores only if no clients
+                for my $cfg (values %PATH2CFG) {
+                        my $sto = delete($cfg->{-lei_store}) // next;
+                        eval { $sto->wq_io_do('done') };
+                        warn "E: $@ (dropping store for $cfg->{-f})" if $@;
+                        $sto->wq_close;
+                }
+        };
+        # returns true: continue, false: stop
+        $n + scalar(keys(%$PublicInbox::DS::AWAIT_PIDS));
 }
 
 # lei(1) calls this when it can't connect
@@ -1354,24 +1371,8 @@ sub lazy_start {
                 dir_idle_handler($_[0]) if $_[0]->fullname ne $path;
         });
         $dir_idle->add_watches([$sock_dir]);
-        local @PublicInbox::DS::post_loop_do = (sub {
-                if (@st = defined($path) ? stat($path) : ()) {
-                        if ($dev_ino_expect ne pack('dd', $st[0], $st[1])) {
-                                warn "$path dev/ino changed, quitting\n";
-                                $path = undef;
-                        }
-                } elsif (defined($path)) { # ENOENT is common
-                        warn "stat($path): $!, quitting ...\n" if $! != ENOENT;
-                        undef $path;
-                        $quit->();
-                }
-                return 1 if defined($path);
-                my $n = PublicInbox::DS::close_non_busy() or
-                        drop_all_stores(); # drop stores only if no clients
-                # returns true: continue, false: stop
-                $n + scalar(keys(%$PublicInbox::DS::AWAIT_PIDS));
-        });
-
+        local @PublicInbox::DS::post_loop_do = (\&can_stay_alive,
+                                                \$path, $dev_ino_expect);
         # STDIN was redirected to /dev/null above, closing STDERR and
         # STDOUT will cause the calling `lei' client process to finish
         # reading the <$daemon> pipe.
diff --git a/lib/PublicInbox/Watch.pm b/lib/PublicInbox/Watch.pm
index cf0720e3..3426d4a7 100644
--- a/lib/PublicInbox/Watch.pm
+++ b/lib/PublicInbox/Watch.pm
@@ -533,6 +533,8 @@ sub watch_nntp_init ($$) {
         }
 }
 
+sub quit_inprogress { !$_[0]->quit_done } # post_loop_do CB
+
 sub watch { # main entry point
         my ($self, $sig) = @_;
         my $first_sig;
@@ -545,7 +547,7 @@ sub watch { # main entry point
                 add_timer(0, \&poll_fetch_fork, $self, $intvl, $uris);
         }
         watch_fs_init($self) if $self->{mdre};
-        local @PublicInbox::DS::post_loop_do = (sub { !$self->quit_done });
+        local @PublicInbox::DS::post_loop_do = (\&quit_inprogress, $self);
         PublicInbox::DS::event_loop($first_sig); # calls ->event_step
         _done_for_now($self);
 }