tags:

views:

568

answers:

9

How do I write a program that tells when my other program ends?

A: 

This is called the "halting problem" and is not solvable. See http://en.wikipedia.org/wiki/Halting_problem

Viktor
A: 

If you want analyze one program without execution than it's unsolvable problem.

elephantum
+4  A: 

On Windows, a technique I've used is to create a global named object (such as a mutex with CreateMutex), and then have the monitoring program open that same named mutex and wait for it (with WaitForSingleObject). As soon as the first program exits, the second program obtains the mutex and knows that the first program exited.

On Unix, a usual way to solve this is to have the first program write its pid (getpid()) to a file. A second program can monitor this pid (using kill(pid, 0)) to see whether the first program is gone yet. This method is subject to race conditions and there are undoubtedly better ways to solve it.

Greg Hewgill
+5  A: 

If you want to write a generic program that determine from source code if another program will halt or not, you're out of luck as it is impossible. This is known as the Halting Problem.

On the other hand, if you just want your program to launch another program, and wait for its exit, take a look at the wait (if you only execute a single child) or the waitpid (if you have many childs and want to wait for end of execution of a specific one). However, those two function only work if you launched the other program.

If you didn't launch the process, then you can try to poll to see if the program is still alive. This can be done with the kill function, sending the 0 signal. That won't kill the program, but will return an error code if the process is no longer running. As most operating system do not immediately reuse pid, this should be relatively safe, even if you poll slowly (one time per second for example).

All of this only work on POSIX system.

Sylvain Defresne
A: 

Most operating systems its generally the same kind of thing....

you record the process ID of the program in question and just monitor it by querying the actives processes periodically

In windows at least, you can trigger off events to do it...

Keith Nicholas
+10  A: 

The only way to do a waitpid() or waitid() on a program that isn't spawned by yourself is to become its parent by ptrace'ing it.

Here is an example of how to use ptrace on a posix operating system to temporarily become another processes parent, and then wait until that program exits. As a side effect you can also get the exit code, and the signal that caused that program to exit.:

#include <sys/ptrace.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc, char** argv) {

    int pid = atoi(argv[1]);
    int status;
    siginfo_t si;

    switch (ptrace(PTRACE_ATTACH, pid, NULL)) {
        case 0:
            break;
        case -ESRCH:
        case -EPERM:
            return 0;
        default:
            fprintf(stderr, "Failed to attach child\n");
            return 1;
    }
    if (pid != wait(&status)) {
        fprintf(stderr, "wrong wait signal\n");
        return 1;
    }
    if (!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGSTOP))  {
        /* The pid might not be running */
        if (!kill(pid, 0)) {
            fprintf(stderr, "SIGSTOP didn't stop child\n");
            return 1;
        } else {
            return 0;
        }
    }
    if (ptrace(PTRACE_CONT, pid, 0, 0)) {
        fprintf(stderr, "Failed to restart child\n");
        return 1;
    }

    while (1) {
        if (waitid(P_PID, pid, &si, WSTOPPED | WEXITED)) {
            // an error occurred.
            if (errno == ECHILD)
                return 0;
            return 1;
        }
        errno = 0;

        if (si.si_code & (CLD_STOPPED | CLD_TRAPPED)) {
            /* If the child gets stopped, we have to PTRACE_CONT it
             * this will happen when the child has a child that exits.
             **/
            if (ptrace(PTRACE_CONT, pid, 1, si.si_status)) {
                if (errno == ENOSYS) {
                    /* Wow, we're stuffed. Stop and return */
                    return 0;
                }
            }
            continue;
        }

        if (si.si_code & (CLD_EXITED | CLD_KILLED | CLD_DUMPED)) {
            return si.si_status;
        }
        // Fall through to exiting.
        return 1;
    }
}
Jerub
A: 

Umm you can't, this is an impossible task given the nature of it.

Let's say you have a program foo that takes as input another program foo-sub.

Foo {
 func Stops(foo_sub) { run foo_sub; return 1; }
}

The problem with this all be it rather simplistic design is that quite simply if foo-sub is a program that never ends, foo itself never ends. There is no way to tell from the outside if foo-sub or foo is what is causing the program to stop and what determines if your program simply takes a century to run?

Essentially this is one of the questions that a computer can't answer. For a more complete overview, Wikipedia has an article on this.

jtyost2
+3  A: 

If you want to spawn another process, and then do nothing while it runs, then most higher-level languages already have built-ins for doing this. In Perl, for example, there's both system and backticks for running processes and waiting for them to finish, and modules such as IPC::System::Simple for making it easier to figure how the program terminated, and whether you're happy or sad about that having happened. Using a language feature that handles everything for you is way easier than trying to do it yourself.

If you're on a Unix-flavoured system, then the termination of a process that you've forked will generate a SIGCHLD signal. This means your program can do other things your child process is running.

Catching the SIGCHLD signal varies depending upon your language. In Perl, you set a signal handler like so:

use POSIX qw(:sys_wait_h);

sub child_handler {

    while ((my $child = waitpid(-1, WNOHANG)) > 0) {
         # We've caught a process dying, its PID is now in $child.
         # The exit value and other information is in $?
    }

    $SIG{CHLD} \&child_handler;  # SysV systems clear handlers when called,
                                 # so we need to re-instate it.
}

# This establishes our handler.
$SIG{CHLD} = \&child_handler;

There's almost certainly modules on the CPAN that do a better job than the sample code above. You can use waitpid with a specific process ID (rather than -1 for all), and without WNOHANG if you want to have your program sleep until the other process has completed.

Be aware that while you're inside a signal handler, all sorts of weird things can happen. Another signal may come in (hence we use a while loop, to catch all dead processes), and depending upon your language, you may be part-way through another operation!

If you're using Perl on Windows, then you can use the Win32::Process module to spawn a process, and call ->Wait on the resulting object to wait for it to die. I'm not familiar with all the guts of Win32::Process, but you should be able to wait for a length of 0 (or 1 for a single millisecond) to check to see if a process is dead yet.

In other languages and environments, your mileage may vary. Please make sure that when your other process dies you check to see how it dies. Having a sub-process die because a user killed it usually requires a different response than it exiting because it successfully finished its task.

All the best,

Paul

pjf
+2  A: 

Are you on Windows ? If so, the following should solve the problem - you need to pass the process ID:

  bool WaitForProcessExit( DWORD _dwPID )
  { 
     HANDLE hProc = NULL;
     bool bReturn = false;

     hProc = OpenProcess(SYNCHRONIZE, FALSE, _dwPID);

     if(hProc != NULL)
     {
       if ( WAIT_OBJECT_0 == WaitForSingleObject(hProc, INFINITE) )
       {
         bReturn = true;
       }
     }

     CloseHandle(hProc) ;
  }

  return bReturn;
}

Note: This is a blocking function. If you want non-blocking then you'll need to change the INFINITE to a smaller value and call it in a loop (probably keeping the hProc handle open to avoid reopening on a different process of the same PID).

Also, I've not had time to test this piece of source code, but I lifted it from an app of mine which does work.

RichS