Skip to content

Instantly share code, notes, and snippets.

@jstimpfle
Last active November 30, 2022 13:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jstimpfle/bd0bd81b1aaa900689cbd414f189d475 to your computer and use it in GitHub Desktop.
Save jstimpfle/bd0bd81b1aaa900689cbd414f189d475 to your computer and use it in GitHub Desktop.
win32_proc_wait.cpp - Get notified when a Windows process terminates
/* win32_proc_wait.cpp - Get notified when a Windows process terminates */
#include <Windows.h>
typedef void Proc_Terminated_Callback(void *data);
/* Proc_Wait - Waiting for a process to finish on Win32.
Zero out a Proc_Wait structure before use.
Call proc_wait_open() to register an asynchronous wait operation for a process handle.
The registration was successful iff "true" was returned. An unsuccessful registration has no effect
and you can attempt another registration later without having to zero out the struct.
A successfully registered wait operation must later be closed using proc_wait_close().
It is ok (there will be no effect) if you call proc_wait_close() on a zeroed Proc_Wait
structure, or after a registration failed, or after the wait operation was already closed before.
If you close a process handle before closing the wait operation, behaviour is undefined.
*/
struct Proc_Wait
{
HANDLE hWait;
Proc_Terminated_Callback *callback;
void *data;
};
void CALLBACK proc_wait_cb(PVOID data, BOOLEAN wastimer)
{
assert(!wastimer);
Proc_Wait *pw = (Proc_Wait *) data;
pw->callback(pw->data);
}
bool proc_wait_open(Proc_Wait *pw, HANDLE hProc, Proc_Terminated_Callback callback, void *data)
{
assert(!pw->hWait);
HANDLE hWait;
if (! RegisterWaitForSingleObject(
&hWait,
hProc,
&proc_wait_cb,
pw,
INFINITE,
WT_EXECUTEONLYONCE))
{
return false;
}
pw->hWait = hWait;
pw->callback = callback;
pw->data = data;
return true;
}
void proc_wait_close(Proc_Wait *pw)
{
// Support calling this function multiple times, or on a zeroed Proc_Wait struct.
if (!pw->hWait)
return;
/* We're using UnregisterWaitEx() with INVALID_HANDLE_VALUE here to indicate
"no completion queue". Compared to this function, the normal
UnregisterWait() wouldn't be of good use for us: If the wait callback is
running while we're trying unregister it, then UnregisterWait() will return
immediately, without offering a wait to synchronize with the thread where
the callback is running - a race condition that would require us to add a
different means of synchronization. Instead, UnregisterWaitEx() will always
wait for the return of any callbacks that had already been started at the
time of the unregister request. */
if (!UnregisterWaitEx(pw->hWait, INVALID_HANDLE_VALUE))
assert(false); // this operation should never fail
pw->hWait = NULL;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment