From 70a26c3af4e4e8eb9bcf86c9724bc560c3b1a8f9 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Tue, 12 Sep 2023 06:13:04 +0000 Subject: 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. --- lib/PublicInbox/DS.pm | 6 +++--- lib/PublicInbox/Select.pm | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 lib/PublicInbox/Select.pm (limited to 'lib/PublicInbox') diff --git a/lib/PublicInbox/DS.pm b/lib/PublicInbox/DS.pm index b3edc094..d47df491 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 00000000..9df3a6bd --- /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; -- cgit v1.2.3-24-ge0c7