-1

In the following perl script, i intend to timeout the execution of script child_script.pl if it goes beyond 1 hour. However, the logic doesn't seem to be working as the script is still running after the specified time limit.

Any guesses what am I doing wrong here?

I'm referring to follwing documentation for implementing the timeout in perl: https://docstore.mik.ua/orelly/perl4/cook/ch16_22.htm

It works fine for a standalone command. Is it possible that system command has an issue ?

MY_CODE (parent_script.pl)

#!/usr/bin/perl

my $sys_cmd = " perl child_script.pl 2>&1 | tee logfile.txt \n";

print "INFO: Enter alarm..\n";
eval {
    local $SIG{ALRM} = sub { die "alarm clock restart" };
    alarm 3600;                   # schedule alarm in 1 hours
    eval {
            print "INFO: Run script.. \n";
            system ($sys_cmd);
    };
    alarm 0;                    # cancel the alarm
};
alarm 0;                        # race condition protection
die if $@ && $@ !~ /alarm clock restart/; # reraise
print "INFO: Exit alarm..\n";
Yash
  • 2,944
  • 7
  • 25
  • 43
  • See also [Timeout for launching a bash command in perl](https://stackoverflow.com/q/50040421/2173773), [Perl: retrieve output from process in IPC::Run if it dies](https://stackoverflow.com/q/50392512/2173773), and [Perl timeout command in windows and linux](https://stackoverflow.com/q/53770493/2173773) for alternative approaches using among others [`IPC::Run`](https://metacpan.org/pod/IPC::Run) – Håkon Hægland Jan 14 '20 at 11:08

2 Answers2

2

You don't time out the script which you have started; you set the alarm for the current (parent) script. You would have to kill the child process (the one you have started using system) from your alarm-sub.

EDIT:

If you have /usr/bin/timeout (as it would be the case if you are running on Linux), it would perhaps be more convenient to use this command for handling the timeout, instead of re-implementing the logic in Perl.

user1934428
  • 19,864
  • 7
  • 42
  • 87
  • Hey user1934428, Sorry I couldn't get your point. I dont intend to kill the parent_script.pl script. I want to kill the child_script.pl if it continues to run after 1 hour. I have updated the names of script in the description for better clarity. Thanks! – Yash Jan 14 '20 at 09:39
  • 1
    But the alarm signal goes to the parent script, not the child. – choroba Jan 14 '20 at 09:46
  • @Yash : I know that you want to; but nothing in your code is killing the child script. – user1934428 Jan 14 '20 at 09:49
  • @Yash : Killing your child can be done using the [kill](https://perldoc.perl.org/functions/kill.html) function of Perl, and there is no _kill_ in your code. – user1934428 Jan 14 '20 at 09:50
  • Thanks, let me check if `kill` helps. – Yash Jan 14 '20 at 10:39
1

The SIGALRM signal may or may not be sent while your system call is running, so alarm may be flaky. This is a good use case for the poor man's alarm.

my $sys_cmd = "perl child_script.pl 2>&1 | tee logfile.txt";
# step 1. Start your long running command and capture it's process id
my $pid = fork();
if ($pid == 0) {
    exec($sys_cmd);
}
# step 2. Start another subprocess for the poor man's alarm.
my $time = 3600;
if (fork() == 0) {
    exec("$^X","-e","sleep 1,kill(0,$pid)||exit for 1..$time;kill -9,$pid");
}
# step 3. wait for first process to finish or be killed
my $c = waitpid $pid, 0;
if ($c & 128 == 9) {
    print "Process timed out and was killed by the poor man's alarm\n";
} else {
    print "Process finished without timing out.\n";
}

The poor man's alarm runs in a separate process with two parameters: a $pid to monitor and a $time to wait. It periodically checks to see if the process being monitored is still alive. If the process is no longer alive, then the poor man's alarm also exits without doing anything. After $time seconds have passed, and the monitored process is still hanging around, the poor man's alarm sends a kill signal to the process, which should terminate it.

mob
  • 117,087
  • 18
  • 149
  • 283
  • Are we sure that `my $time = 3600` is in seconds. I ran this piece of code with `$time = 600` but it keeps on running well beyond 10 mins. – Yash Jan 15 '20 at 02:10