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
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
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:
In my view, passing
INVALID_HANDLE_VALUE is totally confusing; replacing it with some flags like
(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 ProcessManager.exe creates a number of child processes (100 by default) in suspended state, registers wait callbacks on each one, and then let 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:
hWait is not a “real” handle: you cannot pass it to
CloseHandle() and the like. The only thing you can do with it is
That’s all, folks!