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;