]> Sergey Matveev's repositories - public-inbox.git/blobdiff - lib/PublicInbox/DS.pm
ds: clobber $in_loop first at reset
[public-inbox.git] / lib / PublicInbox / DS.pm
index 12df59192cc96f890626795b32d993d0fe372029..8a560ae8b4ad53be049d051350efde8af98e0b9b 100644 (file)
 #        (tmpio = [ GLOB, offset, [ length ] ])
 package PublicInbox::DS;
 use strict;
+use v5.10.1;
+use parent qw(Exporter);
 use bytes;
 use POSIX qw(WNOHANG);
 use IO::Handle qw();
 use Fcntl qw(SEEK_SET :DEFAULT O_APPEND);
 use Time::HiRes qw(clock_gettime CLOCK_MONOTONIC);
-use parent qw(Exporter);
-our @EXPORT_OK = qw(now msg_more);
-use 5.010_001;
 use Scalar::Util qw(blessed);
 use PublicInbox::Syscall qw(:epoll);
 use PublicInbox::Tmpfile;
 use Errno qw(EAGAIN EINVAL);
 use Carp qw(confess carp);
+our @EXPORT_OK = qw(now msg_more dwaitpid);
 
 my $nextq; # queue for next_tick
 my $wait_pids; # list of [ pid, callback, callback_arg ]
@@ -66,8 +66,9 @@ Reset all state
 
 =cut
 sub Reset {
+    $in_loop = undef; # first in case DESTROY callbacks use this
     %DescriptorMap = ();
-    $in_loop = $wait_pids = $later_queue = $reap_armed = undef;
+    $wait_pids = $later_queue = $reap_armed = undef;
     $EXPMAP = {};
     $nextq = $ToClose = $later_timer = $exp_timer = undef;
     $LoopTimeout = -1;  # no timeout by default
@@ -87,9 +88,7 @@ A timeout of 0 (zero) means poll forever. A timeout of -1 means poll and return
 immediately.
 
 =cut
-sub SetLoopTimeout {
-    return $LoopTimeout = $_[1] + 0;
-}
+sub SetLoopTimeout { $LoopTimeout = $_[1] + 0 }
 
 =head2 C<< PublicInbox::DS::add_timer( $seconds, $coderef, $arg) >>
 
@@ -200,12 +199,7 @@ sub RunTimers {
     my $timeout = int(($Timers[0][0] - $now) * 1000) + 1;
 
     # -1 is an infinite timeout, so prefer a real timeout
-    return $timeout     if $LoopTimeout == -1;
-
-    # otherwise pick the lower of our regular timeout and time until
-    # the next timer
-    return $LoopTimeout if $LoopTimeout < $timeout;
-    return $timeout;
+    ($LoopTimeout < 0 || $LoopTimeout >= $timeout) ? $timeout : $LoopTimeout;
 }
 
 # We can't use waitpid(-1) safely here since it can hit ``, system(),
@@ -222,8 +216,13 @@ sub reap_pids {
                my $ret = waitpid($pid, WNOHANG);
                if ($ret == 0) {
                        push @$wait_pids, $ary; # autovivifies @$wait_pids
-               } elsif ($cb) {
-                       eval { $cb->($arg, $pid) };
+               } elsif ($ret == $pid) {
+                       if ($cb) {
+                               eval { $cb->($arg, $pid) };
+                               warn "E: dwaitpid($pid) in_loop: $@" if $@;
+                       }
+               } else {
+                       warn "waitpid($pid, WNOHANG) = $ret, \$!=$!, \$?=$?";
                }
        }
        # we may not be done, yet, and could've missed/masked a SIGCHLD:
@@ -261,19 +260,18 @@ sub PostEventLoop () {
 sub EventLoop {
     $Epoll //= _InitPoller();
     local $in_loop = 1;
+    my @events;
     do {
-        my @events;
-        my $i;
         my $timeout = RunTimers();
 
         # get up to 1000 events
-        my $evcount = epoll_wait($Epoll, 1000, $timeout, \@events);
-        for ($i=0; $i<$evcount; $i++) {
+        epoll_wait($Epoll, 1000, $timeout, \@events);
+        for my $fd (@events) {
             # it's possible epoll_wait returned many events, including some at the end
             # that ones in the front triggered unregister-interest actions.  if we
             # can't find the %sock entry, it's because we're no longer interested
             # in that event.
-            $DescriptorMap{$events[$i]->[0]}->event_step;
+            $DescriptorMap{$fd}->event_step;
         }
     } while (PostEventLoop());
     _run_later();
@@ -616,13 +614,23 @@ sub shutdn ($) {
     }
 }
 
-# must be called with eval, PublicInbox::DS may not be loaded (see t/qspawn.t)
-sub dwaitpid ($$$) {
-       die "Not in EventLoop\n" unless $in_loop;
-       push @$wait_pids, [ @_ ]; # [ $pid, $cb, $arg ]
-
-       # We could've just missed our SIGCHLD, cover it, here:
-       enqueue_reap();
+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 _run_later () {