Solved

_endthread in DLL Process Detach

Posted on 1997-10-30
14
945 Views
Last Modified: 2013-12-03
I have a  NT 32 bit DLL that creates a helper thread in the PROCESS_ATTACH using _beginthread().  When I load the DLL through a VB app, everything works fine until I end the app.  That's when I get an ACCVIO out of VB.  I think the problem is that I try to end the thread out of the PROCESS_DETACH.  I set an event, which I WaitOnSingleObject() for in the ThreadProc.  I think that the problem has something to do with NT wanting to serialize thread activity with the DLLMain callls in my DLL.

Anyway, my question is:
How can I end this thread without ACCVIO'ing given that  I don't know to end the thread until I get the PROCESS_DETACH.
0
Comment
Question by:rcc
  • 6
  • 6
  • 2
14 Comments
 
LVL 22

Expert Comment

by:nietod
ID: 1408080
In Win 32 (NT or 95) only one thread can be attaching (or detaching to a DLL) at a time.  If you create a new thread during attachment the new thread wont't run until the first thread is done attaching because the new thread needs to attach to.  If the first thread waits for the new thread to run before finishing attaching and the new thread waits for the first to finish attacking before it runs, you're going to have a long wait.

Opps, I didn't read your question quite clearly enough, but the same problem occurs durring termination.  The first thread waits and doesn't terminate until a different thread terminates, but that thread doesn't terminate because the first one is waiting in the detach process.

As for a solution, I'd need more information about the nature of what you are doing.  For example, why does the first thread need to wait after it terminates the other?
0
 
LVL 2

Expert Comment

by:anichini
ID: 1408081
Try calling DiableThreadLibraryCalls for your DLL before setting the event to kill the second thread. This will cause the second thread to NOT try and call your DLLMAIN with a THREAD_DETACH message.


0
 

Author Comment

by:rcc
ID: 1408082
There is no reason why the 2nd thread should wait.  I would just like it to end.  If I return from the ThreadProc(), I get an ACCVIO with a call stack like this:

RtlpWaitForCriticalSection@4 + 149 bytes
RtlEnterCriticalSection@4 + 67 bytes
_lock(int 0x00000009) line 247
_nh_malloc_dbg(unsigned int 0x00000074, int 0x00000000, int 0x00000002, char * 0x1024bc54, int 0x000000e7) line 238 + 7 bytes
_malloc_dbg(unsigned int 0x00000074, int 0x00000002, char * 0x1024bc54, int 0x000000e7) line 163 + 27 bytes
_calloc_dbg(unsigned int 0x00000001, unsigned int 0x00000074, int 0x00000002, char * 0x1024bc54, int 0x000000e7) line 493 + 21 bytes
_getptd() line 232 + 21 bytes
_endthread() line 220 + 5 bytes
_threadstart(void * 0x02b40c10) line 176
BaseThreadStart@8 + 81 bytes

I'm not sure what is wrong.  Which critical section is it waiting for? and Why the ACCVIO?  Like I said, I SetEvent in the PROCESS_DETACH, which triggers the ThreadProc to return.  This ought to cause my thread to exit gracefully, but it doesn't.
0
 
LVL 22

Expert Comment

by:nietod
ID: 1408083
It might be helpful to see some code if you can cut out the unimportant parts.
0
 
LVL 2

Accepted Solution

by:
anichini earned 100 total points
ID: 1408084
The critical section it is waiting for is in the standard runtime library. Microsoft uses critical sections to synchronize access to various parts of the runtime library in the multithreaded version.

Perhaps the runtime library has already been freed up at the point the second thread gets to this call (perhaps it was freed as a result of the PROCESS_DETACH). Actually, I could see a situation where the first thread has the PROCESS_DETACH called, and you call SetEvent(). Before the second thread exits, the first thread continues to run, returning from your DllMain into the runtime library's DLL main, which in response to the process detach message probably frees up its resources. Then you switch to the second thread, which because of the set event goes on its merry wait to _endthread(), but because the runtime library's state was freed, it gets an access violation.

A possible solution would be to have the first thread wait for the second thread to die. A way to do this is to do a WaitForSingleObject on the thread's handle.

0
 
LVL 22

Expert Comment

by:nietod
ID: 1408085
The answer sounds reasonable, but I would think that the multi-thread version of the run-time libraries would not allow this kind of problem to occur.  That is, when the 1st thread terminates it should release resources for that thread only, not for all threads.  (In this respect it should not treat the primary (original) thread differently that the secondary threads.)  Are you linking to the multi-threaded library?
0
 

Author Comment

by:rcc
ID: 1408086
Yes, I'm linking to the multi-thread library.

There is one problem with your solution.  Since this DLL is attached to an OCX which could be loaded in VB, I don't really know when the program is ending.  In other words, the PROCESS_DETACH is my indication that thread #1 is ending; I can't call WaitForSingleObject(hthread) in the PROCESS_DETACH.  I can call TerminateThread, but the NT docs are a little unclear of any problems that I might cause; it says that the thread won't clean up it's stack.  I ran some test code, and it appears that memory does get cleaned up, but is that true?  Is TerminateThread safe?
0
6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

 
LVL 22

Expert Comment

by:nietod
ID: 1408087
To the best of my knowledge TerminateThread should be avoided.

  It does not allow the thread to clean up in many ways.  Resources used by the thread, like open files, memory used by the operating system, memory used by the run-time library, etc. may not be freed.  These resources will usually be freed when the entire process terminates, but not always.  For example if a library creates a GDI object (a brush say) when it attaches.  The brush will never be freed if you use TerminateThread.
0
 
LVL 2

Expert Comment

by:anichini
ID: 1408088
I guess I wasn't clear on what I meant. Here's what I meant:

// global
HANDLE g_hEvent;
HANDLE g_hThread2; // handle to second thread

// inside your dll main
{
// ...
case PROCESS_DETACH:
// assume g_hEvent was created earlier
SetEvent(g_hEvent); // tell thread2 to die
WaitForSingleObject(g_hThread2); // wait for thread2 to die
break; // break out of dll main
// ...
}

// your second thread2 should periodically check the status of g_hEvent or (preferably) use one of th WaitForXXX functions. When g_hEvent is signaled your second thread should exit.


0
 
LVL 22

Expert Comment

by:nietod
ID: 1408089
Nope!  This should cause a deadlock.

Inside the detach code you do this:

SetEvent(g_hEvent); // tell thread2 to die
WaitForSingleObject(g_hThread2); // wait for thread2 to die

But thread2 cannot die until thread 1 returns from the detach notification.  Why?  because only one thread can be in the attach/detach notification at a time.  Thread 1 will wait for thread 2 finish terminating before it leaves the detack notification, but thread 2 won't even start to terminate until thread 1 leaves the detach notification code.  That is a deadlock.
0
 
LVL 2

Expert Comment

by:anichini
ID: 1408090
it won't deadlock if you call DiableThreadLibraryCalls. The deadlock occurs with the synchronization that Win32 puts around DllMain, but if the second thread never calls DllMain there will be no deadlock.


0
 
LVL 22

Expert Comment

by:nietod
ID: 1408091
DisableThreadLibraryCalls().  Hadn't seen that before.  That should prevent the deadlock, if it is safe to use.  Some librarys will require thread detach notification.  


0
 
LVL 2

Expert Comment

by:anichini
ID: 1408092
But DisableThreadLibraryCalls() has you specify which DLL you want to disable calls on, so he can just disable it for his DLL.

0
 
LVL 2

Expert Comment

by:anichini
ID: 1408093
Wait a second - I found a better answer in a back issue of MSJ.

The solution is too lengthy for me to post here, but if rcc doesn't have access to MSDN (which has this back issue), I can post the code.

The person asking the question has a problem similar to yours, although they were using ISAPI. The solution is to not kill the threads off in your DLL_PROCESS_DETACH message. What you do is increment your DLL's reference count in your DLL_PROCESS_ATTACH. This causes your DLL not to be unloaded when VB calls FreeLibrary via OLE, and thus it doesn't get the DLL_PROCESS_DETACH. When the worker thread exits, you call FreeLibraryAndExitThread (in rcc's case, he'll want to call FreeLibrary() on his own DLL and then call _endthread()).

But you must be asking: when does the worker thread terminate? The answer is a small stub DLL you create which forwards all the exports from your real DLL. The stub DLL's only purpose is to call an exported function from your real DLL called ShutdownDLL, which signals the threads telling them to exit.

For the source code and a better explanation, look at MSJ Volume 11, September 1996 "Win32 Q&A", look at the second question.


0

Featured Post

Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

Join & Write a Comment

Suggested Solutions

This article describes how to add a user-defined command button to the Windows 7 Explorer toolbar.  In the previous article (http://www.experts-exchange.com/A_2172.html), we saw how to put the Delete button back there where it belongs.  "Delete" is …
zlib is a free compression library (a DLL) on which the popular gzip utility is built.  In this article, we'll see how to use the zlib functions to compress and decompress data in memory; that is, without needing to use a temporary file.  We'll be c…
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…
In this tutorial you'll learn about bandwidth monitoring with flows and packet sniffing with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're interested in additional methods for monitoring bandwidt…

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now