Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Possible Deadlock on Calling FreeLibrary

Tags:

c++

dll

delphi

I have to write a DLL in Delphi XE7. I want to use TParallel.For in the DLL. The DLL is loaded into a C++ application, where everything works. However, when the application terminates, or a call to FreeLibrary is made, the application hangs. If I remove all the TParallel.For loops and replace them with standard loops, the application exits normally.

The TParallel.For loops are very simple:

TParallel.For(0, inImage.Height -1, 
  Procedure(ty : integer)
  begin
    SomeProcedure(ty);
  end);

If I create a Delphi application with exactly the same code, everything works perfectly.

After doing a lot of research and debugging, it looks like there is a deadlock which is preventing the C++ application from exiting when FreeLibrary is called, but I can't find where the problem is in TParallel.

Just to summarise the situation:

  • The TParallel.For loops all complete and produce the correct results.
  • The exact same TParallel.For code in a Delphi .exe works correctly.
  • The DLL is loaded and the functions are called and perform correctly from within the C++ application.
  • The C++ application will exit correctly if there are no TParallel.For loops.
  • The C++ application will hang if there are TParallel.For loops.
  • I am guessing that there is a deadlock which occurs when FreeLibrary is called.
  • If I use the OTL threading library, everything works as it should.

My questions are:

Has anyone else experienced this behaviour?

What is a good debugging strategy to find a deadlock in this situation?

Any advice is greatly appreciated.

UPDATE

OK, so if you want Minimal, Complete and Verifiable example, here you go (thank you Stephen Ball):

library ADelphiDLL;

uses
  System.SysUtils, System.Classes, Threading, SyncObjs;

function IsPrime (N: Integer): Boolean;
var
 Test: Integer;
begin
 IsPrime := True;
 for Test := 2 to N - 1 do
   if (N mod Test) = 0 then
   begin
     IsPrime := False;
     break; {jump out of the for loop}
   end;
end;

function Prime(Max : integer) : boolean;
var
  tot : integer;
begin
   tot := 0;
   TParallel.For(1, Max, procedure (I: Integer)
    begin
      if IsPrime (I) then
        TInterlocked.Increment (Tot);
    end);
   return true;
   end;

exports Prime;

begin
   IsMultiThread := True;
end.

in C++:

#include "stdafx.h"

typedef bool(__stdcall *primesf)(int);

void main()
{
HINSTANCE hGetDLL = LoadLibrary(L"ADelphiDLL.dll");
primesf primes = (primesf)GetProcAddress(hGetProcIDDLL, "Primes");
bool result = primes(100);
FreeLibrary(hGetDLL);// <-- Hangs here forever
}

In response to the very "helpful" comments, "there is a defect in the code " and "debug it yourself", thank you, that is what I have been doing for a too long a time. So, if there is no help forthcoming here, I will try and get permission to switch to OTL, which does work in the DLL in question.

UPDATE 2

OTL works exactly as expected. So, yes, there is a "defect in the code". I give up. I will recommend abandoning Delphi altogether and then we can move everything over to C++ and C#. That has to be a much better short (and long term) solution.

like image 742
Hugooo Avatar asked Apr 21 '17 07:04

Hugooo


1 Answers

I have seen an issue similar to this, although i was using Delphi 10.0 Seattle, and a Delphi EXE loading a Delphi DLL.

Anyway, the solution that I came up with is the following:

  1. In your Delphi DLL, first create your own thread pool.

  2. Use the overloaded version of TParallel.For that takes a thread pool object as its last parameter, and provide your own thread pool object.

  3. Before unloading your Delphi DLL, make sure to free your thread pool object.

This approach solved the problem for me.

TParallel.For documentation:

http://docwiki.embarcadero.com/Libraries/Berlin/en/System.Threading.TParallel.For

Example pseudo-code:

MyPool: TThreadPool;
MyPool := TThreadPool.Create;

TParallel.For(1, Max, procedure (I: Integer)
    begin
      if IsPrime (I) then
        TInterlocked.Increment (Tot);
    end, 
MyPool  
);

MyPool.Free;
like image 87
Torbjörn Olsson Avatar answered Oct 26 '22 07:10

Torbjörn Olsson