Click here to Skip to main content
Click here to Skip to main content
Technical Blog

Tagged as

Windows: Getting callback on child process exit in C++

, 7 Aug 2014 Apache
Rate this:
Please Sign up or sign in to vote.
In my current capacity I sometimes descend into the virtually forgotten depths of C++ desktop programming. One interesting problem I needed to solve is how to get notified about a process exit. My program can create potentially unlimited number of child processes, and I need to know when they die. I

In my current capacity I sometimes descend into the virtually forgotten depths of C++ desktop programming. One interesting problem I needed to solve is how to get notified about a process exit. My program can create potentially unlimited number of child processes, and I need to know when they die. In the .NET world I would use Process.Exited event, but it has no equivalent in Win32.

Short answer to this puzzle is: use RegisterWaitForSingleObject() function.

Download sample application (47K zip file)

Here are more details.

When a Win32 process exits, its handle becomes signaled. We could just wait on it using WaitForSingleObject(), but this would block the executing thread. This is not what I need: I need an asynchronous callback.

A naive approach would be to create a dedicated waiting thread and call WaitForSingleObject() there. This works, but it is adequate only when we deal with just a few processes. Creating threads is expensive and slow.

The next optimization would be to wait for multiple processes in a single thread using WaitForMultipleObjects(). This works too, but WaitForSingleObjects() can wait only on up to 64 handles at a time. If the program creates more than 64 processes, this approach will break.

The next logical step is to create a pool of waiting threads, each of which waits for up to 64 processes. Fortunately, Win32 API already has implementation of such a pool of wait threads, and it can be accessed via RegisterWaitForSingleObject() function. You give at a handle to wait and a callback pointer, and the rest is taken care of by Win32 API.

When you no longer need the callback, you remove it by calling UnregisterWait() or UnregisterWaitEx().

However, as usual in C++, some things should be handled with care. First, the documentation says that if the object you wait on stays signaled forever, you need to specify WT_EXECUTEONLYONCE flag. They are not kidding. Without this flag I received two calls of the callback for each process exit.

Also, one must be careful not to create deadlocks and race conditions. If your wait callback uses some shared resources such as pointers to class instances, etc., you must ensure that they are not destroyed before the callback is called. This is harder than it sounds, because the callback may be called on random thread at any time. But we are in luck: function UnregisterWaitEx has a mode in which it waits for any pending callbacks:

UnregisterWaitEx(hCallback, INVALID_HANDLE_VALUE);

In my view passing INVALID_HANDLE_VALUE is totally confusing; replacing it with some flags like WT_WAITFORCALLBACKS (which does not really exist) would be more elegant. Anyway, confusing or not, it does do the job of ensuring that all callbacks are finished before you clean up the resources.

Sample Application

Sample application ProcessManager.exe creates a number of child processes (100 by default) in suspended state, registers wait callbacks on each one, and then lets them run. You can stop it at any time by pressing ENTER.

I encapsulate child processes in a class creatively called Process that takes care of starting a process, registering a wait callback, and closing all handles in the destructor.

Each child process executes is a very simple application (TestApp.exe) that sleeps for a random number of milliseconds and then exits. Exiting of a child process triggers a callback in the process manager, that prints a message to the standard output. Writing to the standard output is not thread safe, so I use a critical section named coutAccess to make sure the messages are not garbled.

The code line that installs the exit callback is:

RegisterWaitForSingleObject(&hWait, hProcess, OnExited, this, INFINITE, WT_EXECUTEONLYONCE);

The code line that removes the callback is:

::UnregisterWaitEx(hWait, INVALID_HANDLE_VALUE);

Note, that hWait is not a “real” handle: you cannot pass it to CloseHandle() and the like. The only thing you can do with it is UnregisterWait() or UnregisterWaitEx().

That’s all, folks!

License

This article, along with any associated source code and files, is licensed under The Apache License, Version 2.0

Share

About the Author

Ivan Krivyakov
Technical Lead Thomson Reuters
United States United States
Ivan is a hands-on software architect/technical lead working for Thomson Reuters in the New York City area. At present I am mostly building complex multi-threaded WPF application for the financial sector, but I am also interested in cloud computing, web development, mobile development, etc.
 
Please visit my web site: www.ikriv.com.

Comments and Discussions

 
GeneralWorth using this PinmemberVijay_vijay17-Aug-14 23:44 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.141220.1 | Last Updated 7 Aug 2014
Article Copyright 2014 by Ivan Krivyakov
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid