1

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;
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 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 Sep 01 '21 at 16:56
  • Have you check with `CoInitializeEx(nil,COINIT_MULTITHREADED)` instead of `CoInitialize(nil)`? – Xalo Sep 02 '21 at 07:07

1 Answers1

0

I had also some Other Problem with the BlockingList.

if a Exception occured Inside the TryTake until False Loop of the Blocking List Interfaced objects would not be freed.

So i did deperately update my OTL Source to the current Version (3.07.8 was 07.6). The Problem with CoInitialize and Unitialize is gone... But... i must have still a bug in the Composition. In place of the OnTerminadted Handler i attached A Monitor to my CreateIOmniWorkers, but i dont receive Events there. The OnTerminated Handler also does not Fire. So it works... But i am not confident that i did it right.

repeat
  if not BlockingList.TryTake(Params) then
    break; // simplified

    InterfacedObject := TInferfacedObject.Create as ISomething
    try
      raise Exception while having a TInterfacedObject here ...
    Finally
      InterfaceObject := nil; // if this finally is here in case of Error the Interface would not be freed.
    end;
until false