How can I specify timeout limit for Perl system call?

15,396

Solution 1

See the alarm function. Example from pod:

eval {
    local $SIG{ALRM} = sub { die "alarm\n" }; # NB: \n required
    alarm $timeout;
    $nread = sysread SOCKET, $buffer, $size;
    alarm 0;
};
if ($@) {
    die unless $@ eq "alarm\n";   # propagate unexpected errors
    # timed out
}
else {
    # didn't
}

There are modules on CPAN which wrap these up a bit more nicely, for eg: Time::Out

use Time::Out qw(timeout) ;

timeout $nb_secs => sub {
  # your code goes were and will be interrupted if it runs
  # for more than $nb_secs seconds.
};

if ($@){
  # operation timed-out
}

Solution 2

You can use IPC::Run's run method instead of system. and set a timeout.

Solution 3

How about System::Timeout ?

This module extends system to allow timeout after the specified seconds.

timeout("3", "sleep 9"); # timeout exit after 3 seconds

Solution 4

I've just used the timeout command in Perl + Linux before, something you can test like this:

for(0..4){
  my $command="sleep $_";  #your command
  print "$command, ";
  system("timeout 1.1s $command");  # kill after 1.1 seconds
  if   ($? == -1  ){ printf "failed to execute: $!" }
  elsif($?&127    ){ printf "died, signal %d, %scoredump", $?&127, $?&128?'':'no '}
  elsif($?>>8==124){ printf "timed out" }
  else             { printf "child finished, exit value %d", $? >> 8 }
  print "\n";
}

Output after 4.317 seconds:

sleep 0, child finished, exit value 0
sleep 1, child finished, exit value 0
sleep 2, timed out
sleep 3, timed out
sleep 4, timed out

The timeout command is part of all major "normal" Linux distributions a.f.a.i.k, a part of coreutils.

Share:
15,396

Related videos on Youtube

Lazer
Author by

Lazer

Updated on December 01, 2020

Comments

  • Lazer
    Lazer over 3 years

    Sometimes my system call goes into a never ending state. To, avoid that I want to be able to break out of the call after a specified amount of time.

    Is there a way to specify a timeout limit to system?

    system("command", "arg1", "arg2", "arg3");
    

    I want the timeout to be implemented from within Perl code for portability, and not using some OS specific functions like ulimit.

  • miken32
    miken32 over 7 years
    Link-only answers are not helpful when content moves or changes. Include the relevant parts of the link in your answered, tailored to match the question.
  • FelixSFD
    FelixSFD over 7 years
    A link to a potential solution is always welcome, but please add context around the link so your fellow users will have some idea what it is and why it’s there. Always quote the most relevant part of an important link, in case the target site is unreachable or goes permanently offline. Take into account that being barely more than a link to an external site is a possible reason as to Why and how are some answers deleted?.
  • antred
    antred about 7 years
    This seems to work, but the problem is that the command executed via the system call will continue to run, even if the parent Perl process has already died. Does anyone know how to obtain the process id / handle of the process run by the system call so that we can kill it before the Perl script ends?
  • sger
    sger about 7 years
    @antred - To get the process id then you need to rewrite it using fork instead of system. Have a look at modules like IPC::Cmd & IPC::Run to make this easier (in this context).
  • dennisjbell
    dennisjbell about 4 years
    I don't know why this answer isn't higher. For the context of "when running a system call", using a system solution seems apropo, and is simple and direct.
  • Bill McGonigle
    Bill McGonigle over 3 years
    Perfect for "perl as glue code". Thanks. Next time I'm writing a perl application, IPC::Run will get a go but this is right when I'm shelling out anyway.