From: Eric Wong Date: Tue, 12 Sep 2023 06:13:04 +0000 (+0000) Subject: provide select(2) backend for PublicInbox::DS X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=70a26c3af4e4e8eb9bcf86c9724bc560c3b1a8f9;p=thirdparty%2Fpublic-inbox.git provide select(2) backend for PublicInbox::DS This is safer than relying on an internal API of IO::Poll and doesn't create extra references to IO globs like the public one. --- diff --git a/MANIFEST b/MANIFEST index d7a670b84..63287bad4 100644 --- a/MANIFEST +++ b/MANIFEST @@ -331,6 +331,7 @@ lib/PublicInbox/SearchIdxShard.pm lib/PublicInbox/SearchQuery.pm lib/PublicInbox/SearchThread.pm lib/PublicInbox/SearchView.pm +lib/PublicInbox/Select.pm lib/PublicInbox/SharedKV.pm lib/PublicInbox/Sigfd.pm lib/PublicInbox/Smsg.pm @@ -578,6 +579,7 @@ t/run.perl t/search-amsg.eml t/search-thr-index.t t/search.t +t/select.t t/sha.t t/shared_kv.t t/sigfd.t diff --git a/lib/PublicInbox/DS.pm b/lib/PublicInbox/DS.pm index b3edc094f..d47df491e 100644 --- a/lib/PublicInbox/DS.pm +++ b/lib/PublicInbox/DS.pm @@ -31,7 +31,7 @@ use Scalar::Util qw(blessed); use PublicInbox::Syscall qw(%SIGNUM EPOLLIN EPOLLOUT EPOLLONESHOT EPOLLEXCLUSIVE); use PublicInbox::Tmpfile; -use PublicInbox::DSPoll; +use PublicInbox::Select; use Errno qw(EAGAIN EINVAL ECHILD EINTR); use Carp qw(carp croak); our @EXPORT_OK = qw(now msg_more awaitpid add_timer add_uniq_timer); @@ -43,7 +43,7 @@ my $reap_armed; my $ToClose; # sockets to close when event loop is done our ( %DescriptorMap, # fd (num) -> PublicInbox::DS object - $Poller, # global Epoll, DSPoll, or DSKQXS ref + $Poller, # global Select, Epoll, DSPoll, or DSKQXS ref @post_loop_do, # subref + args to call at the end of each loop @@ -83,7 +83,7 @@ sub Reset { $reap_armed = undef; $LoopTimeout = -1; # no timeout by default - $Poller = PublicInbox::DSPoll->new; + $Poller = PublicInbox::Select->new; } =head2 C<< CLASS->SetLoopTimeout( $timeout ) >> diff --git a/lib/PublicInbox/Select.pm b/lib/PublicInbox/Select.pm new file mode 100644 index 000000000..9df3a6bdb --- /dev/null +++ b/lib/PublicInbox/Select.pm @@ -0,0 +1,40 @@ +# Copyright (C) all contributors +# License: AGPL-3.0+ +# +# This makes select(2) look like epoll to simplify the code in DS.pm. +# Unlike IO::Select, it does NOT hold references to IO handles. +# This is NOT meant to be an all encompassing emulation of epoll +# via select, but only to support cases we care about. +package PublicInbox::Select; +use v5.12; +use PublicInbox::Syscall qw(EPOLLONESHOT EPOLLIN EPOLLOUT); + +sub new { bless {}, __PACKAGE__ } # fd => events + +sub ep_wait { + my ($self, $maxevents, $msec, $events) = @_; + my ($rvec, $wvec) = ('', ''); # we don't use EPOLLERR + while (my ($fd, $ev) = each %$self) { + vec($rvec, $fd, 1) = 1 if $ev & EPOLLIN; + vec($wvec, $fd, 1) = 1 if $ev & EPOLLOUT; + } + @$events = (); + my $n = select($rvec, $wvec, undef, $msec < 0 ? undef : ($msec/1000)); + return if $n <= 0; + while (my ($fd, $ev) = each %$self) { + if (vec($rvec, $fd, 1) || vec($wvec, $fd, 1)) { + delete($self->{$fd}) if $ev & EPOLLONESHOT; + push @$events, $fd; + } + } + $n == scalar(@$events) or + warn "BUG? select() returned $n, but got ".scalar(@$events); +} + +sub ep_del { delete($_[0]->{fileno($_[1])}); 0 } +sub ep_add { $_[0]->{fileno($_[1])} = $_[2]; 0 } + +no warnings 'once'; +*ep_mod = \&ep_add; + +1; diff --git a/t/select.t b/t/select.t new file mode 100644 index 000000000..e8032c5aa --- /dev/null +++ b/t/select.t @@ -0,0 +1,4 @@ +# Copyright (C) all contributors +use v5.12; +local $ENV{TEST_IOPOLLER} = 'PublicInbox::Select'; +require './t/ds-poll.t';