X-Git-Url: http://www.git.stargrave.org/?a=blobdiff_plain;f=lib%2FPublicInbox%2FDS.pm;h=970061fd55f1c44c77d51084d7d9891aeb3f48f3;hb=8775167475d8bfc25d532b777147a8b1ef1cd99b;hp=2f41ea456d72cd11854bad753761a6292243733f;hpb=fdefc70113c221ce7028b230971ab88d590f5827;p=public-inbox.git diff --git a/lib/PublicInbox/DS.pm b/lib/PublicInbox/DS.pm index 2f41ea45..970061fd 100644 --- a/lib/PublicInbox/DS.pm +++ b/lib/PublicInbox/DS.pm @@ -39,9 +39,9 @@ use Errno qw(EAGAIN EINVAL); use Carp qw(confess carp); my $nextq; # queue for next_tick -my $WaitPids; # list of [ pid, callback, callback_arg ] -my $later_queue; # callbacks -my $EXPMAP; # fd -> [ idle_time, $self ] +my $wait_pids; # list of [ pid, callback, callback_arg ] +my $later_queue; # list of callbacks to run at some later interval +my $EXPMAP; # fd -> idle_time our $EXPTIME = 180; # 3 minutes my ($later_timer, $reap_timer, $exp_timer); my $ToClose; # sockets to close when event loop is done @@ -71,8 +71,7 @@ Reset all state =cut sub Reset { %DescriptorMap = (); - $WaitPids = []; - $later_queue = []; + $wait_pids = $later_queue = undef; $EXPMAP = {}; $nextq = $ToClose = $reap_timer = $later_timer = $exp_timer = undef; $LoopTimeout = -1; # no timeout by default @@ -226,25 +225,23 @@ sub RunTimers { } # We can't use waitpid(-1) safely here since it can hit ``, system(), -# and other things. So we scan the $WaitPids list, which is hopefully -# not too big. +# 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. sub reap_pids { - my $tmp = $WaitPids; - $WaitPids = []; - $reap_timer = undef; + my $tmp = $wait_pids or return; + $wait_pids = $reap_timer = undef; foreach my $ary (@$tmp) { my ($pid, $cb, $arg) = @$ary; my $ret = waitpid($pid, WNOHANG); if ($ret == 0) { - push @$WaitPids, $ary; + push @$wait_pids, $ary; # autovivifies @$wait_pids } elsif ($cb) { eval { $cb->($arg, $pid) }; } } - if (@$WaitPids) { - # we may not be donea, and we may miss our - $reap_timer = add_timer(1, \&reap_pids); - } + # we may not be done, yet, and could've missed/masked a SIGCHLD: + $reap_timer = add_timer(1, \&reap_pids) if $wait_pids; } # reentrant SIGCHLD handler (since reap_pids is not reentrant) @@ -262,9 +259,13 @@ sub PostEventLoop () { # loop) if (my $close_now = $ToClose) { $ToClose = undef; # will be autovivified on push + @$close_now = map { fileno($_) } @$close_now; + + # order matters, destroy expiry times, first: + delete @$EXPMAP{@$close_now}; + # ->DESTROY methods may populate ToClose - delete($DescriptorMap{fileno($_)}) for @$close_now; - # let refcounting drop everything in $close_now at once + delete @DescriptorMap{@$close_now}; } # by default we keep running, unless a postloop callback cancels it @@ -626,62 +627,53 @@ sub shutdn ($) { # must be called with eval, PublicInbox::DS may not be loaded (see t/qspawn.t) sub dwaitpid ($$$) { - my ($pid, $cb, $arg) = @_; - if ($in_loop) { - push @$WaitPids, [ $pid, $cb, $arg ]; + die "Not in EventLoop\n" unless $in_loop; + push @$wait_pids, [ @_ ]; # [ $pid, $cb, $arg ] - # We could've just missed our SIGCHLD, cover it, here: - requeue(\&reap_pids); - } else { - die "Not in EventLoop\n"; - } + # We could've just missed our SIGCHLD, cover it, here: + requeue(\&reap_pids); } sub _run_later () { - my $run = $later_queue; - $later_timer = undef; - $later_queue = []; - $_->() for @$run; + my $run = $later_queue or return; + $later_timer = $later_queue = undef; + $_->() for @$run; } sub later ($) { - my ($cb) = @_; - push @$later_queue, $cb; - $later_timer //= add_timer(60, \&_run_later); + push @$later_queue, $_[0]; # autovivifies @$later_queue + $later_timer //= add_timer(60, \&_run_later); } sub expire_old () { - my $now = now(); - my $exp = $EXPTIME; - my $old = $now - $exp; - my %new; - while (my ($fd, $v) = each %$EXPMAP) { - my ($idle_time, $ds_obj) = @$v; - if ($idle_time < $old) { - if (!$ds_obj->shutdn) { - $new{$fd} = $v; - } - } else { - $new{$fd} = $v; - } - } - $EXPMAP = \%new; - $exp_timer = scalar(keys %new) ? later(\&expire_old) : undef; + my $now = now(); + my $exp = $EXPTIME; + my $old = $now - $exp; + my %new; + while (my ($fd, $idle_at) = each %$EXPMAP) { + if ($idle_at < $old) { + my $ds_obj = $DescriptorMap{$fd}; + $new{$fd} = $idle_at if !$ds_obj->shutdn; + } else { + $new{$fd} = $idle_at; + } + } + $EXPMAP = \%new; + $exp_timer = scalar(keys %new) ? later(\&expire_old) : undef; } sub update_idle_time { - my ($self) = @_; - my $sock = $self->{sock} or return; - $EXPMAP->{fileno($sock)} = [ now(), $self ]; - $exp_timer //= later(\&expire_old); + my ($self) = @_; + my $sock = $self->{sock} or return; + $EXPMAP->{fileno($sock)} = now(); + $exp_timer //= later(\&expire_old); } sub not_idle_long { - my ($self, $now) = @_; - my $sock = $self->{sock} or return; - my $ary = $EXPMAP->{fileno($sock)} or return; - my $exp_at = $ary->[0] + $EXPTIME; - $exp_at > $now; + my ($self, $now) = @_; + my $sock = $self->{sock} or return; + my $idle_at = $EXPMAP->{fileno($sock)} or return; + ($idle_at + $EXPTIME) > $now; } 1;