Skip Menu |

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

Report information
The Basics
Id: 65785
Status: resolved
Priority: 0/
Queue: IO-Async

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

Bug Information
Severity: (no value)
Broken in: (no value)
Fixed in: 0.40



Subject: <@mst> GOD DAMMIT DEATH BY WEASEL
_capture_weakself tries to be too efficient, capturing the CODE ref behind a method. This causes problems if you later replace the method code and expect it to work: 16:18 <@mst> reloaded code, forgot the _capture stuff does ->can 16:18 <@mst> so you have to reconfigure the things that use that code as well This should be fixed to simply use regular method dispatch on $weakself. -- Paul Evans
Attached patch should fix it. -- Paul Evans
Subject: rt65785.patch
=== modified file 'lib/IO/Async/Notifier.pm' --- lib/IO/Async/Notifier.pm 2011-02-10 23:30:56 +0000 +++ lib/IO/Async/Notifier.pm 2011-02-16 20:05:19 +0000 @@ -458,6 +458,10 @@ The C<$code> argument may also be a plain string, which will be used as a method name; the returned CODE ref will then invoke that method on the object. +In this case the method name is stored symbolically in the returned CODE +reference, and dynamically dispatched each time the reference is invoked. This +allows it to follow code reloading, dynamic replacement of class methods, or +other similar techniques. If the C<$mref> CODE reference is being stored in some object other than the one it refers to, remember that since the Notifier is only weakly captured, it @@ -480,21 +484,24 @@ if( !ref $code ) { my $class = ref $self; - my $coderef = $self->can( $code ) or + # Don't save this coderef, or it will break dynamic method dispatch, + # which means code reloading, dynamic replacement, or other funky + # techniques stop working + $self->can( $code ) or croak qq(Can't locate object method "$code" via package "$class"); - - $code = $coderef; } weaken $self; return sub { + my $cv = ref( $code ) ? $code : $self->can( $code ); + if( HAS_BROKEN_TRAMPOLINES ) { - return $code->( $self, @_ ); + return $cv->( $self, @_ ); } else { unshift @_, $self; - goto &$code; + goto &$cv; } }; } @@ -521,6 +528,7 @@ The C<$code> argument may also be a plain string, which will be used as a method name; the returned CODE ref will then invoke that method on the object. +As with C<_capture_weakself> this is stored symbolically. As with C<_capture_weakself>, care should be taken against Notifier destruction if the C<$mref> CODE reference is stored in some other object. @@ -533,24 +541,25 @@ my ( $code ) = @_; # actually bare method names work too if( !ref $code ) { + # Don't save this coderef, see _capture_weakself for why my $class = ref $self; - my $coderef = $self->can( $code ) or + $self->can( $code ) or croak qq(Can't locate object method "$code" via package "$class"); - - $code = $coderef; } weaken $self; return sub { + my $cv = ref( $code ) ? $code : $self->can( $code ); + if( HAS_BROKEN_TRAMPOLINES ) { - return $code->( $self, @_[1..$#_] ); + return $cv->( $self, @_[1..$#_] ); } else { # Don't assign to $_[0] directly or we will change caller's first argument shift @_; unshift @_, $self; - goto &$code; + goto &$cv; } }; } === modified file 't/04notifier.t' --- t/04notifier.t 2011-02-09 09:45:12 +0000 +++ t/04notifier.t 2011-02-16 20:05:19 +0000 @@ -2,7 +2,7 @@ use strict; -use Test::More tests => 36; +use Test::More tests => 38; use Test::Fatal; use Test::Refcount; @@ -105,6 +105,19 @@ $mref->( 456 ); is_deeply( \@subargs, [ $notifier, 456 ], '@subargs after invoking $mref on named method' ); +{ + undef @subargs; + my @newargs; + + no warnings 'redefine'; + local *TestNotifier::frobnicate = sub { @newargs = @_; }; + + $mref->( 321 ); + + is_deeply( \@subargs, [], '@subargs empty after TestNotifier::frobnicate replacement' ); + is_deeply( \@newargs, [ $notifier, 321 ], '@newargs after TestNotifier::frobnicate replacement' ); +} + undef @subargs; ok( exception { $notifier->_capture_weakself( 'cannotdo' ) },