user/dev discussion of public-inbox itself
 help / color / mirror / code / Atom feed
From: Eric Wong <e@80x24.org>
To: meta@public-inbox.org
Subject: [PATCH] poll+select: check EBADF + POLLNVAL errors
Date: Mon, 30 Oct 2023 18:29:40 +0000	[thread overview]
Message-ID: <20231030182940.5628-1-e@80x24.org> (raw)

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;

                 reply	other threads:[~2023-10-30 18:31 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: http://public-inbox.org/README

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20231030182940.5628-1-e@80x24.org \
    --to=e@80x24.org \
    --cc=meta@public-inbox.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/public-inbox.git

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).