* [PATCH] poll+select: check EBADF + POLLNVAL errors
@ 2023-10-30 18:29 Eric Wong
0 siblings, 0 replies; only message in thread
From: Eric Wong @ 2023-10-30 18:29 UTC (permalink / raw)
To: meta
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.
---
lib/PublicInbox/DSPoll.pm | 31 ++++++++++++++++++++-----------
lib/PublicInbox/Select.pm | 3 ++-
t/ds-poll.t | 21 +++++++++++++++++++--
3 files changed, 41 insertions(+), 14 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: $!";
}
}
diff --git a/lib/PublicInbox/Select.pm b/lib/PublicInbox/Select.pm
index 9df3a6bd..5817c9ef 100644
--- a/lib/PublicInbox/Select.pm
+++ b/lib/PublicInbox/Select.pm
@@ -20,7 +20,8 @@ sub ep_wait {
}
@$events = ();
my $n = select($rvec, $wvec, undef, $msec < 0 ? undef : ($msec/1000));
- return if $n <= 0;
+ return if $n == 0;
+ die "select: $!" if $n < 0;
while (my ($fd, $ev) = each %$self) {
if (vec($rvec, $fd, 1) || vec($wvec, $fd, 1)) {
delete($self->{$fd}) if $ev & EPOLLONESHOT;
diff --git a/t/ds-poll.t b/t/ds-poll.t
index 57fac3ef..7a7e2bcf 100644
--- a/t/ds-poll.t
+++ b/t/ds-poll.t
@@ -6,13 +6,14 @@
use v5.12;
use Test::More;
use PublicInbox::Syscall qw(EPOLLIN EPOLLOUT EPOLLONESHOT);
+use autodie qw(close pipe syswrite);
my $cls = $ENV{TEST_IOPOLLER} // 'PublicInbox::DSPoll';
use_ok $cls;
my $p = $cls->new;
my ($r, $w, $x, $y);
-pipe($r, $w) or die;
-pipe($x, $y) or die;
+pipe($r, $w);
+pipe($x, $y);
is($p->ep_add($r, EPOLLIN), 0, 'add EPOLLIN');
my $events = [];
$p->ep_wait(9, 0, $events);
@@ -44,4 +45,20 @@ is($p->ep_del($r, 0), 0, 'EPOLL_CTL_DEL OK');
$p->ep_wait(9, 0, $events);
is(scalar @$events, 0, 'nothing ready after EPOLL_CTL_DEL');
+is($p->ep_add($r, EPOLLIN), 0, 're-add');
+SKIP: {
+ $cls =~ m!::(?:DSPoll|Select)\z! or
+ skip 'EBADF test for select|poll only';
+ my $old_fd = fileno($r);
+ close $r;
+ my @w;
+ eval {
+ local $SIG{__WARN__} = sub { push @w, @_ };
+ $p->ep_wait(9, 0, $events);
+ };
+ ok($@, 'error detected from bad FD');
+ ok($!{EBADF}, 'EBADF errno set');
+ @w and ok(grep(/\bFD=$old_fd invalid/, @w), 'carps invalid FD');
+}
+
done_testing;
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2023-10-30 18:31 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-10-30 18:29 [PATCH] poll+select: check EBADF + POLLNVAL errors Eric Wong
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).