views:

2586

answers:

7

In C++ Windows app, I launch several long running child processes (currently I use CreateProcess(...) to do this.

I want the child processes to be automatically closed if my main processes crashes or is closed.

Because of the requirement that this needs to work for a crash of the "parent", I believe this would need to be done using some API/feature of the operating system. So that all the "child" processes are cleaned up.

How do I do this?

A: 

You'd probably have to keep a list of the processes you start, and kill them off one by one when you exit your program. I'm not sure of the specifics of doing this in C++ but it shouldn't be hard. The difficult part would probably be ensuring that child processes are shutdown in the case of an application crash. .Net has the ability to add a function that get's called when an unhandled exception occurs. I'm not sure if C++ offers the same capabilities.

Kibbee
>> if my main processes crashes
jm
+11  A: 

The Windows API supports objects called "Job Objects". The following code will create a "job" that is configured to shut down all processes when the main application ends (when its handles are cleaned up). This code should only be run once.:

HANDLE ghJob = CreateJobObject( NULL, NULL); // GLOBAL
if( ghJob == NULL)
  {
  ::MessageBox( 0, "Could not create job object", "TEST", MB_OK);
  }
else
  {
  JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = { 0 };

// Configure all child processes associated with the job to terminate when the
jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
if( 0 == SetInformationJobObject( ghJob, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli)))
  {
  ::MessageBox( 0, "Could not SetInformationJobObject", "TEST", MB_OK);
  }

Then when each child process is created, execute the following code to launch each child each process and add it to the job object:

STARTUPINFO info={sizeof(info)};
PROCESS_INFORMATION processInfo;

    // Launch child process - example is notepad.exe
if (::CreateProcess( NULL, "notepad.exe", NULL, NULL, TRUE, 0, NULL, NULL, &info, &processInfo))
  {
      ::MessageBox( 0, "CreateProcess succeeded.", "TEST", MB_OK);
      if( ghJob)
    {
    if( 0 == AssignProcessToJobObject( ghJob, processInfo.hProcess))
      {
          ::MessageBox( 0, "Could not AssignProcessToObject", "TEST", MB_OK);
      }
    }

  // Can we free handles now? Not sure about this.
  //CloseHandle(processInfo.hProcess); 
  CloseHandle(processInfo.hThread);
      }

VISTA NOTE: See this link if you encounter access-denied issues with AssignProcessToObject() on vista: http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1150478&SiteID=1

jm
To answer your question in the comment: Yes, you should CloseHandle when you don't need the handle anymore.
Adam Mitz
Yeah, but will that end the job?
jm
No, I don't think so. JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE causes the job to terminate when the last *job* handle is closed, process handles shouldn't make a difference. Have you tried it?
Adam Mitz
It doesn't work on Windows 2000 :( (But it should under other versions of windows)
Rook
A: 

You can keep a separate watchdog process running. Its only task is watching the current process space to spot situations like you describe. It could even re-launch the original application after a crash or provide different options to the user, collect debug information, etc. Just try to keep it simple enough so that you don't need a second watchdog to watch the first one.

Pedro
+2  A: 

One somewhat hackish solution would be for the parent process to attach to each child as a debugger (use DebugActiveProcess). When a debugger terminates all its debuggee processes are terminated as well.

A better solution (assuming you wrote the child processes as well) would be to have the child processes monitor the parent and exit if it goes away.

Rob Walker
Instead of having to call DebugActiveProcess, you could just pass DEBUG_PROCESS as one of your creation flags to CreateProcess. Less code that way.
mrduclaw
A: 

You could encapsulate each process in a C++ object and keep a list of them in global scope. The destructors can shut down each process. That will work fine if the program exits normally but it it crashes, all bets are off.

Here is a rough example:

class myprocess
{
public:
    myprocess(HANDLE hProcess)
        : _hProcess(hProcess)
    { }

    ~myprocess()
    {
        TerminateProcess(_hProcess, 0);
    }

private:
    HANDLE _hProcess;
};

std::list<myprocess> allprocesses;

Then whenever you launch one, call allprocessess.push_back(hProcess);

Adam Pierce
>> if my main processes crashes
jm
A: 

Just off the top of my head:

  • Have you considered using threads instead of processes?
  • Try passing the handle of the main thread/process to the child processes and get them to wait on that handle. This works for threads, as waiting on a thread handle waits until that thread completes and exits. Not too sure if it'll work for processes, should check out MSDN to verify this.
Daemin
+1  A: 

Windows Job Objects sounds like a good place to start. The name of the Job Object would have to be well-known, or passed to the children (or inherit the handle). The children would need to be notice when the parent dies, either through a failed IPC "heartbeat" or just WFMO/WFSO on the parent's process handle. At that point any child process could TermianteJobObject to bring down the whole group.

Adam Mitz