Functions for handling weak pointers

As we already stated in this blog, such a Zeroing ARC model has been implemented in Objective C by Apple, starting with Mac OS X 10.7 Lion, in replacement (and/or addition) to the previous manual memory handling implementation pattern: in its Apple's flavor, ARC is available not only for interfaces, but for objects, and is certainly more sophisticated than the basic implementation available in the Delphi compiler: it is told (at least from the marketing paper point of view) to use some deep knowledge of the software architecture to provide an accurate access to all instances - whereas the Delphi compiler just relies on a out-of-scope pattern. In regard to classic garbage collector memory model, ARC is told to be much more efficient, due to its deterministic nature: Apple's experts ensure that it does make a difference, in term of memory use and program latency - which both are very sensitive on "modest" mobile devices. In short, thanks to ARC, your phone UI won't glitch during background garbage recycling. So mORMot will try to offer a similar feature, even if the Delphi compiler does not implement it (yet).

The main issue with reference counting is the potential circular reference problem. This occurs when an interface has a strong pointer to another, but the target interface has a strong pointer back to the original. Even when all other references are removed, they still will hold on to one another and will not be released. This can also happen indirectly, by a chain of objects that might have the last one in the chain referring back to an earlier object.

See the following interface definition for instance:

  IParent = interface
    procedure SetChild(const Value: IChild);
    function GetChild: IChild;
    function HasChild: boolean;
    property Child: IChild read GetChild write SetChild;
  end;

IChild = interface procedure SetParent(const Value: IParent); function GetParent: IParent; property Parent: IParent read GetParent write SetParent; end;

The following implementation will definitively leak memory:

procedure TParent.SetChild(const Value: IChild);
begin
  FChild := Value;
end;
procedure TChild.SetParent(const Value: IParent);
begin
  FParent := Value;
end;

In Delphi, most common kind of reference-copy variables (i.e. variant, dynamic array or string) solve this issue by implementing copy-on-write. Unfortunately, this pattern is not applicable to interface, which are not value objects, but reference objects, tied to an implementation class, which can't be copied.

One common solution is to use Weak pointers, by which the interface is assigned to a property without incrementing the reference count. In order to easily create a weak pointer, the following function was added to SQLite3Commons.pas:

procedure SetWeak(aInterfaceField: PIInterface; const aValue: IInterface);
begin
  PPointer(aInterfaceField)^ := Pointer(aValue);
end;

Therefore, it could be used as such:

procedure TParent.SetChild(const Value: IChild);
begin
  SetWeak(@FChild,Value);
end;
procedure TChild.SetParent(const Value: IParent);
begin
  SetWeak(@FParent,Value);
end;

But there are still some cases where it is not enough. Under normal circumstances, a class instance should not be deallocated if there are still outstanding references to it. But since weak references don't contribute to an interface reference count, a class instance can be released when there are outstanding weak references to it. Some memory leak or even random access violations could occur. A debugging nightmare...

In order to solve this issue, ARC's Zeroing Weak pointers come to mind.
It means that weak references will be set to nil when the object they reference is released. When this happens, the automatic zeroing of the outstanding weak references prevents them from becoming dangling pointers. And voilà! No access violation any more!

In order to easily create a so-called zeroing weak pointer, the following function was defined in SQLite3Commons.pas:

procedure SetWeakZero(aObject: TObject; aObjectInterfaceField: PIInterface;
  const aValue: IInterface);

A potential use case could be:

procedure TParent.SetChild(const Value: IChild);
begin
  SetWeakZero(self,@FChild,Value);
end;
procedure TChild.SetParent(const Value: IParent);
begin
  SetWeakZero(self,@FParent,Value);
end;

We also defined a class helper around the TObject class, to avoid the need of supplying the self parameter, but unfortunately, the class helper implementation is so buggy it won't be even able to compile before Delphi XE version of the compiler. But it will allow to write code as such:

procedure TParent.SetChild(const Value: IChild);
begin
  SetWeak0(@FChild,Value);
end;

For instance, the following code is supplied in the regression tests, and will ensure that weak pointers are effectively zeroed when SetWeakZero() is used:

function TParent.HasChild: boolean;
begin
  result := FChild<>nil;
end;

Child := nil; // here Child is destroyed Check(Parent.HasChild=(aWeakRef=weakref),'ZEROed Weak');

Here, aWeakRef=weakref is true when SetWeak() has been called, and equals false when SetWeakZero() has been used to assign the Child element to its Parent interface.

Weak pointers implementation details

The SetWeak() function itself is very simple (see above). 
The Delphi RTL/VCL itself use similar code when necessary.

But the SetWeakZero() function has a much more complex implementation, due to the fact that a list of all weak references has to be maintained per class instance, and set to nil when this referring instance is released.

The mORMot implementation tries to implement:
- Best performance possible when processing the Zeroing feature;
- No performance penalty for other classes not involved within weak references;
- Low memory use, and good scalability when references begin to define huge graphs;
- Thread safety - which is mandatory at least on the server side of our framework;
- Compatible with Delphi 6 and later (avoid syntax tricks like generic).

Some good existing implementations can be found on the Internet:
- Andreas Hausladen provided a classical and complete implementation at http://andy.jgknet.de/blog/2009/06/weak-interface-references using some nice tricks (like per-instance optional speed up using a void IWeakInterface interface whose VMT slot will refer to the references list), is thread-safe and is compatible with most Delphi versions - but it will slow down all TObject.FreeInstance calls (i.e. within Free / Destroy) and won't allow any overriden FreeInstance method implementation;
- Vincent Parrett proposed at http://www.finalbuilder.com/Resources/Blogs/PostId/410/WeakRefence-in-Delphi-solving-circular-interfac.aspx a generic-based solution (not thread-safe nor optimized for speed), but requiring to inherit from a base class for any class that can have a weak reference pointing to it;
- More recently, Stefan Glienke published at http://delphisorcery.blogspot.fr/2012/06/weak-interface-references.html another generic-based solution, not requiring to inherit from a base class, but not thread-safe and suffering from the same limitations related to TObject.FreeInstance.

The implementation included within mORMot uses several genuine patterns, when compared to existing solutions:
- It will hack the TObject.FreeInstance at the class VMT level, so will only slow down the exact class which is used as a weak reference, and not others (also its inherited classes won't be overriden) - and it will allow custom override of the virtual FreeInstance method;
- It makes use of our TDynArrayHashed wrapper to provide a very fast lookup of instances and references, without using generic definitions;
- The unused vmtAutoTable VMT slot is used to handle the class-specific orientation of this feature (similar to TSQLRecordProperties lookup as implemented for DI-2.1.3), for best speed and memory use.

See the TSetWeakZeroClass and TSetWeakZeroInstance implementation in SQlite3Commons.pas for the details.

Feedback is welcome in our forum, just as usual.