While working on proper logoff/shutdown processing, I'm running into an interesting race condition inside kernel32. The shutdown command (apps/utils/shutdown) calls ExitWindowsEx() and then terminates, which causes kernel32's DllMain() function to be called with the DLL_PROCESS_DETACH reason. This will clean up kernel32 resources, like calling RtlDeleteCriticalSection(&ConsoleLock).
Now, before the process is completely shutdown, CSRSS will send it a CTRL_LOGOFF_EVENT. To deliver this event, CSRSS calls CreateRemoteThread(). So CreateRemoteThread() is called while the main thread is executing DLL_PROCESS_DETACH cleanup. During handling of CTRL_LOGOFF_EVENT, the new thread will try to enter the ConsoleLock critical section, which was already deleted by the main thread. Chaos results.
To solve this, first I was looking for a way to disable new thread creation when a process enters ExitProcess(), but that won't solve the problem. We can still have a thread A which calls ExitProcess(), have a context switch to existing thread B of the same process which does something that needs the ConsoleLock. Then I considered SuspendThread()ing all other threads of the process when ExitProcess() is called, but that could potentially lead to deadlocks if the suspended threads hold synchronization objects. So now I'm inclined to believe that the solution is to just not do the resource cleanup during DLL_PROCESS_DETACH handling. But that doesn't feel very clean either. Anyone have a better idea?
GvG