You are not logged in.
Pages: 1
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
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
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
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
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
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
Maybe add code to detect such problems.
See http://qc.embarcadero.com/wc/qcmain.aspx?d=92222
Offline
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
Thanks!
Offline
Pages: 1