WaitForSingleObject for thread object does not work when unloading DLL

I ran into unexpected behavior of the Windows threading engine when unloading a DLL. I have a batch of worker threads and I am trying to terminate them when the DLL is unloaded (via DllMain DLL_PROCESS_DETACH). The code is very simple (I am sending an event to end the thread's wait loop):

WaitForSingleObject( ThrHandle, INFINITE );
CloseHandle( ThrHandle );

      

Yet WaitForSingleObject has the hang of it all. It works great if I execute it before the DLL is unloaded. How can this be eliminated?

0


source to share


1 answer


You cannot wait for the thread to exit in DllMain (). If the thread has not already exited by the time DLL_PROCESS_DETACH was received, so there will always be a dead end. This is expected behavior.

The reason for this is that DllMain () calls are serialized through loader locks. When ExitThread () is called, it requests a bootloader lock so that it can call DllMain () using DLL_THREAD_DETACH. Until this call completes, the thread is still running.

So DllMain is waiting for the thread to exit, and the thread is waiting for DllMain to exit, a classic deadlock situation.

See also MSDN Guidelines for Using the Dynamic Link Library .

The solution is to add a new function to your DLL for the application to call before unloading the DLL. As you noted, your code already works fine when explicitly called.




In the case where backward compatibility requirements make it impossible to add such a function, and if you must have worker threads, consider splitting your DLL into two parts, one dynamically loading the other. The dynamically loaded part will contain (at a minimum) all the code needed for the worker threads.

When the DLL, loaded by the application itself, receives DLL_PROCESS_DETACH, you simply set an event to signal the threads exit and then return immediately. One of the threads should be assigned to wait for all the others, and then free the second DLL, which you can do safely using FreeLibraryAndExitThread () .

(Depending on the circumstances, and in particular if worker threads come out and / or new ones created as part of regular operations, you may need to be very careful to avoid race conditions and / or deadlocks; easier if you used a thread pool and callbacks instead of manually creating worker threads.)




In a special case where threads don't need to use any but most basic Windows APIs, it might be possible to use thread pool and work with callbacks to avoid the need for a second DLL.After the calls have exited you can check with WaitForThreadpoolWorkCallbacks () , you can unload the library - you don't need to wait for the threads themselves to exit.

The catch here is that callbacks should avoid any Windows APIs that might take a bootloader lock. It is not documented which API calls are safe in this regard, and it depends on different Windows versions. If you are calling something more complex than SetEvent or WriteFile, say, or if you are using a library and not native Windows API functions, you should not use this approach.

+7


source







All Articles