mardi 30 août 2016

Cleaning up threads in a DLL: _endthreadex() vs terminatethread()

Because of the restrictions on DllMain (and I understood that the same applies to global&static object constructors&destructors in a DLL), such a simple thing as a singleton logger with asynchronous file writing/flushing thread becomes too tricky. The singleton logger is in a DLL, and I have limited influence on the executable loading and unloading this DLL time to time. I can force that executable to call my DLL initialization function before any use, so in the initialization function I can use a critical section to guard a variable telling whether the DLL has been already initialized or it needs init this time. This way initialization from DllMain is avoided, which would lead to a deadlock because I need to launch threads from initialization, and threads call DllMain with DLL_THREAD_ATTACH reason, and that obtains the same loader lock as the one already obtained when we are initializing in DllMain on DLL_PROCESS_ATTACH event.

C++11 thread cannot be used because of this bug (not fixed in MSVC++2013). So I'm using _beginthreadex(), because CreateThread documentation says:

A thread in an executable that calls the C run-time library (CRT) should use the _beginthreadex and _endthreadex functions for thread management rather than CreateThread and ExitThread; this requires the use of the multithreaded version of the CRT. If a thread created using CreateThread calls the CRT, the CRT may terminate the process in low-memory conditions.

But I have no control over the executable to ensure that some deinitialization function from the DLL is called before unloading the DLL. So the only options for cleanup are DllMain's DLL_PROCESS_DETACH and destructors of global/static variables. The problem is that they are called with loader lock obtained, so I cannot make there the DLL threads to exit gracefully because those threads upon a normal exit would attempt to call DllMain with DLL_THREAD_DETACH, which would result in a deadlock (loader lock again). MSDN suggests to use TerminateThread() to handle this:

DLL A gets a DLL_PROCESS_DETACH message in its DllMain and sets an event for thread T, signaling it to exit. Thread T finishes its current task, brings itself to a consistent state, signals DLL A, and waits infinitely. Note that the consistency-checking routines should follow the same restrictions as DllMain to avoid deadlocking. DLL A terminates T, knowing that it is in a consistent state.

So I'm afraid of using _beginthreadex() + TerminateThread() pair, instead of the designed _endthreadex() (the latter would be called by the thread itself if the thread returned normally).

tl;dr Consider a thread which returns from its entry function vs. a thread which does something like Sleep(INFINITE) in the end of its function waiting to get terminated (i.e. after it has got the resources consistent and signalled to the terminating thread that it's ready). Do some CRT or C++11 resources (like thread_local) etc. leak or get corrupted etc. if _endthreadex() is not called, but TerminatThread() is called instead?

Aucun commentaire:

Enregistrer un commentaire