about summary refs log tree commit homepage
path: root/lib/PublicInbox/DSPoll.pm
diff options
context:
space:
mode:
authorEric Wong <e@80x24.org>2023-10-30 18:29:40 +0000
committerEric Wong <e@80x24.org>2023-10-31 03:46:30 +0000
commit20b12985e9deda56f1a0de4a16d99e3ac389a6d4 (patch)
tree14fe61b94ecb413d72eb8cfc3d9cfc561beaa548 /lib/PublicInbox/DSPoll.pm
parentd1a6fa153b50c0ae7deca6e7aa1ac49904701805 (diff)
downloadpublic-inbox-20b12985e9deda56f1a0de4a16d99e3ac389a6d4.tar.gz
I hit this in via select running -cindex with some other
experimental patches.  I can't reproduce the problem, though,
but this ensure we have a chance to diagnose it if it happens
again instead of looping on select(2) => EBADF.
Diffstat (limited to 'lib/PublicInbox/DSPoll.pm')
-rw-r--r--lib/PublicInbox/DSPoll.pm31
1 files changed, 20 insertions, 11 deletions
diff --git a/lib/PublicInbox/DSPoll.pm b/lib/PublicInbox/DSPoll.pm
index fc282de0..0446df4c 100644
--- a/lib/PublicInbox/DSPoll.pm
+++ b/lib/PublicInbox/DSPoll.pm
@@ -12,30 +12,39 @@ package PublicInbox::DSPoll;
 use v5.12;
 use IO::Poll;
 use PublicInbox::Syscall qw(EPOLLONESHOT EPOLLIN EPOLLOUT);
+use Carp qw(carp);
+use Errno ();
 
 sub new { bless {}, __PACKAGE__ } # fd => events
 
 sub ep_wait {
         my ($self, $maxevents, $timeout_msec, $events) = @_;
-        my @pset;
+        my (@pset, $n, $fd, $revents, $nval);
         while (my ($fd, $events) = each %$self) {
                 my $pevents = $events & EPOLLIN ? POLLIN : 0;
                 $pevents |= $events & EPOLLOUT ? POLLOUT : 0;
                 push(@pset, $fd, $pevents);
         }
         @$events = ();
-        my $n = IO::Poll::_poll($timeout_msec, @pset);
-        if ($n >= 0) {
-                for (my $i = 0; $i < @pset; ) {
-                        my $fd = $pset[$i++];
-                        my $revents = $pset[$i++] or next;
-                        delete($self->{$fd}) if $self->{$fd} & EPOLLONESHOT;
+        do {
+                $n = IO::Poll::_poll($timeout_msec, @pset);
+        } while ($n < 0 && $! == Errno::EINTR);
+        die "poll: $!" if $n < 0;
+        return if $n == 0;
+        while (defined($fd = shift @pset)) {
+                $revents = shift @pset or next; # no event
+                if ($revents & POLLNVAL) {
+                        carp "E: FD=$fd invalid in poll";
+                        delete $self->{$fd};
+                        $nval = 1;
+                } else {
+                        delete $self->{$fd} if $self->{$fd} & EPOLLONESHOT;
                         push @$events, $fd;
                 }
-                my $nevents = scalar @$events;
-                if ($n != $nevents) {
-                        warn "BUG? poll() returned $n, but got $nevents";
-                }
+        }
+        if ($nval && !@$events) {
+                $! = Errno::EBADF;
+                die "poll: $!";
         }
 }