#1 2017-08-01 10:46:46

itSDS
Member
From: Germany
Registered: 2014-04-24
Posts: 516

Possible Bug with Interfaces in CallMethod

Hi AB,

yesterday i solved a BUG in our mORMot Service and I'm not sure if it's a Berlin 10.2 BUG(Feature) or something in your mormot.pas:CallMethod Assembler Code. As it is not easy to make a demo i try to explain my observations and may be you have an idea.

We have a SOAP Interface Function like this

  IDFMyService = interface(IInvokable)
...
    function GetDetails(const AIDs : TIntegerDynArray) : RDetailsResult;

the function GetDetails works like this in principle - We use this for a long time now

function <class>.GetDetails(const AIDs : TIntegerDynArray) : RDetailsResult;
var
  LStmt : ISQLDBRows;
  LConnection : TSQLDBConnectionProperties;
begin
  FillChar(Result, SizeOf(Result), 0);
  LConnection := CreateConnection;
  if assigned(LConnection) then try
      LStmt := LConnection.ExecuteInlined(<Any SQL>, True);
  finally
    FreeAndNil(LConnection);
  end;
end;

Using this function from Client sometimes throws Exception in line mormot.pas 57405:

procedure CallMethod(var Args: TCallMethodArgs);
....
        call    [esi].TCallMethodArgs.method
        // retrieve result
        mov     cl, [esi].TCallMethodArgs.resKind  <- Exception

To eliminate the Exception i had to set LStmt to nil after using it and before function end;

function <class>.GetDetails(const AIDs : TIntegerDynArray) : RDetailsResult;
var
  LStmt : ISQLDBRows;
  LConnection : TSQLDBConnectionProperties;
begin
  FillChar(Result, SizeOf(Result), 0);
  LConnection := CreateConnection;
  if assigned(LConnection) then try
      LStmt := LConnection.ExecuteInlined(<Any SQL>, True);
      try
... Do something with LStmt
      finally
        LStmt := nil;
      end;
  finally
    FreeAndNil(LConnection);
  end;
end;

I think there is something wrong with interface release stuff

what do you think

BR
Stefan

Last edited by itSDS (2017-08-01 10:49:11)


Rad Studio 12.1 Santorini

Offline

#2 2017-08-01 11:01:17

itSDS
Member
From: Germany
Registered: 2014-04-24
Posts: 516

Re: Possible Bug with Interfaces in CallMethod

Here is more information concerning the Exception EAccessViolation:

Call Stack wrote:

:7786b802 KERNELBASE.RaiseException + 0x62
mORMot.CallMethod(???)
mORMot.TServiceMethodExecute.RawExecute($463F8A4,0)
mORMot.TServiceMethodExecute.ExecuteJson((...),']',$2F3F9D0,False)
mORMot.TServiceFactoryServer.ExecuteMethod($352D460)
mORMot.ComputeResult
mORMot.TSQLRestServerURIContext.InternalExecuteSOAByInterface
mORMot.TSQLRestRoutingREST.ExecuteSOAByInterface
mORMot.TSQLRestServerURIContext.ExecuteCommand
mORMot.TSQLRestServer.URI($463FBD0)
mORMotHttpServer.TSQLHttpServer.Request($34676A8)
SynCrtSock.THttpServerGeneric.Request($34676A8)
SynCrtSock.THttpApiServer.Execute


Rad Studio 12.1 Santorini

Offline

#3 2017-08-01 11:34:54

Chaa
Member
Registered: 2011-03-26
Posts: 249

Re: Possible Bug with Interfaces in CallMethod

It's a Delphi feature, not a bug.
Compiler create code like this:

function GetDetails(const AIDs : TIntegerDynArray) : RDetailsResult;
var
  LStmt : ISQLDBRows;
  LConnection : TSQLDBConnectionProperties;
begin
  Pointer(LStmt) := nil; // <- compiler create this code for you
  try                    // <- compiler create this code for you

    FillChar(Result, SizeOf(Result), 0);
    LConnection := CreateConnection;
    if assigned(LConnection) then try
        LStmt := LConnection.ExecuteInlined(<Any SQL>, True);
    finally
      FreeAndNil(LConnection);
    end;

  finally              // <- compiler create this code for you
    _IntfClear(LStmt); // <- compiler create this code for you
  end;                 // <- compiler create this code for you
end;

And, as you can see, database connection destroyed earlier than statement object. But statement object refers to database connection, and when statement object destructor called there is an access violation.

Offline

#4 2017-08-01 11:54:50

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,669
Website

Re: Possible Bug with Interfaces in CallMethod

... and it is clearly documented as such.
It happens when you are mixing interfaces and regular classes: they do not have the same exact live time.

Please read again https://synopse.info/files/html/Synopse … #TITLE_178
Everything is already explained here.

In short: you need to explicitly release the ISQLDBRows instance, by setting it to nil, BEFORE freeing the owner's connection.
So there is no bug in the framework, only how you use it.

And don't create a connection properties before each database access.
It is VERY inefficient.

Documentation wrote:

Of course, most of the time you will initialize your TSQLDBConnectionProperties globally for your process, then release it when it ends.

Offline

#5 2017-08-01 12:37:23

itSDS
Member
From: Germany
Registered: 2014-04-24
Posts: 516

Re: Possible Bug with Interfaces in CallMethod

ty chaa and ab
you're right i have missed this part in your documentation.
I rewrote my code an allocate the Connection Properties at service Creation now.


Rad Studio 12.1 Santorini

Offline

Board footer

Powered by FluxBB