Skip Menu |

This queue is for tickets about the IO-Epoll CPAN distribution.

Report information
The Basics
Id: 43192
Status: open
Priority: 0/
Queue: IO-Epoll

People
Owner: Nobody in particular
Requestors: leonerd-cpan [...] leonerd.org.uk
Cc:
AdminCc:

Bug Information
Severity: Important
Broken in: 0.02
Fixed in: (no value)



Subject: Cannot remove event bits from an IO handle's mask by calling $epoll->mask()
Using $epoll->mask() does not correctly work to unset some bits of a mask, (e.g. POLLOUT), while other bits need to remain (e.g. POLLIN). I eventually tracked down this code by adding some printf STDERR's: 140 my $mask = shift; 141 142 printf STDERR "%s->mask( %s=%d, %04x )\n", $self, $io, $fd, $mask; 143 144 if ($mask) { 145 my $combined_mask = $mask; 146 my $op = &EPOLL_CTL_ADD; 147 if ( exists $self->[0]{$fd} ) { 148 $combined_mask |= $_ foreach values %{ $self->[0]{$fd} }; 149 $op = &EPOLL_CTL_MOD; 150 } 151 printf STDERR " epoll_ctl(%d, %d, %d, %04x)\n", $self->[3], $op, $fd, $combined_mask; 152 return if epoll_ctl($self->[3], $op, $fd, $combined_mask) < 0; 153 $self->[0]{$fd}{$io} = $mask; 154 $self->[2]{$io} = $io; 155 } What I get here is that if I want to remove POLLOUT while keeping POLLIN, by doing something of the following $epoll->mask( $io, $epoll->mask( $io ) & ~POLLOUT ); then the POLLOUT bit doesn't get cleared: IO::Epoll=ARRAY(0x8a191a0)->mask( IO::Socket::UNIX=GLOB(0x8e56a88)=5, 0001 ) epoll_ctl(3, 3, 5, 0005) This results in a 100% CPU spin because kernel keeps telling me that this socket is now writable, even though I don't have any more data to write. The application remains responsive because other IO events keep happening, it's just that the process chews all the CPU on the box. Looking over the code, I can see it's being combined with the value that still exists in $self->[0]{$fd}. I've made a modification [see attached patch] I believe should fix this fact. It ignores the current $io entry when calculating the $combined_mask, so that bits in it may be removed. At least, it works for me, though I admit I don't fully understand the $combined_mask calculations. Please check this looks correct... -- Paul Evans
Subject: io-epoll-fix-masks.diff
--- /usr/lib/perl5/IO/Epoll.pm 2009-02-11 00:31:40.057133434 +0000 +++ IO/Epoll.pm 2009-02-11 00:29:59.000000000 +0000 @@ -143,8 +143,11 @@ my $combined_mask = $mask; my $op = &EPOLL_CTL_ADD; if ( exists $self->[0]{$fd} ) { - $combined_mask |= $_ foreach values %{ $self->[0]{$fd} }; - $op = &EPOLL_CTL_MOD; + foreach ( keys %{ $self->[0]{$fd} } ) { + next if $_ eq $io; + $combined_mask |= $self->[0]{$fd}{$_}; + } + $op = &EPOLL_CTL_MOD; } return if epoll_ctl($self->[3], $op, $fd, $combined_mask) < 0; $self->[0]{$fd}{$io} = $mask;
Find attached a patch that contains the fix, and a new test script. Had to be a little evil in the way it checks the values passed to the 'epoll_ctl()' low-level syscall wrapper, because simply calling $epoll->mask() to obtain the current mask doesn't see what values were passed to the kernel. -- Paul Evans
=== modified file 'lib/IO/Epoll.pm' --- lib/IO/Epoll.pm 2009-02-11 23:37:08 +0000 +++ lib/IO/Epoll.pm 2009-02-12 00:48:48 +0000 @@ -143,7 +143,10 @@ my $combined_mask = $mask; my $op = &EPOLL_CTL_ADD; if ( exists $self->[0]{$fd} ) { - $combined_mask |= $_ foreach values %{ $self->[0]{$fd} }; + foreach ( keys %{ $self->[0]{$fd} } ) { + next if $_ eq $io; + $combined_mask |= $self->[0]{$fd}{$_}; + } $op = &EPOLL_CTL_MOD; } return if epoll_ctl($self->[3], $op, $fd, $combined_mask) < 0; === added file 't/rt43192.t' --- t/rt43192.t 1970-01-01 00:00:00 +0000 +++ t/rt43192.t 2009-02-12 00:48:48 +0000 @@ -0,0 +1,44 @@ +#!/usr/bin/perl -w + +use strict; + +use Test::More tests => 11; + +use IO::Epoll qw( + POLLIN POLLOUT + EPOLL_CTL_ADD EPOLL_CTL_MOD EPOLL_CTL_DEL +); + +my $epoll = IO::Epoll->new(); + +# We're not actually going to do any polling so the handle doesn't matter +my $h = \*STDIN; + +my ( $op, $fd, $mask ); + +{ + no strict 'refs'; + no warnings 'redefine'; + *IO::Epoll::epoll_ctl = sub { + ( undef, $op, $fd, $mask ) = @_; + }; +} + +$epoll->mask( $h, POLLIN ); +is( $op, EPOLL_CTL_ADD, '$op setting POLLIN' ); +is( $fd, 0, '$fd setting POLLIN' ); +is( $mask, POLLIN, '$mask setting POLLIN' ); + +$epoll->mask( $h, POLLIN|POLLOUT ); +is( $op, EPOLL_CTL_MOD, '$op setting POLLOUT' ); +is( $fd, 0, '$fd setting POLLOUT' ); +is( $mask, POLLIN|POLLOUT, '$mask setting POLLOUT' ); + +$epoll->mask( $h, POLLIN ); +is( $op, EPOLL_CTL_MOD, '$op clearing POLLOUT' ); +is( $fd, 0, '$fd clearing POLLOUT' ); +is( $mask, POLLIN, '$mask clearing POLLOUT' ); + +$epoll->mask( $h, 0 ); +is( $op, EPOLL_CTL_DEL, '$op clearing POLLIN' ); +is( $fd, 0, '$fd clearing POLLIN' );