Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why must an AnyEvent timer watcher be undef'ed in its callback to fire?

While trying to understand AnyEvent, I create two timers which print to screen each time they're fired. Initially neither worked. But following Joshua Barratt's timer example, I found that if I didn't undef the timer's watcher variable then the timer's callback didn't fire at all. Why is that the case? I suspect it has something to do with how scope works in perl and/or AnyEvent.

Here is my example program:

#!/usr/bin/perl

use AE;

my $cv = AE::cv;

sub func1 {
   my $spoke = 0;
   my $t1; $t1 = AE::timer 0, 1,
      sub { 
         print "Timer 1 Fired\n";
         if($spoke++ > 5) {
            print "Timer 1 Done\n";
            undef $t1;
         }   
      };  
      print "Timer 1 started\n";
}

sub func2 {
   my $spoke = 0;
   my $t2; $t2 = AE::timer 0, 1,
      sub { 
         print "Timer 2 Fired\n";
         if($spoke++ > 5) {
            print "Timer 2 Done\n";
            #undef $t2;
         }   
      };  
      print "Timer 2 started\n";
}

func1();
func2();

$cv->recv;

As is, my code returns:

Timer 1 started
Timer 2 started
Timer 1 Fired
Timer 1 Fired
Timer 1 Fired
Timer 1 Fired
Timer 1 Fired
Timer 1 Fired
Timer 1 Fired
Timer 1 Done

If I uncomment the undef $t2; line, Timer 2's callback is fired and I get this:

Timer 1 started
Timer 2 started
Timer 1 Fired
Timer 2 Fired
Timer 2 Fired
Timer 1 Fired
Timer 1 Fired
Timer 2 Fired
Timer 2 Fired
Timer 1 Fired
Timer 1 Fired
Timer 2 Fired
Timer 2 Fired
Timer 1 Fired
Timer 1 Fired
Timer 1 Done
Timer 2 Fired
Timer 2 Done
like image 497
Le Jeune Renard Avatar asked Oct 01 '22 06:10

Le Jeune Renard


1 Answers

You must keep the guard object (the value of $t1) alive. If all references to it are gone, it will get destroyed, and that cancels the event.

Referencing $t1 in the closure causes the closure to capture it, keeping it alive past it's normal death at then end of func.

If you want to capture a variable you don't otherwise need, you can use

$t2 if 0;   # Keep timer alive until process exit.

Here's a simple example of a closure:

sub make_closure {
   my ($x) = @_;
   return sub {
      print("$x\n");
   };
}

my $f1 = make_closure("Hello, World!");
my $f2 = make_closure("Allo, Jeune Renard!");

$f1->();
$f2->();

Notice how the closure (the anon sub) captures the $x that existed at the time?

like image 76
ikegami Avatar answered Oct 13 '22 11:10

ikegami