Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DLL and class in multithreaded application

I have a class inside my DLL. And this DLL provides an "interface" to create objects of this class and call their methods.

Class code (simplified):

TLogger = class
  private
    //
  public
    function AddToLog(sBuf: PWideChar): Word;
    constructor Create(Name: PWideChar);
    destructor Destroy; override;
end;

constructor Create(Name: PWideChar);
begin
  //
end; 

destructor TLogger.Destroy;
begin
  //
end;

function TLogger.AddToLog(sBuf: PWideChar): Word;
var
  Temp1 : WideString;
  Temp2 : AnsiString;

begin
  Result := NO_ERRORS;

  WaitForSingleObject( FMutex, INFINITE );

  Temp1 := 'a';
  Temp2 := Temp1;

  //

  ReleaseMutex( FMutex );
end;

DLL code

function CreateLogger( LogFileName: PWideChar; PLogger: PCardinal): Word; stdcall;
begin
  try
    PLogger^ := Cardinal(TLogger.Create(LogFileName));
    Result := NO_ERRORS;
  except
    Result := ERR_CREATE_OBJECT;
  end;
end;

function DestroyLogger( PLogger: Cardinal ): Word; stdcall;
begin
  try
    TLogger(PLogger).Free;
    Result := NO_ERRORS;
  except
    Result := ERR_OBJECT_NOT_FOUND;
  end;
end;

function AddToLog( PLogger: Cardinal; BufStr: PWideChar ): Word; stdcall;
begin
  try
    Result := TLogger(PLogger).AddToLog( BufStr );
  except
    Result := ERR_OBJECT_NOT_FOUND;
  end;
end;

When I'm trying to use this library from 1 thread - everything is OK. The problems start when I create many threads that call function AddToLog with random periods (each thread has it's own object of the class). In some time I catch Access Violation or Invalid pointer operation. I've made some research and pointed out that if variable Temp2 has WideString type everything is OK. Another solution is to move mutex to the library code (it's just a "research" code):

function AddToLog( PLogger: Cardinal; BufStr: PWideChar ): Word; stdcall;
begin
  WaitForSingleObject( TLogger(PLogger).FMutex, INFINITE );
  Result := TLogger(PLogger).AddToLog( BufStr );
  ReleaseMutex( TLogger(PLogger).FMutex );
end;

The second solution is bad for me because each object has it's own mutex (idea is that if two objects must work with one file, they have the same mutex to wait each other; if two objects must work with different files, they have different mutexes and work in parallel).

I'm trying to solve this problem for 2 days but I can't understand what goes wrong. How string cast can cause such problem?

like image 688
AntonBazhal Avatar asked Jul 30 '13 14:07

AntonBazhal


1 Answers

Put the following line:

IsMultiThread := True;

as the first line in your DLL project's main code block. This will instruct the memory manager to switch to a thread-safe mode.

This would explain a behaviour difference between AnsiString and WideString because AnsiString is allocated by the Delphi memory manager, and WideString is allocated on the COM heap. When IsMultiThread is False, the Delphi memory manager is not thread-safe, but the COM heap is always thread-safe.

like image 75
Ondrej Kelle Avatar answered Nov 20 '22 15:11

Ondrej Kelle