Yep - I remember that my fix was to set the "destructing" state indicator in
a BeforeDestruction() override.  This was then tested in _Release() to
render it a NO-OP during execution of the destructor chain (incomplete,
obviously, just to give the idea):

 

  Procedure BeforeDestruction;

     SetState(csDestroying);

 

  Function _Release;

    If csDestroying in State then EXIT;

 

Nothing else needs be done, as long as any further BeforeDestruction
overrides call inherited before doing their work, which they should do (in
my framework I introduced another virtual to be overridden in my
descendants, in case there were occasions when work was done to generate
references during the destructor  execution - even in your case, the
FreeInstance() override is redundant I think, other than as a sanity/safety
check and so could be made subject to some conditional compilation flag.

 

 

From: delphi-boun...@delphi.org.nz [mailto:delphi-boun...@delphi.org.nz] On
Behalf Of Todd
Sent: Wednesday, 24 November 2010 6:15 p.m.
To: NZ Borland Developers Group - Delphi List
Subject: Re: [DUG] How's this for inconsistent

 

Actually, this would be better

function TamObject._Release: Integer;
begin
  Result := InterlockedDecrement(FCount);
  if (FCount = 0) then
  begin
    //add a reference count, incase an interface is acquired and released
during destruction
    InterlockedIncrement(FCount);
    self.Destroy;
  end;
end;  

procedure TamObject.FreeInstance;
begin
  //remove the reference count added in _Release
  InterlockedDecrement(FCount);
  assert(FCount = 0,'Destroying object with non-zero reference count');
  inherited FreeInstance;
end;



I spotted that they fixed that a while ago - I remember having to fix the
issue myself many years ago so was quite pleased to see that it was now
taken care of in TInterfaceObject as a matter of course.  For some reason I
never noticed the omission of the same facility in the destructor.  And yes,
it's a [potentially] big problem.

 

I need to think about this tho... setting a fake ref count during execution
of the constructor is safe enough as you know precisely when construction is
done and to restore the ref count back to zero.

 

Setting a fake ref count during destruction strikes me as more problematic
and makes me nervous, but I can't quite put my finger on why.  It might be
nothing.  That doesn't mean it can't be fixed, only that the solution put in
place for construction might not work for destruction and it wasn't felt
necessary to do any extra work for a more comprehensive solution.

 

 

Certainly in the case of my code where I fixed this I had specific
"constructing" / "destructing" state markers (it wasn't a general purpose
interfacedobject class but a base class in a far richer framework that
happened to also implement its own version of IUnknown) - I know I didn't
rely on side effects of a faked ref count.

 

 

From: delphi-boun...@delphi.org.nz [mailto:delphi-boun...@delphi.org.nz] On
Behalf Of Todd
Sent: Wednesday, 24 November 2010 16:55
To: NZ Borland Developers Group - Delphi List
Subject: [DUG] How's this for inconsistent

 

The Delphi developer who implemented TInterfacedObject obviously considered
the case when an interface reference is grabbed during construction......

// Set an implicit refcount so that refcounting
// during construction won't destroy the object.
class function TInterfacedObject.NewInstance: TObject;
begin
  Result := inherited NewInstance;
  TInterfacedObject(Result).FRefCount := 1;
end;

procedure TInterfacedObject.AfterConstruction;
begin
// Release the constructor's implicit refcount
  InterlockedDecrement(FRefCount);
end;

but didn't consider applying the same logic during destruction. So grabing
an interface reference during destruction causes all hell to break loose, as
the _Release method tries to free the object again and again recursively.

procedure TInterfacedObject.BeforeDestruction;
begin
  if RefCount <> 0 then
    Error(reInvalidPtr);
end;

function TInterfacedObject._Release: Integer;
begin
  Result := InterlockedDecrement(FRefCount);
  if Result = 0 then
    Destroy;
end;

Todd.

 
 
_______________________________________________
NZ Borland Developers Group - Delphi mailing list
Post: delphi@delphi.org.nz
Admin: http://delphi.org.nz/mailman/listinfo/delphi
Unsubscribe: send an email to delphi-requ...@delphi.org.nz with Subject:
unsubscribe

 

_______________________________________________
NZ Borland Developers Group - Delphi mailing list
Post: delphi@delphi.org.nz
Admin: http://delphi.org.nz/mailman/listinfo/delphi
Unsubscribe: send an email to delphi-requ...@delphi.org.nz with Subject: 
unsubscribe

Reply via email to