Delphi AsyncCalls Deadlock when Terminating the Threads (com+ dll finalization)

爷,独闯天下 提交于 2020-01-03 06:41:10

问题


I have a big Delphi 2007 project, and I use AsyncCall

I have extracted and tested the multithreading code in a single console application and everything works fine.

My Delphi 2007 project produces a COM DLL and I have some unit tests that are written in C# - mstest that calls the DLL.

The unit tests hang quite often randomly (tests passed) and i found out it happens at the AsyncCall unit - finalizatoin section that frees the ThreadPool.

destructor TThreadPool.Destroy;
var
  I: Integer;
  Call: TInternalAsyncCall;
begin
  FMaxThreads := FThreadCount; // Do not allocation new threads
  FDestroying := True; // => Sync in this thread because there is no other thread (required for FAsnycCallHead.Free)

  // Allow the threads to terminate if there is no task
  for I := FThreadCount - 1 downto 0 do
    FThreads[I].Terminate;
  // Wake up all sleeping threads and keep them awake so they can terminate
  SetEvent(FThreadTerminateEvent);
  // Wait and destroy the threads
****Hangs here -------->      for I := FThreadCount - 1 downto 0 do   
****------->        FThreads[I].Free;

  ReleaseAutoDeleteAsyncCalls;

  // Clean up not yet released AutoDelete InternalAsyncCalls.
  while FAsyncCallHead <> nil do
  begin
    Call := FAsyncCallHead.FNext;
    CheckAutoDelete(FAsyncCallHead);
    FAsyncCallHead := Call;
  end;

  CloseHandle(FThreadTerminateEvent);
  CloseHandle(FWakeUpEvent);
  CloseHandle(FMainThreadSyncEvent);
  DeallocateHWnd(FMainThreadVclHandle);
  DeleteCriticalSection(FAsyncCallsCritSect);

  inherited Destroy;
end;

If I remove

  for I := FThreadCount - 1 downto 0 do
    FThreads[I].Free;

It then works fine. I can't reproduce this in a simple example and can't think of any reasons that could hang the threads when terminating.

I believe it is very similar to the issues raised here:

http://qc.embarcadero.com/wc/qcmain.aspx?d=29843

If a thread stops, it calls ThreadExit from Classes:ThreadProc And ThreadExit calls the DLLEntryProc with DLL_THREAD_DETACH. Only one thread in a process can be in a DLL initialization or detach routine at a time.

The problem is that the finalization of a com+ dll is run from the DLL_THREAD_DETACH, so that the Classes:TThread.WaitFor will wait indefinitly because the thread won't terminate because it can't handle its DLL_THREAD_DETACH till the finalization finishes.

update

I have spent several days finding out the causes, and there is a work around which is to execute the finalization section of AsyncCall unit manually.

It is nothing to do with the algorithms, because even very simple code can cause problem in com+ dll.

Here are the steps to reproduce:

create a com+ dll create a method that has the following code: make sure TestAsync is called and you will notice the deadlock when the thread pool (from AsyncCall) is cleaning up the threads.

procedure Test(int i: integer); cdecl;
begin
  Exit;
end;

procedure TestAsyn;
var
  t: IAsyncCall;
begin
  t := AsyncCall(@Test, [0])
  t.Sync;
end;

回答1:


It looks like you're trying to do something that you're not allowed to do within initialization/finalization of a dll.

You'll need to run that finalization code from somewhere else. Usually this is done inside of a helper function that the dll exports. The other option is to run the finalization when the last com object of your dll is freed.

The exported function DllCanUnloadNow looks like a candidate for terminating the threads.



来源:https://stackoverflow.com/questions/33137336/delphi-asynccalls-deadlock-when-terminating-the-threads-com-dll-finalization

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!