Subject: | Wrap epoll_pwait() and provide IO::Ppoll-like wrapping |
This patch adds a new syscall function, epoll_pwait(), and extends the
high-level API object methods to cover those methods added by IO::Ppoll,
which covers the signal mask in the same way.
This allows an IO::Epoll object to perform signal-safe polling in a
manner identical to IO::Ppoll.
--
Paul Evans
Subject: | IO-Epoll_pwait_IO-Ppoll.diff |
=== modified file 'Epoll.xs'
--- Epoll.xs 2008-11-21 19:49:40 +0000
+++ Epoll.xs 2008-11-23 17:42:03 +0000
@@ -62,4 +62,50 @@
free(events);
}
OUTPUT:
- RETVAL
\ No newline at end of file
+ RETVAL
+
+SV *
+epoll_pwait(epfd,maxevents,timeout,sigmask)
+ int epfd
+ int maxevents
+ int timeout
+ SV *sigmask
+CODE:
+ struct epoll_event *events;
+ sigset_t *sigmask_real;
+ int ret, i;
+
+ if(SvOK(sigmask)) {
+ if(!sv_derived_from(sigmask, "POSIX::SigSet"))
+ Perl_croak(aTHX_ "epoll_pwait: sigmask is not of type POSIX::SigSet");
+
+ /* This code borrowed from POSIX.xs */
+ IV tmp = SvIV((SV*)SvRV(sigmask));
+ sigmask_real = INT2PTR(sigset_t*, tmp);
+ }
+ else {
+ sigmask_real = NULL;
+ }
+
+ events = (struct epoll_event *)malloc(maxevents * sizeof(struct epoll_event));
+ if (!events) {
+ errno = ENOMEM;
+ XSRETURN_UNDEF;
+ }
+ ret = epoll_pwait(epfd, events, maxevents, timeout, sigmask_real);
+ if (ret < 0) {
+ free(events);
+ XSRETURN_UNDEF;
+ } else {
+ AV *results = (AV*)sv_2mortal((SV*)newAV());
+ for (i = 0; i < ret; i++) {
+ AV *ev = (AV*)sv_2mortal((SV*)newAV());
+ av_push(ev, newSVnv(events[i].data.fd));
+ av_push(ev, newSVnv(events[i].events));
+ av_push(results, newRV((SV*) ev));
+ }
+ RETVAL = newRV((SV *)results);
+ free(events);
+ }
+OUTPUT:
+ RETVAL
=== modified file 'lib/IO/Epoll.pm'
--- lib/IO/Epoll.pm 2008-11-23 17:35:55 +0000
+++ lib/IO/Epoll.pm 2008-11-23 17:42:03 +0000
@@ -36,6 +36,7 @@
epoll_create
epoll_ctl
epoll_wait
+ epoll_pwait
) ],
'compat' => [ qw(
POLLIN
@@ -71,6 +72,7 @@
epoll_create
epoll_ctl
epoll_wait
+ epoll_pwait
);
our $VERSION = '0.01';
@@ -109,11 +111,12 @@
# [1] maps fd's to returned masks
# [2] maps fd's to handles
# [3] is the epoll fd
+# [4] is the signal mask, if used. If present will use epoll_pwait() instead of epoll_wait()
sub new
{
my $package = shift;
- my $self = bless [ {}, {}, {}, undef ] => $package;
+ my $self = bless [ {}, {}, {}, undef, undef ] => $package;
$self->[3] = epoll_create(15);
if ($self->[3] < 0) {
@@ -181,7 +184,7 @@
my $msec = defined $timeout ? $timeout * 1000 : -1;
- my $ret = epoll_wait($self->[3], $maxevents, $msec);
+ my $ret = epoll_pwait($self->[3], $maxevents, $msec, $self->[4]);
return -1 unless defined $ret;
foreach my $event (@$ret) {
@@ -237,6 +240,47 @@
POSIX::close($self->[3]);
}
+# IO::Ppoll API extension
+
+sub sigmask
+{
+ my $self = shift;
+
+ if( my ( $newmask ) = @_ ) {
+ $self->[4] = $newmask;
+ }
+ else {
+ $self->[4] ||= POSIX::SigSet->new();
+ return $self->[4];
+ }
+}
+
+sub sigmask_add
+{
+ my $self = shift;
+ my @signals = @_;
+
+ my $sigmask = $self->sigmask;
+ $sigmask->addset( $_ ) foreach @signals;
+}
+
+sub sigmask_del
+{
+ my $self = shift;
+ my @signals = @_;
+
+ my $sigmask = $self->sigmask;
+ $sigmask->delset( $_ ) foreach @signals;
+}
+
+sub sigmask_ismember
+{
+ my $self = shift;
+ my ( $signal ) = @_;
+
+ return $self->sigmask->ismember( $signal );
+}
+
# IO::Poll compatibility constants
sub POLLNVAL () { 0 };
@@ -291,10 +335,11 @@
watched file descriptors. You will need at least version 2.5.44 of Linux
to use this module, and you might need to upgrade your C library.
-The C<epoll(2)> API comprises three system calls: C<epoll_create(2)>,
-C<epoll_ctl(2)> and C<epoll_wait(2)>. C<IO::Epoll> provides a low-level
-API which closely matches the underlying system calls. It also provides
-a higher-level layer designed to emulate the behavior of C<IO::Poll>.
+The C<epoll(2)> API comprises four system calls: C<epoll_create(2)>,
+C<epoll_ctl(2)>, C<epoll_wait(2)> and C<epoll_pwait(2)>. C<IO::Epoll>
+provides a low-level API which closely matches the underlying system calls.
+It also provides a higher-level layer designed to emulate the behavior of
+C<IO::Poll> and C<IO::Ppoll>.
=head1 LOW-LEVEL API
@@ -365,6 +410,17 @@
On error, C<epoll_wait> returns undef and sets C<errno> appropriately.
+=head2 epoll_pwait
+
+Wait for events on the C<epoll> file descriptor C<$epfd>.
+
+ $ret = epoll_pwait($epfd, $maxevents, $timeout, $sigmask)
+
+Identical to C<epoll_wait>, except that the kernel will atomically swap the
+current signal mask for the process to that supplied in C<$sigmask>, wait for
+events, then restore it to what it was originally. The C<$sigmask> parameter
+should be undef, or an instance of C<POSIX::SigSet>.
+
=back
=head1 HIGH LEVEL API
@@ -411,6 +467,39 @@
=back
+=head1 IO::Ppoll METHODS
+
+IO::Epoll also provides methods compatible with IO::Ppoll. When any of these
+methods are called, the IO::Epoll object switches up to IO::Ppoll-compatible
+mode, and will use the C<epoll_pwait(2)> system call when the C<poll> method
+is invoked.
+
+=over 4
+
+=item sigmask
+
+Returns the C<POSIX::SigSet> object in which the signal mask is stored. Since
+this is a reference to the object used in the call to C<epoll_pwait(2)>, any
+modifications made to it will be reflected in the signal mask given to the
+system call.
+
+=item sigmask_add ( SIGNALS )
+
+Adds the given signals to the signal mask. These signals will be blocked
+during the C<poll> call.
+
+=item sigmask_del ( SIGNALS )
+
+Removes the given signals from the signal mask. These signals will not be
+blocked during the C<poll> call, and may be delivered while C<poll> is
+waiting.
+
+=item sigmask_ismember ( SIGNAL )
+
+Tests if the given signal is present in the signal mask.
+
+=back
+
=head1 Exportable constants
@@ -446,8 +535,8 @@
=head1 SEE ALSO
-C<IO::Poll> C<IO::Select> C<epoll(4)> C<epoll_create(2)> C<epoll_ctl(2)>
-C<epoll_wait(2)>
+C<IO::Poll> C<IO::Select> C<IO::Ppoll> C<epoll(4)> C<epoll_create(2)>
+C<epoll_ctl(2)> C<epoll_wait(2)> C<epoll_pwait(2)>
=head1 AUTHOR
=== added file 't/IO-Ppoll-compat.t'
--- t/IO-Ppoll-compat.t 1970-01-01 00:00:00 +0000
+++ t/IO-Ppoll-compat.t 2008-11-23 17:42:03 +0000
@@ -0,0 +1,59 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+use Test::More tests => 10;
+
+use IO::Epoll;
+
+use POSIX qw(
+ sigprocmask SIG_BLOCK
+ SIGHUP SIGTERM SIGUSR1 SIGUSR2
+ EINTR
+);
+
+my $epoll = IO::Epoll->new();
+
+ok( !$epoll->sigmask_ismember( SIGHUP ), 'SIGHUP not in initial set' );
+
+$epoll->sigmask_add( SIGHUP );
+
+ok( $epoll->sigmask_ismember( SIGHUP ), 'SIGHUP now in set' );
+
+$epoll->sigmask_del( SIGHUP );
+
+ok( !$epoll->sigmask_ismember( SIGHUP ), 'SIGHUP no longer in set' );
+
+my $SIGHUP_count = 0;
+$SIG{HUP} = sub { $SIGHUP_count++ };
+
+kill SIGHUP, $$;
+
+is( $SIGHUP_count, 1, 'Caught SIGHUP before sigprocmask' );
+
+sigprocmask( SIG_BLOCK, POSIX::SigSet->new( SIGHUP ) );
+
+kill SIGHUP, $$;
+
+is( $SIGHUP_count, 1, 'Not caught SIGHUP after sigprocmask' );
+
+my $ret = $epoll->poll( 0.1 );
+my $dollarbang = $!+0;
+
+is( $ret, -1, 'poll() returns undef' );
+is( $dollarbang, EINTR, 'poll() failed with EINTR' );
+
+is( $SIGHUP_count, 2, 'Caught SIGHUP after poll' );
+
+sigprocmask( SIG_BLOCK, POSIX::SigSet->new( SIGTERM ) );
+$epoll->sigmask_add( SIGTERM );
+
+my $SIGTERM_count = 0;
+$SIG{TERM} = sub { $SIGTERM_count++ };
+
+kill SIGTERM, $$;
+
+$ret = $epoll->poll( 0.1 );
+
+is( $ret, 0, 'poll() returns 0' );
+is( $SIGTERM_count, 0, 'Not caught SIGTERM after poll()' );