Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

trying to get timeout to work

by Skeeve (Parson)
on Apr 11, 2012 at 12:58 UTC ( [id://964533]=perlquestion: print w/replies, xml ) Need Help??

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

I'm desperately trying to get timeouts to work and came up with this test code:

use Sys::SigAction qw( set_sig_handler ); eval { my $h = set_sig_handler( 'ALRM' ,sub { die "TIMEOUT\n"; }, { mask=>[ 'ALRM' ], safe=>1, }); eval { alarm 3; $opened= open my $check, '-|', 'sleep 60'; if ($opened) { # slurp its output $line= do { local $/; <$check>; }; $exit_code= $!+0; $closed= close $check; } alarm 0; }; alarm 0; die $@ if $@; }; alarm 0; print $@;
Of course I can't really ready something from a "sleep" command, But I expected this to return after 3 seconds with a "Timeout". Instead I have to wait 60 seconds, so the full sleep-time.

I also tried a different command than "sleep" like, for example, a long running "find" command but still the sleep does not die.

What am I doing wrong or what do I misunderstand?


s$$([},&%#}/&/]+}%&{})*;#$&&s&&$^X.($'^"%]=\&(|?*{%
+.+=%;.#_}\&"^"-+%*).}%:##%}={~=~:.")&e&&s""`$''`"e

Replies are listed 'Best First'.
Re: trying to get timeout to work
by Eliya (Vicar) on Apr 11, 2012 at 14:13 UTC

    alarm doesn't automatically kill the piped subprocess (and the parent waits for it), so you need to take care of this yourself, e.g. in the signal handler

    use Sys::SigAction qw( set_sig_handler ); eval { my $h = set_sig_handler( 'ALRM' ,sub { kill 15, $opened; # <-- die "TIMEOUT\n"; }, { ...
Re: trying to get timeout to work (easier with threads)
by BrowserUk (Patriarch) on Apr 11, 2012 at 17:08 UTC

    T'is easier with a thread, See Re: Backticks and SIGALRM.

    Ignore this:


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    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 start of some sanity?

      That sounds promising. I will have to check threads and threads::shared as I in fact need (or wanted) to:

      1. Run several system commands in parallel
      2. Store the results in an sqlite DB
      3. Time out if a command runs too long

      As it seemed to be not too easy to get the data from the parallel commands back to the parent, so that I just have one writer to the sqlite DB, I skipped this part for now and concentrated on the timeout part.

      It seems with threads::shared, I can achieve what I wanted in the first place. Thanks BrowserUk!


      s$$([},&%#}/&/]+}%&{})*;#$&&s&&$^X.($'^"%]=\&(|?*{%
      +.+=%;.#_}\&"^"-+%*).}%:##%}={~=~:.")&e&&s""`$''`"e

        Here's an improved version.

        • The old version always waited for at least the timeout period, even if the command completed early.

          This one returns as soon as the external command terminates.

        • The old version returned nothing if the process timed out.

          This version will return as much as was available before the process was killed.

        • It also addresses a potential "Uninitialised" warning if the system is heavily loaded and the thread takes a while to start.

          If the thread or process was slow starting due to system load or other factors, the old version could try to use $pid before it was set. This corrects that error.

        #! perl -slw use strict; use threads; use threads::shared; $|++; our $N ||= 11; my $TIMEOUT = 10; my $extApp = q[ perl -lwe"$|++; print $_ and sleep 1 for 1 .. $ARGV[0] +" ]; my @results :shared; my $pid :shared; async { $pid = open my $fh, "$extApp $N |" or die "$!, $^E"; push @results, $_ while <$fh>; }->detach; sleep 1 until $pid; sleep 1 while kill 0, $pid and $TIMEOUT--; kill 3, $pid and warn 'Command timed out' if $TIMEOUT; print "command return \n'@results'"; __END__ C:\test>timeoutcmd -N=1 command return '1 ' C:\test>timeoutcmd -N=10 command return '1 2 3 4 5 6 7 8 9 10 ' C:\test>timeoutcmd -N=11 Command timed out at C:\test\timeoutcmd.pl line 21. command return '1 2 3 4 5 6 7 8 9 10 11 '

        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        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 start of some sanity?

Re: trying to get timeout to work
by halfcountplus (Hermit) on Apr 11, 2012 at 14:03 UTC

    It's because of the eval. Throw this in after you declare $h:

    local $SIG{ALRM};
    And see what happens.

      The problem with this is that the alarm signal will then simply kill the script... (which is the default action for SIGALRM)

        Yes, but it will happen in the requested 3 seconds. If you want a non-fatal handler:

        local $SIG{ALRM} = sub { print STDERR "hey\n" };
        Ie, there is a difference in behaviour between that and set_sig_handler(). Whoops, no there isn't, but adding STDERR reporting does shed some light on when the alarm fires...

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://964533]
Approved by lidden
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others chilling in the Monastery: (4)
As of 2024-03-19 03:40 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found