Shawn Blakesley
Shawn Blakesley

Reputation: 1903

Perl kill(0, $pid) in Windows always returning 1

I'm trying to make a Perl script that will run a set of other programs in Windows. I need to be able to capture the stdout, stderr, and exit code of the process, and I need to be able to see if a process exceeds it's allotted execution time. Right now, the pertinent part of my code looks like:

...
        $pid = open3($wtr, $stdout, $stderr, $command);
        if($time < 0){
            waitpid($pid, 0);
            $return = $? >> 8;
            $death_sig = $? & 127;
            $core_dump = $? & 128;
        }
        else{
            # Do timeout stuff, currently not working as planned
            print "pid: $pid\n";
            my $elapsed = 0;
            #THIS LOOP ONLY TERMINATES WHEN $time > $elapsed ...?
            while(kill 0, $pid and $time > $elapsed){
                Time::HiRes::usleep(1000);  # sleep for milliseconds
                $elapsed += 1;
                $return = $? >> 8;
                $death_sig = $? & 127;
                $core_dump = $? & 128;
            }
            if($elapsed >= $time){
                $status = "FAIL";
                print $log "TIME LIMIT EXCEEDED\n";
            }
        }
        #these lines are needed to grab the stdout and stderr in arrays so 
        #  I may reuse them in multiple logs
        if(fileno $stdout){
            @stdout = <$stdout>;
        }
        if(fileno $stderr){
            @stderr = <$stderr>;
        }
...

Everything is working correctly if $time = -1 (no timeout is needed), but the system thinks that kill 0, $pid is always 1. This makes my loop run for the entirety of the time allowed.

Some extra details just for clarity:

For that future person who may have a similar issue I got the code to work, here is the modified code sections:

        $pid = open3($wtr, $stdout, $stderr, $command);
        close($wtr);
        if($time < 0){
            waitpid($pid, 0);
        }
        else{
            print "pid: $pid\n";
            my $elapsed = 0;
            while(waitpid($pid, WNOHANG) <= 0 and $time > $elapsed){
                Time::HiRes::usleep(1000);  # sleep for milliseconds
                $elapsed += 1;
            }

            if($elapsed >= $time){
                $status = "FAIL";
                print $log "TIME LIMIT EXCEEDED\n";
            }
        }
        $return = $? >> 8;
        $death_sig = $? & 127;
        $core_dump = $? & 128;
        if(fileno $stdout){
            @stdout = <$stdout>;
        }
        if(fileno $stderr){
            @stderr = <$stderr>;
        }
        close($stdout);
        close($stderr);

Upvotes: 2

Views: 1404

Answers (1)

ikegami
ikegami

Reputation: 385789

Instead of kill 0, use waitpid $pid, WNOHANG:

use POSIX qw( WNOHANG );

if (waitpid($pid, WNOHANG) > 0) {
   # Process has ended. $? is set.
   ...
}

Upvotes: 4

Related Questions