[Lazarus] Delphi post-XE3 roadmap

Bernd prof7bit at gmail.com
Tue Aug 28 12:47:27 CEST 2012


2012/8/27 Michael Schnell <mschnell at lumino.de>:
> On 08/24/2012 10:36 PM, Marco van de Voort wrote:
>>
>> I read that as that there will be ref counted objects, not that all
>> objects will be ref counted per se. --
>
>
> Ok, but if there are reef-counted objects what is the point in not using
> them ?

consider for example this code (simplified real world example):

TConnection is a reference counted object and the only other object
that holds a reference to it is its Buddy object and the following
method is called by LNet which does (and can) NOT hold a reference to
it, only a TMethod callback that does not affect the reference count.
Now carefully watch what happens when this method is called.

procedure TConnection.OnTCPFail(ASocket: TLHandle; const Error: String);
begin
  FDisconnectLock.Acquire;
  if not FShuttingDown then begin
    FShuttingDown := True;

    //no more callbacks
    FSocket.OnRead := @Client.DummySocketEvent;
    FSocket.OnWrite := @Client.DummySocketEvent;
    FSocket.OnError := @Client.DummySocketError;
    FSocket.Disconnect();

    // remove references to the connection in all other objects.
    if Assigned(FBuddy) then begin
      if IsOutgoing then
        FBuddy.SetOutgoing(nil)
      else begin
        FBuddy.SetIncoming(nil); // bug: this will free ourselves NOW!!!
      end;
      FBuddy := nil;             // Crash because we are free already!
    end;
  end;
  FDisconnectLock.Release;
end;

The very moment we clear the (last and only) reference to ourselves in
FBuddy we have committed suicide effective immediately(!) and while
the method is still running its own object instance has been freed
already and the next attempt to access any of our own(!) fields will
segfault! The reason is the LNet component that actually called this
method did not hold a properly reference counted reference!

These type of bugs are not easy to find (or to understand) if you are
not fully aware who is holding a reference (and even more importantly
who is not!) and when exactly the refererence counter will reach zero
(and maybe from within which thread this might happen).

Instead of explicitly callling free() on all your objects in the
correct order you have to make sure you set the last variable holding
a reference to nil at the correct time and in the correct order to
trigger the destructors in the correct order and not too early (and
also not too late!), this can sometimes be even more complicated to
achieve than just explicitly calling Free()

The pragmatic fix to he above problem was:

procedure TConnection.OnTCPFail(ASocket: TLHandle; const Error: String);
begin
  Self._AddRef;
  FDisconnectLock.Acquire;

  [...]

  FDisconnectLock.Release;
  Self._Release;  // <-- now we will be freed exactly here!
end;

If such behavior were suddenly the norm for *all* objects (and not
only for interfaces or otherwise explicitly and knowingly reference
counted objects) then I am sure most of currently existing code would
completely stop working from one day to the other and large parts of
many applications and frameworks would have to be rewritten from
scratch.




More information about the Lazarus mailing list