If you need RegisterWaitForSingleObject-like behavior in the new Vista threadpool -- a great feature, by the way, that amortizes the cost of losing a thread to waiting by cramming up to 63 waits into a single threadpool thread, leading to better scalability on systems that must wait for a lot of things concurrently, sometimes for long or unpredictable times (and the magic behind ASP.NET Asynchronous Pages) -- you'll want to look at the SetThreadpoolWait and related APIs. You get basically the same functionality, with a mostly cleaned up interface and the added benefit of having cleanup groups to take care of resource cleanup (if you so choose):
VOID WINAPI SetThreadpoolWait(PTP_WAIT pwa, HANDLE h, PFILETIME pftTimeout);
One thing annoyed me about this API when I first tried to use it. To be truthful, it still does. Instead of a DWORD timeout specified in 1ms intervals, you'll notice you have to supply a pointer to a FILETIME data structure. Wha-wha-whaat? This differs from just about every other wait API on Windows and is not as simple as it sounds.
The no-timeout case, which is typically -1 (a.k.a. INFINITE), is simple enough: just pass a NULL pointer. The "check, but immediately timeout if unsignaled without waiting," variant, typically specified with 0, is pretty straightforward. Just initialize your FILETIME to contain 0's. There are many ways to do this but using a struct field initializer is probably the easiest:
FILETIME ft = {0,0};
SetThreadpoolWait(..., &ft);
But for real timeouts, how the heck do you get your hands on a properly initialized FILETIME? OK, here's where things get nasty. You can grab ahold of a FILETIME from many places, including from an actual file's creation or modification time, but you probably don't want to do that. GetSystemTimeAsFileTime(&ft) will turn the current time into a FILETIME, which is useless without additional math to turn it into a time relative to now (otherwise, you'd just use a {0,0} FILETIME as noted above). Alternatively, you could start with a SYSTEMTIME (the current can be retrieved with GetSystemTime), which allows you to define a precise year, month, day, hour, minute, second, and/or millisecond, and then turn that into a FILETIME with SystemTimeToFileTime(..., &ft). But how many times do you really have a precise absolute date and time at which you want your wait to stop? Yes, February 3rd, 2019, at 6:29 AM (and 39.66 seconds) please.
Whew! At this point, you're probably bewildered. Most wait timeouts are defined in terms of relative time. Thankfully, you can still define a relative time when calling SetThreadpoolWait. But sadly you have to resort to some messy casting and data conversion to do this. How do you do that? Simple. If the FILETIME's contents represent a negative integer, then the threadpool adds the negation of that to the current system time during registration to come up with an absolute time. So it does the GetSystemTimeAsFileTime/addition nonsense for you. Well, how the heck do you do create a negative FILETIME anyway? "Easy"!
__declspec(align(8)) FILETIME ft;
*reinterpret_cast<LONG64 *>(&ft) = -1000;
SetThreadpoolWait(..., &ft);
That causes the threadpool to use a timeout of 1000 from the current time. 1000 units of what you might ask? FILETIME represents its time with 100ns units, so this particular timeout of -1000 is interpreted to mean 100 microseconds from now. Generally, if you have n milliseconds, you'd calculate n * 1000 (to get microsecond units) * 10 (to get 100 nanosecond units).
[Update: 12/26/2006: I changed the examples to use __declspec(align(8)), ensuring that the FILETIME is aligned on an 8-byte boundary. Pavel noted in comments to this post that, since FILETIME consists of two DWORDs, VC++ will align on 4-byte boundaries. On some architectures -- like IA64 -- treating these as a single 64-bit aggregate piece of data will present alignment faults to the software. (This is unlike the behavior you'll see on X86 and X64, where such faults are fixed transparently by the hardware and/or OS, albeit at a performance hit. You can ask the OS to fix these up on IA64 automatically, with SetErrorMode(SEM_NOALIGNMENTFAULTEXCEPT), but this results in even worse performance than on other architectures.) Raymond wrote about this previously. One solution is to align manually, as shown here; another is to memcpy data to and from the FILETIME/LONG64; yet another, which is probably the cleanest, is to simply access the FILETIME's fields directly. E.g. FILETIME ft = { -m * 1000 * 10, ~0 } or ft.dwLowDateTime = -m * 1000 * 10; ft.dwHighDateTime = ~0, for some milliseconds timeout m.]
To save you some time and frustration in this process, you can just use the following little shim in your code. I do. It gives you a nice, relative DWORD-based timeout interface to the SetThreadpoolWait API:
VOID SetThreadpoolWaitWithMsTimeout(PTP_WAIT pwa, HANDLE h, DWORD dwMilliseconds) {
__declspec(align(8)) FILETIME ft;
PFILETIME pft;
if (dwMilliseconds == -1) {
// Just pass NULL to the wait API to signal "no timeout".
pft = NULL;
} else {
pft = &ft;
// FILETIME uses 100ns intervals. To convert milliseconds into 100ns units,
// we must multiply by 1000 (to get microseconds) and then by 10 (to get 100ns).
// We take the negative of this number to specify "relative to now" time.
*reinterpret_cast<LONG64 *>(pft) = -dwMilliseconds * 1000 * 10;
}
SetThreadpoolWait(pwa, h, pft);
}
To be honest, I don't know the reasoning behind the break from tradition here. [Update: 12/26/2006: Richard pointed out, very correctly, that the NTDLL implementations have alwasy dealt in terms of FILETIMEs and performed DWORD ms to 100ns conversions; in that sense, this is hardly a "break from tradition" for Windows generally, but is certainly a new direction for KERNEL32 itself.] Using a FILETIME clearly causes some trickiness for something that should be damn simple, but it does have one distinct advantage. The DWORD-based APIs only permit you to specify relative time, whereas the FILETIME approach allows you to specify relative or absolute, depending on what you need. I have to admit that I can’t think of many cases with wait registrations where you’d want an absolute timeout, but I might be unimaginative. This also has the benefit of removing some degree of ambiguity: with relative, you always have to wonder: time relative to what? Is it relative to the time at which an actual wait thread sees the registration and adds the HANDLE to its list of waitable objects? Or is it relative to the time at which the call to register the wait is made? Or something else? In the end, I’d have preferred a separate API for absolute time… I just like my DWORD-based relative timeouts. They are familiar and comfortable. Continuing to use RegisterWaitForSingleObject is an option, but doesn't let you use environments, cleanup groups, etc. Tradeoffs abound.