#1 2012-12-05 15:39:17

h.hasenack
Member
From: Nijmegen, Netherlands
Registered: 2012-08-01
Posts: 173
Website

Interface/object lifetime on serverside objects

Hi Ab

I am still unit testing my own code and came across the strange phenomenon: During the processing of an (remote) interfaced call, the reference count of my TInterfacedObject (serverside) is 0, even on its direct call entry.

The nasty side effect of this is that when I pass on an interface of "self" to any subroutine, my object gets freed when the routine returns (due to the reference count getting 0 again).

May I suggest increasing the reference count at least during the call? Unfortunately, when dropping the refcount after the call this would free the object and I am not sure whether this is what you want.

I understand there's some kind of timeout dropping the interface with the restserver if the connection is lost somehow. This could also drop the reference count for the interface and thus free the object.

I hope you get what I mean.

Regards, Hans

Offline

#2 2012-12-05 16:33:57

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 13,187
Website

Re: Interface/object lifetime on serverside objects

Sounds like a bug, here.
Not so difficult to fix, I suppose.

Could you create a ticket for it?
See http://synopse.info/fossil/reportlist

I'll check it.

Offline

#3 2012-12-05 16:42:29

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 13,187
Website

Re: Interface/object lifetime on serverside objects

I just check it in the debugger.

I've FRefCount=2 within the method execution, on server or client side.

How to you instantiate your instances?
Do you have any regression code to provide?

Offline

#4 2012-12-06 07:45:08

h.hasenack
Member
From: Nijmegen, Netherlands
Registered: 2012-08-01
Posts: 173
Website

Re: Interface/object lifetime on serverside objects

The interface is a "registered" one, created by the factory on server side. My implementation object is derived from TInterfacedObjectWithCustomCreate, de method called is Harvest(True:boolean)

Here's a call stack to call of the interface method on the server side

uLCSSimulationImpl.TLCSSimulationImpl.Harvest(True)
uLCSSimulationImpl.TLCSSimulationImpl.CheckResultsChanged(1636016)
:0065119B Mormot::CallMethod(Params=????)
mORMot.CallMethod(???)
mORMot.TServiceMethod.InternalExecute((...),nil {#0},$FE431B20,'',[])
mORMot.TServiceFactoryServer.ExecuteMethod($18FACC,2,0,'[1636016','','','')
mORMot.TSQLRestServer.LaunchService($18FACC,'',400)
mORMot.TSQLRestServer.URI('LCSServer/CRMS6H2TKXSUAAVAIW34CCBIA/LCSSimulation.CheckResultsChanged?session_signature=0000004C002529B97A1A92F7','POST','[1636016'#0,'','',$18FA4B)
mORMot.TSQLRestServer.AnswerToMessage((74, (), 7474580, $18FDD4, 200))
SynCommons.WndProcMethod(8390460,74,7474580,1637844)
...

I checked out: When the constructor of the object is called, it reveals that an object (pointer) is stored rather than an interface instance. This causes the reference count to be 0 instead of 1.
Here's the calls stack when calling the constructor:

uLCSSimulationImpl.TLCSSimulationImpl.Create
mORMot.TServiceFactoryServer.CreateInstance
mORMot.AddNew
mORMot.TServiceFactoryServer.InternalInstanceRetrieve((1, 623915141, nil),2)
mORMot.TServiceFactoryServer.ExecuteMethod($18FACC,2,0,'[1636016]','','','')
mORMot.TSQLRestServer.LaunchService($18FACC,'',400)
mORMot.TSQLRestServer.URI('LCSServer/A1VY503GHHDKPBNN47C4W5MII/LCSSimulation.CheckResultsChanged?session_signature=0000004C00253030C328628D','POST','[1636016]','','',$18FA4B)
mORMot.TSQLRestServer.AnswerToMessage((74, (), 5244374, $18FDD4, 200))
SynCommons.WndProcMethod(22416962,74,5244374,1637844)
...

When debugging Mormot.pas, and checking out a watch TInterfacedObject(Inst.Instance).FRefCount it reveals the RefCOunt of the instantiated object is 0.

function TServiceFactoryServer.InternalInstanceRetrieve(
  var Inst: TServiceFactoryServerInstance; aMethodIndex: integer): boolean;
procedure AddNew;
var i: integer;
    P: ^TServiceFactoryServerInstance;
begin
  Inst.Instance := CreateInstance;
  if Inst.Instance=nil then <<====== Here the FRefcount of the instantiated object is 0
    exit;
  P := pointer(fInstances);
  for i := 1 to fInstancesCount do
    if P^.InstanceID=0 then begin
      P^ := Inst; // found an empty entry -> use it
      exit;
    end else
    inc(P);
  fInstance.Add(Inst); // append a new entry
end;

Offline

#5 2012-12-06 09:04:44

h.hasenack
Member
From: Nijmegen, Netherlands
Registered: 2012-08-01
Posts: 173
Website

Re: Interface/object lifetime on serverside objects

Here's the Patch I came up with. Unsure though if it is safe (memory leaks) for the other parts where 'CreateInstance' is called.

My own unit tests turn up OK now, the unexpected destruction of my implementation object has disappeared.

My guess: You'd probably want to change CreateInstance so it returns an IInterface rather than an TInterfacedObject.
This way, you could leave the reference counting to the RTL. I tried this, but I had to change so much code that it made me feel uncomfortably.

Here's another idea for your TServiceFactoryServer: instead of registering classes, you could also register constructors (or functions that instantiate interfaces) rather than classes. This way, you are not limited to using TInterfacedObject derived classes for your interface implementations. It would also address the virtual constructor for the interface implementation class that we discussed a while ago.

function TServiceFactoryServer.CreateInstance: TInterfacedObject;
begin
  if fImplementationClassWithCustomCreate then
    result := TInterfacedObjectWithCustomCreateClass(fImplementationClass).Create else
    result := fImplementationClass.Create;
  // HH:Added to ensure reference count of at least 1
  (Result as IInterface)._AddRef;
end;

procedure TServiceFactoryServerInstance.SafeFreeInstance;
begin
  try
    InstanceID := 0;
    // HH:Added to use reference count to drop interfaces
    // rather than direct destructor calls.
    ((Instance as TInterfacedObject) as IInterface)._Release;

  //    FreeAndNil(Instance);
  except
    ; // just ignore any error in customer code -->>HH: Are you shure you dont want to catch 'fatal' exceptions here? or at least report them somewehere?
  end;
end;

destructor TServiceFactoryServer.Destroy;
var i: integer;
begin
  try // release any internal instance (should have been done by client)
    for i := 0 to fInstancesCount-1 do
      fInstances[i].SafeFreeInstance;
//      if fInstances[i].Instance<>nil then
//        fInstances[i].Instance.Free;
  except
    ; // better ignore any error in business logic code
  end;
  DeleteCriticalSection(fInstanceLock);
  inherited;
end;

Offline

#6 2012-12-06 10:11:08

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 13,187
Website

Re: Interface/object lifetime on serverside objects

h.hasenack wrote:

My guess: You'd probably want to change CreateInstance so it returns an IInterface rather than an TInterfacedObject.
This way, you could leave the reference counting to the RTL.

This was my first implementation.
And was a failure: a lot of A/V in regression code.
That's why I switched to directly handling class instances.

Offline

#7 2012-12-06 10:56:24

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

Re: Interface/object lifetime on serverside objects

Maybe add code to detect such problems.
See http://qc.embarcadero.com/wc/qcmain.aspx?d=92222

Offline

#8 2012-12-06 13:20:48

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 13,187
Website

Re: Interface/object lifetime on serverside objects

h.hasenack wrote:

The nasty side effect of this is that when I pass on an interface of "self" to any subroutine, my object gets freed when the routine returns (due to the reference count getting 0 again).

Up to now, on the server side, I only passed instances of self, not interfaces on self...
So I did not catch the problem.

Should be fixed with http://synopse.info/fossil/info/e2623df46f

Thanks a lot for the report.
Don't hesitate to create tickets, which are easier to track.

Offline

#9 2012-12-06 14:02:03

h.hasenack
Member
From: Nijmegen, Netherlands
Registered: 2012-08-01
Posts: 173
Website

Re: Interface/object lifetime on serverside objects

Thanks!

Offline

Board footer

Powered by FluxBB