int80h
int80h

Reputation: 265

Killing an application started using system() in Perl

I am trying to run an application inside a Perl script using system(). The application I'm running gets stuck sometimes (it enters some kind of infinite loop). Is there a way I can know if this application is stuck and kill it to continue with the Perl script?

I'm trying to do something like this:

start testapp.exe;
if(stuck with testapp.exe) {
    kill testapp.exe;
}

Upvotes: 2

Views: 6535

Answers (4)

Karel Bílek
Karel Bílek

Reputation: 37734

Determining if "it is stuck in infinite loop" is called Halting Problem and is undecidable.

If you want to kill it, you will have to fork the application using fork and then kill it from the other fork, if it is going for too long.

You can determine if the proccess is going for too long by this

use POSIX ":sys_wait_h";
waitpid($pid, WNOHANG)>0 #waitpid returns 0 if it still running

at least, according to this manual page

I am not sure how well it works on various systems, you can try it out.

Not a direct answer, but I can recommend using forks module if you want to fork with ease, but it works only on UNIX systems (not windows).


OK, more helping code :) It works in UNIX, according to perlfork perldoc, it should work on Windows exactly the same way.

use warnings;
use strict;

use POSIX ":sys_wait_h";
my $exited_cleanly;                 #to this variable I will save the info about exiting

my $pid = fork;
if (!$pid) {
     system("anything_long.exe");        #your long program 
} else {
     sleep 10;                           #wait 10 seconds (can be longer)
     my $result = waitpid(-1, WNOHANG);  #here will be the result

     if ($result==0) {                   #system is still running
         $exited_cleanly = 0;            #I already know I had to kill it
         kill('TERM', $pid);             #kill it with TERM ("cleaner") first
         sleep(1);                       #wait a bit if it ends
         my $result_term = waitpid(-1, WNOHANG);
                                         #did it end?

         if ($result_term == 0) {        #if it still didnt...
             kill('KILL', $pid);         #kill it with full force!
         }  
     } else {
         $exited_cleanly = 1;            #it exited cleanly
     }  
}

#you can now say something to the user, for example
if (!$exited_cleanly) {...} 

Upvotes: 2

aculich
aculich

Reputation: 14865

Here is one way you can handle the problem if you know that testapp should not take more than N seconds to do its thing, then you can use a timeout to kill the app by way of IPC::Run.

In the example below there is a timeout of 1 second which kills the sleep 10 command that takes too long (longer than the timeout of 1 second). If this doesn't do what you want, then you should provide more information about how you can detect that testapp.exe is "stuck".

#!/usr/bin/env perl

use IPC::Run qw( run timeout );

eval { # if (stuck with testapp.exe for more than N seconds)
    @cmd = ('sleep', '10'); # this could be testapp.exe instead of sleep
    run \@cmd, \$in, \$out, \$err, timeout( 1 ) or die "test"; # start testapp.exe
    print "do stuff if cmd succeeds\n";
};
print "more stuff to do afterwards whether or not command fails or succeeds\n";

Upvotes: 1

ikegami
ikegami

Reputation: 386706

system("start testapp")

is short for

system("cmd", "/c", "start testapp")

Perl just knows about cmd; it doesn't know anything about start, much less about testapp. system is not the tool you want. That's the first problem.


The second problem is that you haven't defined what it means to be "stuck". If you want to monitor a program, it needs a heartbeat. A heartbeat is a periodic activity that can be externally examined. It can be writing to a pipe. It can be changing a file. Anything.

The monitoring program listens for this heartbeat, and presumes the program is dead if the heart stops beating, so to speak.


"Killing" is done using signals in unix, but it's done using TerminateProcess in Windows. The third problem is that Perl core does not give you access to that function.


The solution to the first and third problem is Win32::Process. It allows you to launch a process in the background, and it also allows you to terminate it.

Creating a heartbeat is up to you.

Upvotes: 1

Jonathan Leffler
Jonathan Leffler

Reputation: 755074

You can't determine that the application is stuck if you execute it like that, because the system statement won't return until the application terminates.

So, at least, you need to start the test application so it can run asynchronously from the Perl script that is to monitor it.

Having resolved that part of the problem, you have to establish a mechanism that will allow the monitoring Perl script to determine that the application is stuck. That is a non-trivial exercise, and likely system dependent, unless you adopt a simple expedient such as requiring the application to write a heart-beat indication somewhere, and the Perl script monitors for the heart-beat. For example (not necessarily a good example), the application could write the current time into a file identified by its PID, and the Perl script could monitor the file to see if the heart-beat is sufficiently recent. Of course, this assumes that the 'infinite loop' doesn't include code that writes to the heart-beat file.

Upvotes: 0

Related Questions