refcounting models - an update

From: Robert Collins <robertc@dont-contact.us>
Date: 11 Jul 2003 16:03:10 +1000

I've written some thoughts on refcounting + cbdata and interoperation,
both here and in the dev guide.

I've finally had time to sit down and check for corner cases - so here
is a thumbnail sketch of my guidelines. I'll update the developers guide
shortly... if no-one poses a case I hadn't considered.

As a refresher, in 3.0 we have 3 ways memory can be allocated:
* via xfree and xmalloc - ::operator new and ::operator delete use
this.
* via MemPools
* via CBDATA (which uses MemPools).

In terms of remote storage - objects with pointers to other objects, we
have 4 models:
* plain pointers
* cbdata pointers
* RefCount<> template smart pointers. (new in 3.0)
* Manually refcounted pointers. (i.e. StoreEntry).

What I'm here to address is guidelines for choosing which allocation
strategy, and which remote storage strategy to use when extending (or
cleaning up) squid.

There are three common usage patterns of remote storage that we see in
squid:
* Complete ownership
* shared ownership
* callback ownership

Complete ownership:
When object A owns an instance of class B, and is the only object that
ever keeps that reference beyond synchronous calls, A is completely
responsible for the management of B - when it is allocated, and when it
is freed. In this case, either the default operator ::new and ::delete,
or a MemPool for class B established for memory allocation. A plain
pointer is most appropriate for the remote ownership. Note that this is
-not- compatible if the instance of class B is ever passed (or passes
itself) to a callback function, where cbdata should be used - see below.

Shared ownership:
When multiple objects hold references to a single instance, then
RefCounting (manual or automatic) is appropriate. This allows the
original creator to be disposed of and still have the instance intact
and available for the other objects that hold references. Note that this
is -compatible- with Callback ownership. Additionally, callback
ownership is -not- appropriate to use if one wants to call methods on
the object. That requires reference counting, which is -not- what cbdata
provides.

Callback ownership:
Squid's callback logic works thusly:
When callback is ready to occur, the callback is only called IF the
object to be called back is still valid - it hasn't been destroyed. The
CBDATA functions such as cbdataReferenceValid are used to check this. An
object that holds function,data pairs has callback ownership over the
data - that is:
* It doesn't prevent the data object being freed.
* It doesn't hold a reference to the object.
* It is able to test for the objects validity.
Callback ownership requires the objects memory to be managed by the
cbdataAlloc and cbdataFree routines. This is compatible with Shared
ownership.

Ok, now the guidelines:
* If an object calls any callback routines, it must be CBDATA, and have
it's new and delete operators overloaded appropriately. Additionality it
must have a deleteSelf() method.
* If an object has more than one owner (that is, an object that either
calls into it, or that passes it along to another object), then it
should be automatically reference counted. Manual reference counting is
deprecated.
* If an object has only one owner, then it doesn't need to be reference
counted, but may still need to be CBDATA - see two points up.

Now, in terms of corner cases, Henrik expressed some concern about
cbdata and reference counting interactions:
It works our very simply:
* Anything that needs to call on an object, or use it for anything other
than an argument when doing a callback, holds a RefCount to it. That
guarantees the object is not deleted until the RefCount is set to NULL.
* Callback routines call cbdataReference and cbdataReferenceDone as
usual.
* If the RefCount hits 0 while cbdataReferences are outstanding, then:
  * the destructor runs, freeing the objects resources.
  * cbdataFree is called, marking the object as invalid
  * routines waiting to callback will not do so - as it's invalid.
  * the memory will be held until all cbdataReferences are released.
* If the cbdata references is 0 when the RefCount count hits 0, then the
above happens, but the memory is immediately released.

The only catch is there is likely areas in squid abusing
cbdataReferences as a substitute for refcounting - and they should be
fixed when adding the RefCount machinery to a given class .. like I did
with ConnStateData today. No harm will come to such existing areas, as
the behaviour doesn't change until one adds the RefCount machinery.

Rob

-- 
GPG key available at: <http://members.aardvark.net.au/lifeless/keys.txt>.

Received on Fri Jul 11 2003 - 00:03:18 MDT

This archive was generated by hypermail pre-2.1.9 : Tue Dec 09 2003 - 16:20:16 MST