Deano Deano - 7 months ago 50
Perl Question

Perl EV module create periodic events

I'm using EV module to create a timed events that will run indefinitely in a defined time interval, here is my code with defined intervals.

#!/usr/bin/perl
use strict;
use warnings qw/ all FATAL /;
use feature 'say';
use EV;
use Time::Piece 'localtime';

my $one_sec = EV::timer 1,1, sub {
say "(1s) Periodic event at ", localtime(EV::now)->hms;
};

my $five_sec = EV::timer 5, 5, sub {
say "(5s) Periodic event at ", localtime(EV::now)->hms;
};

EV::run;


I'm trying to define these functions dynamically from hash reference, by looping through it to create these functions.

my $hash = {
'one_sec' => '1',
'five_sec' => '5',
};


I have used foreach to loop through the hash.

foreach my $key ( keys %$hash )
{

my $key = EV::timer $hash->{$key},$hash->{$key}, sub {
say "($hash->{$key}) Periodic event at ", localtime(EV::now)->hms;
};

EV::run;
}


It works, but only for the first iteration, I'm not sure why is that?

Your thoughts are highly appreciated.

Answer

The following code is not tested.


There are two things wrong with your code:

First, you are starting the event loop in the body of your foreach loop, which will make it run within the first iteration of the foreach. It will then get stuck there, and only one of the timers will be created. To fix that, the EV::run needs to be moved to outside of the foreach.

However, it will then not do anything at all. That's because the event-loop has nothing to do. That is because you are defining your EV::timer objects lexically within the body of the foreach. Once one iteration is done, that variable ($key) will go out of scope, destroying the timer object. When it's done creating both, it will just run an empty event-loop. In addition to that, you redefined $key, which was already the "one_sec" or "five_sec" string that comes out of the hash.

Instead, you need to put the event objects somewhere that's outside of the loop, too (because each block creates a lexical scope in Perl when you use my, which is a feature).

You should also use more speaking variable names so you don't get confused.

#!/usr/bin/perl
use strict;
use warnings qw/ all FATAL /;
use feature 'say';
use EV;
use Time::Piece 'localtime';

my $config = {         # was $hash
    'one_sec'  => '1',
    'five_sec' => '5',
}

my $timers;
foreach my $key ( keys %$config ) {
    $timers->{$key} = EV::timer $config->{$key},$config->{$key}, sub {
        say "($config->{$key})  Periodic event at ", localtime(EV::now)->hms;
    };
}

EV::run;