Subject Is XNet local connection thread safe?
Author

Hi,

   I have been having some problems with a threaded application which creates local Xnet connections in multiple threads. If two or more threads are calling ISC_ATTACH_DATABASE at the same time, the connection fails with error -901 (335544741) Connection Lost to Database.


  If I wrap the call to ISC_ATTACH_DATABASE in a critical section so that only one thread can call it at a time there is no problem. 


  I get the same issue with IBX and IBDac (devart) but not FireDAC. On reading the source, FireDAC wraps everything it does with the database client in a critical section. So it is stable but slow in a threaded application.


  If I use a localhost TCP IP connection, there are no problems.


  This feels like a thread safety bug in the Firebird client.  Should it be thread safe for calls to ISC_ATTACH_DATABASE?


   If this is, or could be a bug, what is the correct way to report it?


Sample code below.


Regards,

  Will.



Delphi XE.

Using IBX or Devart IBDAC

Firebird Classic 2.5.7

Windows 10



unit uFBClientMCVEMain;


interface


uses

  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

  Dialogs, StdCtrls, DB;


type

  TForm1 = class(TForm)

    Button1: TButton;

    procedure Button1Click(Sender: TObject);

  private

    { Private declarations }

  public

    { Public declarations }

  end;


  TDBWorkerThread = class(TThread)

  strict private

    fConnectionString : String ;

  protected

    constructor Create(aConnectionString : String) ; overload ;

    procedure Execute ; override ;

  end;


var

  Form1: TForm1;


implementation

uses IBDatabase ;


{$R *.dfm}


procedure TForm1.Button1Click(Sender: TObject);

var

  i : integer ;

begin

  for i := 0 to 10 do

  begin

    TDBWorkerThread.Create('SAMPLEDB');

  end;

end;


{ TDBWorkerThread }


constructor TDBWorkerThread.Create(aConnectionString: String);

begin

  inherited Create(false) ;

  FreeOnTerminate := True ;

  FConnectionString := aConnectionString ;

end;


procedure TDBWorkerThread.Execute;

var

  DB : TIBDatabase ;

  Trans : TIBTransaction ;

  i : integer ;

begin

  inherited;

  DB := TIBDatabase.Create(nil) ;

  Trans := TIBTransaction.Create(nil);

  try

    DB.Params.Clear ;

    DB.DatabaseName := FConnectionString ;

    DB.Params.Add('user_name=SYSDBA') ;

    DB.Params.Add('password=masterkey') ;

    DB.Params.Add('lc_ctype=ISO8859_1') ;

    DB.LoginPrompt := False ;


    Trans.DefaultDatabase := DB ;

    Trans.Params.Clear ;

    Trans.Params.Add('read_committed') ;

    Trans.Params.Add('rec_version');

    Trans.Params.Add('nowait');

    Trans.Params.Add('read');

    Trans.AllowAutoStart := False ;

    DB.DefaultTransaction := Trans ;


    for i := 0 to 99 do

    begin

      DB.Open;


      sleep(10000) ;

//The application will crash before it has done any calls to DB.Close. ruling 

//that out as an aggravating factor.

      DB.Close ;

    end;


  finally

    Trans.Free ;

    DB.Free ;

  end;

end;



end.