0
votes

I am using TOmniBlockingCollection from a "Server Thread".

In this thread, I am hosting a Single-Threaded Apartment COM object to a DataProvider (CoInitialize() and CoUninitialize() are called).

Then additionally, I am using multiple worker threads to write to an ADO Dataset (TADOCommand), which also needs CoInitialize()/CoUninitialize().

Only when I catch an error from inside the WorkerFunction and try to terminate the "Server Thread", I am waiting (infinitely?) on the CoUninitialize() in the "Server Thread" (inherited from TThread) that started the WorkerFunction.

Callstack:

:774546bc ntdll.ZwWaitForAlertByThreadId + 0xc 
.... some other system functions
:771e8069 combase.CoUninitialize + 0xf9
UTServerThread.TServerThread.Execute (calls CoUninitialize)

How do I avoid the wait in the Server Thread when I catch an error ... it seems to be something COM-related in multi-threading.

Pseudo Code:

procedure CreateIOmniWorkers;
begin
  for Each IOmniWorker in OmniWorkerArry
  begin
    IOmniWorker := CreateTask("WorkerFunction" as TOmniTaskMethod, Description)
    .SetParameter('Input', InputCollection)
    .WithLock(TSynchroObject.Create) // The Used Logger inside Worker Funtion needs a Lock
    .OnTerminated(ErrorHandler);
  end;
end;

procedure ErrorHandler(const task: IOmniTaskControl);
var
  eException: Exception;
begin
  if Assigned(task.FatalException) then
  begin
    eException := task.DetachException;
    try
    Logger.Error(
      'TOTLBlockingListWorker', eException.ClassName,
      Format(rsScriptErrorDataBlock,[eException.Message, eException.StackTrace]));
    finally
      FreeAndNil(eException);
    end;
  end;
end;

procedure WorkerFunction(const task: IOmniTask; var SyncData: TSyncOutputValueHolder);
begin
  CoInitialize(nil);
  try
    CreateConnection(SyncData);
    // Somewhere here a Exception happens
    WriteSQLStyle(task, SyncData);
  finally
    CoUninitialize; // is called
  end;
end;

procedure UTServerThread.TServerThread.Execute
begin
  CoInitialize(nil);
  try
    while not Terminated
    begin
      CreateIOmniWorkers;
      with TOTLBlockingCollection do
       while DataAvailable
         TOTLBlockingCollection.Add(Datapackage);
       TOTLBlockingCollection.CompleteAdding;
       while not TOTLBlockingCollection.IsFinalized
         Sleep(250);
    end;
  finally
    CoUninitialize; // Here the (infinite?) Wait happens.
  end;
end;
Make sure all COM interfaces, especially implicit/temporary ones, are properly released before calling CoUninitialize(). For this reason, I tend to NOT write code directly between CoInitialize/CoUninitialize, but instead move the code inside of a procedure called between CoInitialize/CoUninitialize, so that all local/temporary variables are finalized on exit, eg: procedure DoTaskWork; begin ... end; procedure WorkerFunction(...); begin CoInitialize(nil); try DoTaskWork; finally CoUninitialize; end; end;Remy Lebeau
Have you check with CoInitializeEx(nil,COINIT_MULTITHREADED) instead of CoInitialize(nil)?Xalo