Search code examples
perlsleepalarmdie

Perl: Call a Subroutine Every 15 Seconds


So I've been doing some looking around on here as well as on perl.org and perlmaven.com, but I'm confused with what's happening with the code that I'm trying to run. I've googled what $SIG is and found something on here that gives me a bit of an idea of what local $SIG is but I'm still confused by what he meant by this:

Perl has a built-in hash called %SIG, in which the keys are the names of the signals available in you operating system. The values are subroutines (more specifically references to subroutines), that will be called when the specific signal arrives.

In addition to the standard signals of your operating system Perl added two internal "signals". One of them is called WARN and is triggered every time some code calls the warn() function. The other one is called DIE and it is triggered when die() is called.

It also says on here that I shouldn't mix sleep calls within an alarm call, but what's explained on there seems like another language to me. I'm confused. I'm having trouble getting out of the subroutine without it dying on me. The code that I'm running is this (I obtained it from another post on here but I don't want to revive that post as it is from 2011):

sub checkfind {
didfindchange;
    while ($call_counter <= 20) {
        my $previous_call_finished = 0;
        eval {
            local $SIG{ALRM} = sub { die "Alarm.\n" };
            alarm 15;
            if ($did_find_change eq "false") {
                didfindchange;
                print("Original FIND value: $original_find_value\n");
                print("FIND value: $ems_find_value\n");
            }
            elsif ($did_find_change eq "true") {
                print("FIND changed.\n");
                print("Original FIND value: $original_find_value\n");
                print("FIND value: $ems_find_value\n");
                $previous_call_finished = 1;
            }
            sleep;
        };
        #Propagate unexpected errors.
        die unless $@ eq "Alarm.\n";
        warn "Timed out!\n" unless $previous_call_finished
    }
    return;
}

My program just keeps dying and I'm unsure of the reason why. Any help would be appreciated. Thank you!


Solution

  • Generally, one would use an alarm for subroutines / external processes which can take a long time (waiting for db connection, network resource, really computationally expensive calculation, etc.). You have to reset the alarm every time after the task finishes alarm(0); For what I understand that you are trying to do, a simple sleep / timeout loop like the following should work for you. For something more sophisticated (waiting for file events, network events), you may wish to consider programming with the EV event loops perldoc EV or the AnyEvent library perldoc AnyEvent

    #!/usr/bin/env perl
    
    use warnings;
    use strict;
    
    use constant TIMEOUT => 10;
    use constant SLEEP   => 2;
    
    check_for_something();
    exit 0; # Success
    
    sub check_for_something {
        my $start_time = time();
        my $elapsed = 0;
        while ( $elapsed < TIMEOUT ) {
            print "$elapsed seconds have elapsed...";
            my $value = look_for_something();
            print "Value is " . ( $value ? 'true' : 'false' ) . "\n";
            if ($value) {
                print "Value is true...returning\n";
                return "value has changed...";
            }
    
            sleep SLEEP;
            $elapsed = time() - $start_time;
        }
        die "Timed out after " . TIMEOUT . " seconds!\n";
    }
    
    sub look_for_something {
        # !! changes the number into either '1' or 'undef'
        # ! !! inverts the sense
        return ! !! int rand (2 * TIMEOUT / SLEEP); # False most of the time, occasionaly true     
    }
    

    Output

    perl test_find.pl
    0 seconds have elapsed...Value is false
    2 seconds have elapsed...Value is false
    4 seconds have elapsed...Value is false
    6 seconds have elapsed...Value is false
    8 seconds have elapsed...Value is false
    Timed out after 10 seconds!
    
    perl test_find.pl
    0 seconds have elapsed...Value is false
    2 seconds have elapsed...Value is false
    4 seconds have elapsed...Value is true
    Value is true...returning