Way back in Windows XP, MS introduced a new synchronization primitive called “keyed events”. These were exposed through the undocumented native system calls NtCreateKeyedEvent, NtOpenKeyedEvent, NtReleaseKeyedEvent, and NtWaitForKeyedEvent. Keyed events were then used by MS to implement (or reimplement) higher level win32 synchronization primtives.
When I dropped support for Win2K some time back, I switched the implementation of some of my own portable synchronization primitives to use keyed events on Windows. This eliminated some potential scalability issues and generally simplified things. It allows sleeping and later waking a thread with a total of 2 kernel mode transitions, nice.
Well, things are rarely simple. I am now seeing deadlocks at process exit in one of my projects. The issue is that NtReleaseKeyedEvent gets stuck in the kernel if the thread that called NtWaitForKeyedEvent was terminated. A quick internet search turns up this hit:
It seems that the keyed event implementation was not well tested in combination with thread termination.
Many win32 gurus will point out that you should not terminate threads, so this issue is the developers fault and not a fault of the keyed event implementation. That conclusion is naive. Terminating threads needs to be dealt with in Windows at least for two reasons:
1) The win32 ExitProcess implementation implicitly terminates threads. When building simple single binary applications it is trivial to make sure your threads are finished before TerminateProcess is called by the runtime, but when building DLLs for use by 3rd party client applications or modules, things are not as simple.
2) Some Windows facilities (eg. ReadFile() and WriteFile() to console handles) have a tendency to get stuck and prevent process exit, causing problems even in relatively simple applications.
For better or worse, thread termination is something that at least needs to be dealt with at process exit time, a time when the resource leak issues associated with thread termination are irrelevent.
(Insert tirade here about loader-lock, DLL TLS issues, user-mode win32 “handles”, TerminateProcess implementation, and about Microsofts inability to fix broken designs.)
The Windows CRITICAL_SECTION and SRW implementations uses keyed events and are supposed to handle thread termination, so there are work-arounds. But, if you look at how keyed events work, the hang is not surprising and shows the keyed events themselves are probably working as intended. My solution is to drop keyed events and go back to using a pool of notification events.