I do believe this is indeed a bug, but not in POE::Session. It seems the GC semantics have changed somewhat and call() specifically invokes a GC sweep in the cases where a return value is expected from an event.
I think this behavior is wrong (and I may have been the ultimate reason this behavior exists at all [GC after call()], some long long time ago sadly). For intra-session call()s, GC should not be called. Effectively, the $kr_active_session is $session, meaning that we are inside an event in $session that is current executing. GC should be reserved for when that source event finishes, not in the middle of it. So what I think is happening is this: his call invokes a GC sweep a couple of times until it depletes the event_count at which point the GC sweep will collect the session. But since kr_active_session is still around, delay() has no problem enqueuing an event against it. Then when the zombie session's state is invoked, Kernel happily dispatches even when it can no longer resolve the session (for source_session), since the reference is still valid. ASSERT_DEFAULT catches this, by the way. Recommendation: remove the _data_ses_gc_sweep() calls in call(). For what it is worth the tests seem to run fine on my machine with these calls removed from call(). On Fri, 8 Apr 2011 11:57:18 +0800 flw <su2ad...@gmail.com> wrote: > Sorry for my poor english, let me show it just by code. > > flw@waker:~/study$ perl -MPOE -le 'print "$POE::VERSION > $POE::Kernel::VERSION $POE::Session::VERSION"' > 1.299 1.299 1.299 > flw@waker:~/study$ ./bug.pl > start > call bar .... > inner bar > call bar .... > inner bar > found bug. > call bar .... > call bar .... > call bar .... > flw@waker:~/study$ cat bug.pl > #!/usr/bin/perl > use strict; > use warnings; > use POE; > > create POE::Session( > package_states => [ > main => [qw( _start foo bar die_on_bug )], > ], > ); > > run POE::Kernel(); > > sub _start{ > print "start\n"; > > $_[KERNEL]->yield( 'foo' ); > } > > exit 0; > > my $found_bug; > > sub foo { > my $sess = $_[KERNEL]->_resolve_session($_[SESSION]) || ''; > if ( $_[SESSION] ne $sess and not $found_bug ){ > $found_bug = 1; > print "found bug.\n"; > $_[KERNEL]->delay( die_on_bug => 3 ); > } > > print "call bar ....\n"; > my $ret = $_[KERNEL]->call( $_[SESSION] => bar => 1 ); # bad > #$_[KERNEL]->call( $_[SESSION] => bar => 1 ) or 1; # bad > too #my @ret = $_[KERNEL]->call( $_[SESSION] => bar => 1 ); # bad > too # $_[KERNEL]->call( $_[SESSION] => bar => 1 ); # good > > $_[KERNEL]->delay( foo => 1 ); > } > > sub bar { > print "inner bar\n"; > } > > sub die_on_bug { > exit 0; > } > flw@waker:~/study$ -- Nicholas Perez XMPP/Email: n...@nickandperla.net http://search.cpan.org/~nperez/ http://github.com/nperez