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