http://www.perlmonks.org?node_id=877819

aceofspace has asked for the wisdom of the Perl Monks concerning the following question:

Hi, I've never used the alarm function as mentioned here:

http://perldoc.perl.org/functions/alarm.html

So I made a test script to see how it works.
But I couldn't get it working. Could you kindly help?
Here's the test script I wrote:
eval { local $SIG{ALRM} = sub { die "alarm\n" }; alarm $timeout; &do_program(1, 100); alarm 0; }; if($@) { print "$@<BR>\n"; }

&do_program(1, 100) will take about 2 minutes to run from start to finish.

Above script does not work because &do_program(1, 100) will continue to run from start to finish in 2 minutes regardless what I set the $timeout variable at different numbers less than 120(2 minutes).

Awaiting your wisdom to solve this problem. Thanks.

Jack

Replies are listed 'Best First'.
Re: Perl Alarm Not Working
by cdarke (Prior) on Dec 19, 2010 at 13:14 UTC
    The following works for me:
    #!/usr/local/bin/perl use warnings; use strict; sub do_program { print "Please wait for a timeout....\n"; <STDIN> } my $timeout = 5; eval { local $SIG{ALRM} = sub { die "alarm\n" }; alarm $timeout; &do_program(1, 100); alarm 0; }; if($@) { print "$@<BR>\n"; }
    Also on Linux 2.6.18.
    You don't give details of what do_program is actually doing. If, as the name implies, it is running another process, then you should be aware that pending alarms are cleared in a child process. This is a UNIX feature, not anything to do with Perl. It then depends on how you are actually running the external process, and how you are waiting for it.

    Proc::Background might help, it has a timeout feature.

    Update: it occurs to me that, because of your use of <BR> in the print statement, you might be running under mod_perl. There are some notes concerning alarm here: http://perl.apache.org/docs/2.0/user/coding/coding.html.
Re: Perl Alarm Not Working
by ikegami (Patriarch) on Dec 19, 2010 at 05:04 UTC
    What OS are you running, and what's do_program?
      Operation system is Linux 2.6.18

      do_program is a program that abstracts site information from different domains.

      The domains are stored in a MySql database, so the program will retrieve domains from the database, then abstract site information from these domains.

      Jack
Re: Perl Alarm Not Working
by ww (Archbishop) on Dec 19, 2010 at 13:28 UTC

    Still need more detail about &do_program(1, 100);. Is that (merely) a subroutine in the same script or is it a sub that calls an external program... or ??? IOW, show us the code of the sub.

    Semi-irrelevant: The value in $@ is the error message from whatever you're calling in &do_program(1, 100);; it's not going to be populated the the case you describe.

      &do_program(1, 100); is a subroutine in the same script.

      In a nutshell, calling the &do_program(1, 100), it will do the followings:

      1) Connect to the MySql database
      2) Retrive 100 domains beginning from row 1 for 100 rows in one of the tables.
      3) Remote access each of the 100 domains using LWP::UserAgent
      4) Then abstract the site(domain) page information

      It generally takes about 2 minutes to complete all of the above steps 1 to 4.
      (Notes: I've watched it ran many times, it took around that kind time to complete.)

      I'm mainly trying to find out if the scripts in the eval { } will work or not.

      They do not seem to work, because &do_program(1, 100) will continue to run from start to finish even though I set the alarm timeout variable, $timeout at different numbers less than 120(2 minutes), e.g. $timeout=20, $timeout=10, $timeout=5... down to 1 second $timeout=1.

      For some reason, there is no alarm signal generated in the eval {} block??
        As I said, your code works for me (in an eval block).

        One possibility is that MySQL is itself changing the signal handling. Mr. Google brought up a few suggestions: http://forums.mysql.com/read.php?51,256433,256478#msg-256478 is typical, with some alternative code, but I would have thought this would be common.

        The DBI provides the cancel operation on statement handles $sth->cancel() specifically for calling from alarm handlers. This implies that the database driver itself might be immune to SIGALRM and it is up to the Perl signal handler to cancel the operation.
        • "there is no alarm signal generated in the eval {} block?? "
          So, use debug or print statements to see if that's actually so... and, not just incidentally, time the sub at sub-second resolution (Time::HiRes, for example) or using (your favorite flavor of) profiler.
        • The pseudo-code in your points 1) .. 4) does NOT inform me of any possible issues inside the sub. Code, as requested (unless long) * would do so.

        * And if it's more than 10-15 lines, consider reducing it to a minimal form that still exhibits the problem. That serves two purposes: first, it may make something jump into view that clears up your question and, second, it makes it easier for us to help whilst our crystal balls are broken and psi blocked by DHS.

        My only guess is that the MySQL driver uses alarm internally, leaving it canceled.

        Can you simulate the DB code for testing purposes?

Re: Perl Alarm Not Working
by BrowserUk (Patriarch) on Dec 19, 2010 at 21:11 UTC

    With Deferred (or Safe) Signals enabled--the default since 5.8.something--there are (many) somethings that cannot be interrupted by SIGALRM.

    Reading your later description of do_program(), it seems like that it is doing many, maybe even most, of those somethings.

    You may find the use of Perl::Unsafe::Signals worth testing.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      The db code won't be interruptible* (a good thing), but the LWP stuff will be. No idea what "abstract the site page information" means, much less how it's implemented, so I don't know about that.

      * — That means the signal handler will be called when the db code returns if the signal was sent when the db code was executing.

        the LWP stuff will be [interuptable]

        You sure?

        When I run the following two one liners, 'here' is printed after 30 seconds--when the server times out--not after 10 as it would be if the alarm had interupted the socket read.

        C:\test>start /b perl -MIO::Socket -E"$s=IO::Socket::INET->new(Listen=>1,LocalPort=>12345);$c=$s->accept; +sleep 30" C:\test>perl -MIO::Socket -E"$s=IO::Socket::INET->new('localhost:12345');eval{alarm 10;<$s>};pri +nt'here'" here

        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.