27

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.

Lazer
  • 90,700
  • 113
  • 281
  • 364
  • 1
    This is a duplicate more times than I can count: http://stackoverflow.com/questions/2679582/how-can-i-kill-a-perl-system-call-after-a-timeout http://stackoverflow.com/questions/1962985/how-can-i-timeout-a-forked-process-that-might-hang http://stackoverflow.com/questions/2423288/ways-to-do-timeouts-in-perl http://stackoverflow.com/questions/3427401/perl-make-script-timeout-after-x-number-of-seconds http://stackoverflow.com/questions/1165316/how-can-i-limit-the-time-spent-in-a-specific-section-of-a-perl-script are just a few. – Ether Oct 19 '10 at 16:05

4 Answers4

29

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
}
AndyG
  • 39,700
  • 8
  • 109
  • 143
draegtun
  • 22,441
  • 5
  • 48
  • 71
  • 2
    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? – antred Feb 17 '17 at 10:53
  • 1
    @antred - To get the process id then you need to rewrite it using [`fork`](http://perldoc.perl.org/perlfork.html) instead of [`system`](http://perldoc.perl.org/functions/system.html). Have a look at modules like IPC::Cmd & IPC::Run to make this easier (in this context). – draegtun Feb 19 '17 at 13:44
15

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

ysth
  • 96,171
  • 6
  • 121
  • 214
4

How about System::Timeout ?

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

timeout("3", "sleep 9"); # timeout exit after 3 seconds
melpomene
  • 84,125
  • 8
  • 85
  • 148
John Chain
  • 658
  • 4
  • 9
  • 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. – miken32 Jan 31 '17 at 20:29
  • 1
    A link to a potential solution is always welcome, but please [add context around the link](http://meta.stackexchange.com/a/8259) 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?](http://stackoverflow.com/help/deleted-answers). – FelixSFD Jan 31 '17 at 20:29
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.

Kjetil S.
  • 3,468
  • 20
  • 22
  • 2
    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. – dennisjbell Apr 06 '20 at 15:03
  • 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. – Bill McGonigle Oct 19 '20 at 21:25