Thread deadlocks on destructor

I have this dll that gets loaded.  We can call it DLL A.  DLL A loads another DLL called DLL B.  DLL B upon construction creates a thread that does some stuff.  At some point DLL A hits the destructor, which calls FreeLibrary on DLL B.  So inside the destructor for DLL B it does some cleanup, including a line that is givng me a problem found by AppVerifier.

WaitForSingleObject(thread, amount of time);

I am waiting for the thread I constructed in B to finish processing before closing the handle to it.

Applcation Verifier says this could be causing a deadlock.  

"Probable cause
This stop is generated when the current thread is currently running code inside the DllMain function of one of the DLLs loaded in the current process and it calls WaitForSingleObject or WaitForMultipleObjects to wait on a thread handle in the same process.

This will most likely lead to a deadlock because the thread handle will not get signaled unless that second thread is exiting. When the second thread calls ExitThread it will try to acquire the DLL loader lock then call DllMain (DLL_THREAD_DETACH) for all DLLs in the current process. But the loader lock is owned by the first thread (the one that is waiting on the thread handle) so the two threads will deadlock.
"
This is just a probable cause, and I don't even understand it.  Does there seem to be anything wrong with my logic?
cophiAsked:
Who is Participating?
 
waysideConnect With a Mentor Commented:
If you don't need to get the DLL_THREAD_ATTACH and DLL_THREAD_DETACH messages in your DllMain (which it appears you don't, since you don't do anything with them) you could use the DisableThreadLibraryCalls()  function to disable the notifications.

This would prevent DllMain from ever being called with DLL_THREAD_ATTACH or DLL_THREAD_DETACH, and should prevent any possible deadlock, even if the AppVerifier still complains about one.
   
0
 
Mikeh926Commented:
Your code logic is fine, the problem is that you are trying to do some if it from within DllMain().

DllMain() is called when the DLL is loaded and unloaded, and when threads are created and destroyed.

What the AppVerifier is trying to tell you is that there is a global mutex around all calls to DllMain. This means that only one thread can be executing code inside DLLMain at any one time. When your main thread calls FreeLibrary on DLL B, it will call DllMain in DLL B with a thread/process detatch. This will grab the mutex, preventing anyone else from getting it. I assume this is where you are doing your cleanup. Inside that DllMain you are signaling your other thread to close and then waiting for it to close. The problem is that when you other thread closes, it will attempt to call DllMain with a thread detach message, but it can't because your first thread is still in the function and has the mutex. Both threads end up wait for each other, the first one waiting for the second thread to quit, and the second thread waiting to get the mutex to call DLLMain, thus the deadlock.

To fix this problem, try and avoid putting any code in DllMain(). Instead create Initialise() and Close() functions (or whatever you want to name them) and call them directly after/before loading/freeing your Dll.

Regards,
Mike.
0
 
waysideCommented:
Are you doing all of this from within the DllMain's of your dll's?

This is in general a Bad Idea. See the documentation for DllMain for a list of problems that can be caused by doing real work inside of DllMain. It's much better to instead create initialization and termination functions, and do the heavy lifting there.
0
Never miss a deadline with monday.com

The revolutionary project management tool is here!   Plan visually with a single glance and make sure your projects get done.

 
cophiAuthor Commented:
This is all I have inside of dll Main in DLL B.

    switch (ul_reason_for_call)
      {
            case DLL_PROCESS_ATTACH:
            case DLL_THREAD_ATTACH:
            case DLL_THREAD_DETACH:
            case DLL_PROCESS_DETACH:
                  break;
    }
    return TRUE;

In DLL A I have

   switch (ul_reason_for_call)
   {
      case DLL_PROCESS_ATTACH:
      {
         InitializeCriticalSection( &prog );
         break;
      }
      case DLL_THREAD_ATTACH:
      case DLL_THREAD_DETACH:
      {
         break;
      }
      case DLL_PROCESS_DETACH:
      {
         DeleteCriticalSection( &prog );
         break;
      }
   }
   
   return TRUE;
0
 
Mikeh926Commented:
Well, looking at your code it sounds like AppVerifier is incorrect. If your code works fine and does not deadlock then I would just ignore it. After all it is only saying that it _could_ cause a problem, not that it will or won't. In reality it doesn't really know if it will or not, it's just alerting you to something that might be a problem.
 
0
 
waysideCommented:
> So inside the destructor for DLL B it does some cleanup, including a line that is givng me a problem found by
> AppVerifier.
>
> WaitForSingleObject(thread, amount of time);

Can you show the code around this call that AppVerifier flagged? It doesn't make sense that it would flag a call that doesn't exist. It also isn't clear to me what you mean by "inside the destructor for DLL B", a DLL is not a class, it doesn't have a destructor.
0
 
cophiAuthor Commented:
wayside, you are right, dll b is instantiates a class, that is desctructed later on.  The class instantiates the thread and it trys to close it in the destructor

there is an enter critcial section
do some stuff
leave critical section
SetEvent(threadEvent);
WaitForSingleObject(thread, amount of time);
CloseHandle(thread);
0
All Courses

From novice to tech pro — start learning today.