Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
951 views
in Technique[技术] by (71.8m points)

windows - perl alarm with subprocess

I have a perl script that runs a series of batch scripts for regression testing. I want to implement a timeout on the batch scripts. I currently have the following code.

    my $pid = open CMD, "$cmd 2>&1 |";
    eval {
               # setup the alarm
               local $SIG{ALRM} = sub { die "alarm
" };
               # alarm on the timeout
               alarm $MAX_TIMEOUT;
               log_output("setting alarm to $MAX_TIMEOUT
");

               # run our exe
               while( <CMD> ) {
                       $$out_ref .= $_;
               }
               $timeRemaining = alarm 0;
            };
            if ($@) {
                    #catch the alarm, kill the executable
            }

The problem is that no matter what I set the max timeout to, the alarm is never tripped. I've tried using Perl::Unsafe::Signals but that did not help.

Is this the best way to execute the batch scripts if I want to be able to capture their output? Is there another way that would do the same thing that would allow me to use alarms, or is there another method besides alarms to timeout the program?

I have built a test script to confirm that alarm works on with my perl and windows version, but it does not work when I run a command like this.

I'm running this with activeperl 5.10.1 on windows 7 x64.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

It's hard to tell when alarm will work, when a system call will and won't get interrupted by a SIGALRM, how the same code might behave differently on different operating systems, etc.

If your job times out, you want to kill the subprocess you have started. This is a good use case for the poor man's alarm:

my $pid = open CMD, "$cmd 2>&1 |";
my $time = $MAX_TIMEOUT;

my $poor_mans_alarm = "sleep 1,kill(0,$pid)||exit for 1..$time;kill -9,$pid";
if (fork() == 0) {
    exec($^X, "-e", $poor_mans_alarm);
    die "Poor man's alarm failed to start";  # shouldn't get here
}
# on Windows, instead of  fork+exec, you can say
#    system 1, qq[$^X -e "$poor_mans_alarm"]


...

The poor man's alarm runs in a separate process. Every second, it checks whether the process with identifier $pid is still alive. If the process isn't alive, the alarm process exits. If the process is still alive after $time seconds, it sends a kill signal to the process (I used 9 to make it untrappable and -9 to take out the whole subprocess tree, your needs may vary).

(The exec actually may not be necessary. I use it because I also use this idiom to monitor processes that might outlive the Perl script that launched them. Since that wouldn't be the case with this problem, you could skip the exec call and say

if (fork() == 0) {
    for (1..$time) { sleep 1; kill(0,$pid) || exit }
    kill -9, $pid;
    exit;
}

instead.)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...