jsh6303
jsh6303

Reputation: 2040

Perl kill process with timeout ignored

I was testing my source code, in which the child process calls several other programs (some of which are C++).

#Other variables and functions

my $MAX_TIME = 10;#testing 10 minutes
my $timeRemaining = $MAX_TIME * 60;
my $pid = fork();
if ( $pid == 0 ) {
  #child process
  my $nowTime = localtime;
  print "Run started at $nowTime\n";

  #This run() calls a for loop in perl, in each iteration there are several C++ programs
  run();

  setpgrp(0,0);
}
elsif ($pid > 0){
  my $nowTime = localtime;
  eval{
    local $SIG{ALRM} = sub {
      kill -9, $pid;
      print "Run completed at $nowTime\nJob time out for $MAX_TIME minutes\n";
      log();
      die "TIMEOUT!\n";
    };
    alarm $timeRemaining;
    waitpid($pid, 0);
  };
  print "Run completed at $nowTime with no timeout\n";
}

When I checked the print out, I noticed that after 10 minutes, the "Run completed at $nowTime with no timeout\n" part gets printed out, and the child process is still executing. The die "TIMEOUT!\n"; part in the parent process does not get executed. Is it because of the C++ programs that the perl program calls cannot be killed once it started?

Upvotes: 0

Views: 384

Answers (1)

ikegami
ikegami

Reputation: 385789

First of all, kill is failing because $pid isn't a process group.

run();
setpgrp(0,0);

should be

setpgrp(0,0);
run();

Secondly, the reason you see

Run completed at $nowTime with no timeout

even when there's a timeout is that you execute

print "Run completed at $nowTime with no timeout\n"; 

whether there's a timeout or not.


Thirdly, you don't disable the alarm when the child is reaped. Add

alarm(0);

Fourthly, you expect $nowTime to contain the current time without making it so.


Finally, you still need to reap your child even if you kill it. (Ok, this can be skipped if the parent exits immediately anyway.)


Fixed:

use strict;
use warnings;

use POSIX qw( strftime );

sub current_time { strftime("%Y-%m-%d %H:%M:%S", localtime) }

sub run {
  print("a\n");
  system('perl', '-e', 'sleep 3;');
  print("b\n");
  system('perl', '-e', 'sleep 3;');
  print("c\n");
}

my $MAX_TIME = 5;
my $pid = fork();
die($!) if !defined($pid);
if ($pid) {
  if (eval{
    local $SIG{ALRM} = sub {
      kill KILL => -$pid;
      die "TIMEOUT!\n";
    };
    alarm($MAX_TIME);
    waitpid($pid, 0);
    alarm(0);
    return 1;
  }) {
    print "[".current_time()."] Run completed.\n";
  } else {
    die($@) if $@ ne "TIMEOUT!\n";
    print "[".current_time()."] Run timed out.\n";
    waitpid($pid, 0);
    print "[".current_time()."] Child reaped.\n";
  }
} else {
  print "[".current_time()."] Run started.\n";
  setpgrp(0,0);
  run();
}

Output:

[2017-05-11 14:58:06] Run started.
a
b
[2017-05-11 14:58:11] Run timed out.
[2017-05-11 14:58:11] Child reaped.

Upvotes: 3

Related Questions