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

Hello everyone, I have a script (> 400 Lines) in wich I have implemented a timeout routine using SIG{ALRM}. The code used is the following:
$cmd="myCommand"; # Timeout evaluation: eval { # Set action to be done when alarm occurs: local $SIG{ALRM} = sub { die "ALARM_COMMAND_TIMEOUT" }; # Schedule alarm in $CMD_TIMEOUT seconds: alarm($CMD_TIMEOUT); ### execute the command that can block $childPID = open(PIPE, "-|"); ### Unable to create child process unless(defined $childPID) { close(PIPE); die "ERROR_FORK_PROCESS: $!\n"; } ### Child Process Created ... execute command unless($childPID) { exec($cmd) or die "ERROR_OPEN_PIPE : $!\n"; ### Unable to create child process } wait; alarm(0); close(PIPE); }; alarm(0);
During the test period, I noticed something strange. When using this code, my whole script is 'respawned' instead of the single command I want to check for timeout. Maybe the code is wrong. All I need is to run a single command and wait for the result withing a given timeout. If the command takes too long I want to kill it. The script is running on AIX 4.3.3 & 5.2. Perl version is 5.8. Any suggestions ? Thank you very much.

Replies are listed 'Best First'.
Re: Timeout with SIG{ALRM} not working as expected
by Random_Walk (Prior) on Jan 19, 2005 at 12:04 UTC

    Why can you not do the relatively simple ?

    $cmd="myCommand"; # Timeout evaluation: eval { # Set action to be done when alarm occurs: local $SIG{ALRM} = sub { die "ALARM_COMMAND_TIMEOUT" }; # Schedule alarm in $CMD_TIMEOUT seconds: alarm($CMD_TIMEOUT); ### execute the command that can block $Results = `$cmd`; if ($?) {# optional error handling} } wait; alarm(0); close(PIPE); }; alarm(0);

    The backticks spawn a child to run the command and block 'till it returns

    Update

    OK, I tried that, well this perl -le'$SIG{ALRM}=sub{die "TIMEOUT"};eval{alarm 1;`sleep 100`};print $@' and it did not work for me, this did however

    perl -le'$SIG{ALRM}=sub {die "TIMEOUT"}; eval{alarm 10; open PIPE, "while [ 1 ] ;do echo \"foo\";sleep 5;done|"; while (<PIPE>){print}}; print $@'
    If you trust the child to die on SIGPIPE then it will die when the eval exits

    Cheers,
    R.

    Pereant, qui ante nos nostra dixerunt!
Re: Timeout with SIG{ALRM} not working as expected
by Anonymous Monk on Jan 19, 2005 at 11:37 UTC
    Not sure if that's your intention, but if the child process takes too long, you kill the parent (non-fatally, since it's done inside an eval). You make no attempt to kill the child. So, after a time-out, the parent goes on its merry way while neither closing the pipe, nor doing a wait. Bad things (for some value of bad) will happen: if the child has finished before the parent, the child will become a zombie (because you didn't wait for it) - and if the parent finished before the child, the child might die due to a SIGPIPE if it writes to the pipe after the parent has finished.
      I forgot to add the code just after the eval:
      if ($@ and $@ =~ /ALARM_COMMAND_TIMEOUT/) { &writeLog("Killing child process (pid = $childPID)"); kill(1, $childPID); }
A reply falls below the community's threshold of quality. You may see it by logging in.