X-Git-Url: http://www.git.stargrave.org/?a=blobdiff_plain;f=lib%2FPublicInbox%2FDS.pm;h=0a763d0ef643ad0d5c5deb8e027b9df67d4438ae;hb=HEAD;hp=9563a1cbbbb5795fc724c9b3d19b8e76dd66a7ab;hpb=6e9397d12635eae55c9114ed9689413154fed8ce;p=public-inbox.git diff --git a/lib/PublicInbox/DS.pm b/lib/PublicInbox/DS.pm index 9563a1cb..0a763d0e 100644 --- a/lib/PublicInbox/DS.pm +++ b/lib/PublicInbox/DS.pm @@ -30,13 +30,12 @@ use Time::HiRes qw(clock_gettime CLOCK_MONOTONIC); use Scalar::Util qw(blessed); use PublicInbox::Syscall qw(:epoll); use PublicInbox::Tmpfile; -use Errno qw(EAGAIN EINVAL); +use Errno qw(EAGAIN EINVAL ECHILD EINTR); use Carp qw(carp croak); -our @EXPORT_OK = qw(now msg_more dwaitpid awaitpid add_timer add_uniq_timer); +our @EXPORT_OK = qw(now msg_more awaitpid add_timer add_uniq_timer); my %Stack; my $nextq; # queue for next_tick -my $wait_pids; # list of [ pid, callback, callback_arg ] my $AWAIT_PIDS; # pid => [ $callback, @args ] my $reap_armed; my $ToClose; # sockets to close when event loop is done @@ -75,11 +74,11 @@ sub Reset { # we may be iterating inside one of these on our stack my @q = delete @Stack{keys %Stack}; for my $q (@q) { @$q = () } - $AWAIT_PIDS = $wait_pids = $nextq = $ToClose = undef; + $AWAIT_PIDS = $nextq = $ToClose = undef; $ep_io = undef; # closes real $Epoll FD $Epoll = undef; # may call DSKQXS::DESTROY - } while (@Timers || keys(%Stack) || $nextq || $wait_pids || - $ToClose || keys(%DescriptorMap) || $AWAIT_PIDS || + } while (@Timers || keys(%Stack) || $nextq || $AWAIT_PIDS || + $ToClose || keys(%DescriptorMap) || $PostLoopCallback || keys(%UniqTimer)); $reap_armed = undef; @@ -209,43 +208,23 @@ sub await_cb ($;@) { warn "E: awaitpid($pid): $@" if $@; } -# We can't use waitpid(-1) safely here since it can hit ``, system(), -# and other things. So we scan the $wait_pids list, which is hopefully -# not too big. We keep $wait_pids small by not calling dwaitpid() -# until we've hit EOF when reading the stdout of the child. - +# This relies on our Perl process is single-threaded, or at least +# no threads are spawning and waiting on processes (``, system(), etc...) +# Threads are officially discouraged by the Perl5 team, and I expect +# that to remain the case. sub reap_pids { $reap_armed = undef; - my $tmp = $wait_pids // []; - $wait_pids = undef; - $Stack{reap_runq} = $tmp; my $oldset = block_signals(); - - # old API - foreach my $ary (@$tmp) { - my ($pid, $cb, $arg) = @$ary; - my $ret = waitpid($pid, WNOHANG); - if ($ret == 0) { - push @$wait_pids, $ary; # autovivifies @$wait_pids - } elsif ($ret == $pid) { - if ($cb) { - eval { $cb->($arg, $pid) }; - warn "E: dwaitpid($pid) in_loop: $@" if $@; - } + while (1) { + my $pid = waitpid(-1, WNOHANG) // last; + last if $pid <= 0; + if (defined(my $cb_args = delete $AWAIT_PIDS->{$pid})) { + await_cb($pid, @$cb_args) if $cb_args; } else { - warn "waitpid($pid, WNOHANG) = $ret, \$!=$!, \$?=$?"; + warn "W: reaped unknown PID=$pid: \$?=$?\n"; } } - - # new API TODO: convert to waitpid(-1) in the future as long - # as we don't use threads - for my $pid (keys %$AWAIT_PIDS) { - my $wpid = waitpid($pid, WNOHANG) // next; - my $cb_args = delete $AWAIT_PIDS->{$wpid} or next; - await_cb($pid, @$cb_args); - } sig_setmask($oldset); - delete $Stack{reap_runq}; } # reentrant SIGCHLD handler (since reap_pids is not reentrant) @@ -719,40 +698,27 @@ sub long_response ($$;@) { undef; } -sub dwaitpid ($;$$) { - my ($pid, $cb, $arg) = @_; - if ($in_loop) { - push @$wait_pids, [ $pid, $cb, $arg ]; - # We could've just missed our SIGCHLD, cover it, here: - enqueue_reap(); - } else { - my $ret = waitpid($pid, 0); - if ($ret == $pid) { - if ($cb) { - eval { $cb->($arg, $pid) }; - carp "E: dwaitpid($pid) !in_loop: $@" if $@; - } - } else { - carp "waitpid($pid, 0) = $ret, \$!=$!, \$?=$?"; - } - } -} - sub awaitpid { - my ($pid, @cb_args) = @_; - $AWAIT_PIDS->{$pid} //= @cb_args ? \@cb_args : 0; + my ($pid, @cb_args) = @_; # @cb_args = ($cb, @args), $cb may be undef + $AWAIT_PIDS->{$pid} = \@cb_args if @cb_args; # provide synchronous API if (defined(wantarray) || (!$in_loop && !@cb_args)) { - my $ret = waitpid($pid, 0) // -2; + my $ret; +again: + $ret = waitpid($pid, 0) // -2; if ($ret == $pid) { my $cb_args = delete $AWAIT_PIDS->{$pid}; @cb_args = @$cb_args if !@cb_args && $cb_args; await_cb($pid, @cb_args); - return $ret; + } else { + goto again if $! == EINTR; + carp "waitpid($pid): $!"; + delete $AWAIT_PIDS->{$pid}; } + return $ret; + } elsif ($in_loop) { # We could've just missed our SIGCHLD, cover it, here: + enqueue_reap(); } - # We could've just missed our SIGCHLD, cover it, here: - enqueue_reap() if $in_loop; } 1;