메뉴 건너뛰기

넷서버 사용 팁/강좌

사용팁 DLL과 Deadlock

admin 2008.01.20 06:52 조회 수 : 6278

스레드에서 DLL을 사용할 경우 Deadlock이 발생할 가능성이 높다는 것은  여러 디버깅책에서 나왔습니다.
하나의 해결책을 제시한 ng 아티클입니다.

No, the dll does not pass or export strings so sharemem should not be
>necessary.
>
>In experimenting with this problem, I've stripped it down to its simplest
>components: a thread unit that defines a thread that just calls messagebeep
>every two seconds, a main unit that creates and frees the thread in its
>initialization and finalization sections and defines a procedure that starts
>the thread, a dll project that exports the main units procedure, and a
>simple app that loads the dll dynamically and hooks the dll's procedure to a
>button event so that the thread can be started.

I believe your basic problem is that you're running into this Windows
restriction (from the documentation of ExitProcess and ExitThread):

  Only one thread in a process can be in a DLL initialization or detach
  routine at a time.

The unit finalizations within the DLL take place during the handling of a
DLL_PROCESS_DETACH in the context of the main thread. The finalization tells
the thread to quit and waits for it. The thread's state, however, will not
become signaled until all DLL thread detach routines have been executed in the
context of the terminating thread. However, (because of the above restriction)
since the main thread is still in its process detach routine, the terminating
thread has to wait for it to return before it can execute its detach routines.
Result: deadlock.

By the way, I was able to produce the deadlock with an even more minimal set up
than you used. I had a console app consisting of LoadLibrary/FreeLibrary and a
DLL which used BeginThread and this thread proc:

var Finished: boolean = false;

function ThreadProc(P: pointer): integer;
begin
  repeat
    Sleep(1000);
  until Finished;
  Result:= 0;
end;

{... to the DllProc}
  DLL_PROCESS_DETACH:
    Finished:= true;
    if ThreadH <> 0 then
      WaitforSingleObject(ThreadH, INFINITE);
{...}



>
>If I start the thread and then close the app and use the series of
>statements outlined in my initial post to kill the
>thread, the calling app will hang in the waitfor function of classes.pas and
>never shutdown.
>If I use Peter's suggestion and set FreeOnTerminate
>the app will close properly, but this solution does not work with my "real"
>project. It cause AV's. It also does not work with an experimental case
>where multiple threads are created. Again AV's result. This leads me to
>believe that there is a timing problem.

Using FreeonTerminate allows the DLL_PROCESS_DETACH routine to complete, but it
then proceeds to call the routine with DLL_THREAD_DETACH. Having
DLL_PROCESS_DETACH run before DLL_THREAD_DETACH is no doubt a recipe for AV's.
Among other things, the heap manager is uninitialized during the finalization
(in DLL_PROCESS_DETACH) of the system unit.

Basically, you have to figure out some way to make sure that DLL_PROCESS_DETACH
does not execute until all the threads are terminated. One way to do this would
be for each thread to call LoadLibrary on your DLL when they start, and then
call FreeLibrary when they terminate (this is the idea behind the API function
FreeLibraryAndExitThread). That way, the DLL will not actually be unloaded
until the last thread terminates.

But that means you have to have some way of telling the threads when to
terminate. If the idea is to terminate when the application does, you might set
up a windows hook to check for the destruction of the application's main window,
and use that as a signal to destroy the threads.

Finally, I think there is one small bug in the VCL's DLL thread implementation:
during the processing of DLL_THREAD_DETACH, ExitThreadTLS is called before
DllProc (this happens in the _StartLib procedure in system.pas; the code with
the comment "Init any needed TLS" will call one of InitProcessTLS, InitThreadTLS
or ExitThreadTLS depending on the DLL_XXXX parameter. ExitProcessTLS is
correctly called after DllProc and unit finalizations). ExitThreadTLS calls
LocalFree on the thread's local storage. This means that a DllProc which tries
to access a threadvar will be accessing a block of memory which has been
Local-freed. Whether that will inevitably lead to AV's I don't know.

Good luck,

Greg Chapman

위로