use strict; use warnings; use AnyEvent; use AnyEvent::Ping; use Data::Dumper; # separate the hosts wrt their timer groups my @todoList1 = map { "192.168.100." . $_ } (1 .. 2); my @todoList2 = map { "192.168.100." . $_ } (3 .. 4); # a global hash keyed on IPs, => [0,[]]: no pinging now, =>[1,[]]: ping in progress # the array ref storing ping history can be a Queue of finite size my $pingState = { map { $_ => [0,[]] } @todoList1, @todoList2 }; my $cv = AnyEvent->condvar; my @timers = ( AnyEvent->timer ( after => 2, interval => 5, cb => sub { warn "Timer 1 at " . AnyEvent->now . "\n"; ping_hosts (1, @todoList1); } ), AnyEvent->timer ( after => 2, interval => 7, cb => sub { warn "Timer 2 at " . AnyEvent->now . "\n"; ping_hosts (2, @todoList2); } ), AnyEvent->timer ( after => 20, cb => sub { warn "Timer 3 at " . AnyEvent->now . "\n"; $cv->send; } ), ); $cv->recv; print "DONE.\n".Dumper($pingState); sub ping_hosts { my ($num, @hosts) = @_; my $ping_timeout = 2; #seconds to wait for response my $ping_attempts = 4; #number of pings to issue, my $ping_pktsize = 32; #packet size of ping my $ping = AnyEvent::Ping->new (packet_size => $ping_pktsize); $ping->timeout ($ping_timeout); #$cv_ping->begin(sub { shift->send($_[0]) }); foreach my $host (@hosts) { if( $pingState->{$host}->[0] == 1 ){ print "ping in progress already to host '$host', skipping till the next timer ...\n"; next } $pingState->{$host}->[0] = 1; # set the "ping in progress" flag $ping->ping ( #mock_ping( # emulate the ping $host, $ping_attempts, sub { print "Pinger $num: $host state:" . Dumper ($_[0]) . "\n"; # store the ping results in global history and remove the "ping-in-progress" flag push @{$pingState->{$host}->[1]}, $_[0]; $pingState->{$host}->[0] = 0; # ping finished }, # mock_ping extra parameters # $ping_timeout, # 50/100 # probability of a failed ping ); } } # code below is # optional for testing without ping'ing sub mock_ping { my ($host, $attempts, $cb, $timeout, $failure_prob) = @_; # no $attempts, just fail or succeed so many % of times my $started = AnyEvent->time; if( rand()<$failure_prob ){ # simulate a failed ping with lots of delay # so as to coincide with next timer event AEsleep($timeout*3); $cb->([$host, "$started -> fail -> ".AnyEvent->time]); return 0; } else { # a successful ping safely within the timeout AEsleep($timeout/2); # simulate a ping $cb->([$host, "$started -> succ -> ".AnyEvent->time]); return 1; # success ping } } # this is the closest I came to emulating spawning a blocking # ping. Note that using Perl's sleep() is no-go with AnyEvent's timers # AFAIU, re: "It is usually a mistake to intermix alarm and sleep calls" # and sadly there is no AE::sleep() AFAIK sub AEsleep($) { `sleep $_[0]` }