diff options
Diffstat (limited to 'lib/PublicInbox/DSKQXS.pm')
-rw-r--r-- | lib/PublicInbox/DSKQXS.pm | 76 |
1 files changed, 36 insertions, 40 deletions
diff --git a/lib/PublicInbox/DSKQXS.pm b/lib/PublicInbox/DSKQXS.pm index eccfa56d..dc6621e4 100644 --- a/lib/PublicInbox/DSKQXS.pm +++ b/lib/PublicInbox/DSKQXS.pm @@ -1,4 +1,4 @@ -# Copyright (C) 2019-2021 all contributors <meta@public-inbox.org> +# Copyright (C) all contributors <meta@public-inbox.org> # Licensed the same as Danga::Socket (and Perl5) # License: GPL-1.0+ or Artistic-1.0-Perl # <https://www.gnu.org/licenses/gpl-1.0.txt> @@ -11,15 +11,12 @@ # # It also implements signalfd(2) emulation via "tie". package PublicInbox::DSKQXS; -use strict; -use warnings; -use parent qw(Exporter); +use v5.12; use Symbol qw(gensym); use IO::KQueue; use Errno qw(EAGAIN); -use PublicInbox::Syscall qw(EPOLLONESHOT EPOLLIN EPOLLOUT EPOLLET - EPOLL_CTL_ADD EPOLL_CTL_MOD EPOLL_CTL_DEL); -our @EXPORT_OK = qw(epoll_ctl epoll_wait); +use PublicInbox::OnDestroy; +use PublicInbox::Syscall qw(EPOLLONESHOT EPOLLIN EPOLLOUT EPOLLET); sub EV_DISPATCH () { 0x0080 } @@ -41,23 +38,23 @@ sub kq_flag ($$) { sub new { my ($class) = @_; - bless { kq => IO::KQueue->new, owner_pid => $$ }, $class; + my $fgen = $PublicInbox::OnDestroy::fork_gen; + bless { kq => IO::KQueue->new, fgen => $fgen }, $class; } # returns a new instance which behaves like signalfd on Linux. # It's wasteful in that it uses another FD, but it simplifies # our epoll-oriented code. sub signalfd { - my ($class, $signo, $nonblock) = @_; + my ($class, $signo) = @_; my $sym = gensym; - tie *$sym, $class, $signo, $nonblock; # calls TIEHANDLE + tie *$sym, $class, $signo; # calls TIEHANDLE $sym } sub TIEHANDLE { # similar to signalfd() - my ($class, $signo, $nonblock) = @_; + my ($class, $signo) = @_; my $self = $class->new; - $self->{timeout} = $nonblock ? 0 : -1; my $kq = $self->{kq}; $kq->EV_SET($_, EVFILT_SIGNAL, EV_ADD) for @$signo; $self; @@ -66,12 +63,11 @@ sub TIEHANDLE { # similar to signalfd() sub READ { # called by sysread() for signalfd compatibility my ($self, undef, $len, $off) = @_; # $_[1] = buf die "bad args for signalfd read" if ($len % 128) // defined($off); - my $timeout = $self->{timeout}; my $sigbuf = $self->{sigbuf} //= []; my $nr = $len / 128; my $r = 0; $_[1] = ''; - do { + while (1) { while ($nr--) { my $signo = shift(@$sigbuf) or last; # caller only cares about signalfd_siginfo.ssi_signo: @@ -79,13 +75,13 @@ sub READ { # called by sysread() for signalfd compatibility $r += 128; } return $r if $r; - my @events = eval { $self->{kq}->kevent($timeout) }; + my @events = eval { $self->{kq}->kevent(0) }; # workaround https://rt.cpan.org/Ticket/Display.html?id=116615 if ($@) { next if $@ =~ /Interrupted system call/; die; } - if (!scalar(@events) && $timeout == 0) { + if (!scalar(@events)) { $! = EAGAIN; return; } @@ -94,36 +90,37 @@ sub READ { # called by sysread() for signalfd compatibility # field shows coalesced signals, and maybe we'll use it # in the future... @$sigbuf = map { $_->[0] } @events; - } while (1); + } } # for fileno() calls in PublicInbox::DS sub FILENO { ${$_[0]->{kq}} } -sub epoll_ctl { - my ($self, $op, $fd, $ev) = @_; - my $kq = $self->{kq}; - if ($op == EPOLL_CTL_MOD) { - $kq->EV_SET($fd, EVFILT_READ, kq_flag(EPOLLIN, $ev)); - eval { $kq->EV_SET($fd, EVFILT_WRITE, kq_flag(EPOLLOUT, $ev)) }; - } elsif ($op == EPOLL_CTL_DEL) { - $kq->EV_SET($fd, EVFILT_READ, EV_DISABLE); - eval { $kq->EV_SET($fd, EVFILT_WRITE, EV_DISABLE) }; - } else { # EPOLL_CTL_ADD - $kq->EV_SET($fd, EVFILT_READ, EV_ADD|kq_flag(EPOLLIN, $ev)); - - # we call this blindly for read-only FDs such as tied - # DSKQXS (signalfd emulation) and Listeners - eval { - $kq->EV_SET($fd, EVFILT_WRITE, EV_ADD | - kq_flag(EPOLLOUT, $ev)); - }; - } +sub _ep_mod_add ($$$$) { + my ($kq, $fd, $ev, $add) = @_; + $kq->EV_SET($fd, EVFILT_READ, $add|kq_flag(EPOLLIN, $ev)); + + # we call this blindly for read-only FDs such as tied + # DSKQXS (signalfd emulation) and Listeners + eval { $kq->EV_SET($fd, EVFILT_WRITE, $add|kq_flag(EPOLLOUT, $ev)) }; 0; } -sub epoll_wait { - my ($self, $maxevents, $timeout_msec, $events) = @_; +sub ep_add { _ep_mod_add($_[0]->{kq}, fileno($_[1]), $_[2], EV_ADD) }; +sub ep_mod { _ep_mod_add($_[0]->{kq}, fileno($_[1]), $_[2], 0) }; + +sub ep_del { + my ($self, $io, $ev) = @_; + my $kq = $_[0]->{kq} // return; # called in cleanup + my $fd = fileno($io); + $kq->EV_SET($fd, EVFILT_READ, EV_DISABLE); + eval { $kq->EV_SET($fd, EVFILT_WRITE, EV_DISABLE) }; + 0; +} + +sub ep_wait { + my ($self, $timeout_msec, $events) = @_; + # n.b.: IO::KQueue is hard-coded to return up to 1000 events @$events = eval { $self->{kq}->kevent($timeout_msec) }; if (my $err = $@) { # workaround https://rt.cpan.org/Ticket/Display.html?id=116615 @@ -142,9 +139,8 @@ sub epoll_wait { sub DESTROY { my ($self) = @_; my $kq = delete $self->{kq} or return; - if (delete($self->{owner_pid}) == $$) { + delete($self->{fgen}) == $PublicInbox::OnDestroy::fork_gen and POSIX::close($$kq); - } } 1; |