Potential Race condition in AnyEvent::Handle when a connect error occurs
Ryan Bullock
rrb3942 at gmail.com
Wed Jan 26 17:26:37 CET 2011
I receive the following error with my perl module on perl 5.12.2 with
AnyEvent 5.31 and EV 4.03 when a connection error occurs and using the
on_connect_error callback:
EV: error in callback (ignoring): Can't call method "destroy" on an
undefined value at
/home/rbullock/perl5/perlbrew/perls/5.12.2-nothreads/lib/site_perl/5.12.2/x86_64-linux/AnyEvent/Handle.pm
line 541.
The pertinent block inside AnyEvent::Handle:
if ($self->{on_connect_error}) {
$self->{on_connect_error}($self, "$!");
$self->destroy;
} else {
$self->_error ($!, 1);
}
This seems to happen because I am storing the AnyEvent::Handle in my
blessed hash reference, but if a connect error occurs I am cleaning up
and releasing everything in my hash reference. This is causing the
AnyEvent::Handle object to get garbage collected (before destroy is
called) because the reference to $self in the connect block is
weakened.
This is not a problem if you are only using on_error because calling
$self->_error creates a non weakened copy of the reference that keeps
it from getting garbage collected.
The only way I can see to fix this in my code is to attempt to keep a
copy of the handle around which seems a bit hacky.
I think this could be fixed in AnyEvent by checking $self before
calling destroy (if its already gone it shouldn't matter) or by
handling the on_connect_error through a method call.
Here is a copy of a simply test case that seems to reliably recreate
the problem (I hope this formats ok):
#!/usr/bin/env perl
use strict;
use warnings;
use AnyEvent;
my $foo = Foo::Bar->new();
AnyEvent->loop;
package Foo::Bar;
use strict;
use warnings;
use Scalar::Util qw/weaken/;
use AnyEvent::Handle;
sub new {
my ($class) = @_;
my $self = bless {}, $class;
my $weak = $self;
weaken($weak);
#Connect to something that will not work to provoke a on_connect_error
$self->{handle} = AnyEvent::Handle->new( connect => [
'127.0.0.1', '9999' ],
on_connect_error => sub { $weak->on_connect_error(@_) }
);
return $self;
}
sub on_connect_error {
my ($self, $hdl, $message) = @_;
$self->destroy;
}
sub destroy {
my ($self) = @_;
$self->DESTROY;
bless $self, "Foo::Bar::destroyed";
return 1;
}
sub DESTROY {
my ($self) = @_;
#Release everything
%$self = ();
}
sub Foo::Bar::destroyed::AUTOLOAD {
return;
}
1;
More information about the anyevent
mailing list