MPQC  3.0.0-alpha
The Reference Library

The Reference Library provides a means to automatically free memory that is no longer needed.

Introduction to Reference Counting

It is fairly easy in C++ to create a pointer to an object that actually references invalid memory. One common way to do this is to create an object with new and store that object's pointer. Then the pointer is given to another object's member function as an argument which keeps a copy of the pointer for future use. After the member function returns, the routine that originally created the object delete's it, not knowing that another object has since created a reference to the object. The result of using the delete'ed object is unpredictable and would likely be a program crash. It is up to the programmer to provide the logic necessary to avoid this problem. The programmer must also deal with the problem of calling to delete operator on any new'ed memory when it is no longer referenced.

Reference counting is one technique that can be applied to automate memory management. In this approach, a count of how many pointers point to an object is attached to that object. This count is managed by a smart pointer class which mimics the behavior of C++ pointers by providing operator->(). This class has a pointer to the reference counted object and increments the reference count of objects when they are assigned to it while decrementing the counts of the objects that are displaced by these assigments. The smart pointer class automatically delete's the object when its reference count drops to zero.

A deficiency of this method is that unreferenced circular lists are not automatically deleted. Circular list implementors must provide a mechanism to detect when the list is dereferenced and then break the list's circularity to let the automated reference mechanism finish the work.

The reference library provides smart pointers and a base class that can be used to maintain reference counts to objects. For an object to be reference counted its class must inherit from the RefCount class. This adds sizeof(int) bytes of overhead per object and makes the destructor virtual (so a vtable will be added to objects of the class, if there wasn't already a virtual member in the class).

The smart pointers that maintain the reference counts are provided by the Ref class template. A smart pointer to a class A which inherits from RefCount would have the type Ref.

Thread Safety of the Reference Counting Package

The referencing counting package is thread-safe if the CPP macro REF_USE_LOCKS is defined to 1. This means that Ref's to a particular object can be created and reassigned and destroyed in different threads. However, the Ref's themselves are not thread-safe. For example, a static Ref cannot be simultaneously modified from multiple threads.

Because there is an overhead associated with locking access to an object's reference count, locking is not turned on by default, and, thus, making and deleting references to an object in multiple threads is not thread-safe by default. The RefCount::use_locks member is passed a bool value to turn locking on and off on a per object basis.

Customizing the Reference Counting Package

The behaviour of the package can be modified at compile time with the following five macros, each of which should be undefined, 0, or 1:

REF_CHECK_STACK
If this is 1, referenced objects are checked to see if they reside on the stack, in which case storage for the object is not managed, if management is enabled.
REF_MANAGE
If this is 1, the unmanage member is enabled.
REF_CHECK_MAX_NREF
If this is 1, the reference count is checked before it is incremented to make sure it isn't too big.
REF_CHECK_MIN_NREF
If this is 1, the reference count is checked before it is decremented to make sure it isn't already zero.
REF_USE_LOCKS
If this is 1, modification of the reference count is locked to allow thread-safe execution.

If a macro is undefined, then the behaviour is architecture dependent—usually, the macro will be set to 1 in this case. For maximum efficiency and for normal operation after the program is debugged, compile with all of the above macros defined to zero. This can also be done by defining REF_OPTIMIZE.

An include file can be used to set these options as well. This has the advantage that dependency checking will force an automatic recompile of all affected files if the options change. This is done in the file mpqc_config.h, which is produced by the automated configuration procedure.

Note that all source code that uses references must be compiled with the same value for REF_MANAGE. Changing this can change the storage layout and the interpretation of the reference count data.

A Reference Example

Following is a simple example of how to manage memory with reference counts.

#include <util/container/ref.h>
class A: virtual public RefCount {};
class B: public A {};
int
main()
{
  Ref<A> a1(new A);
  Ref<A> a2;
  // Create another reference to the A object pointed to by a1.
  a2 = a1;
  // Make a2 refer to a new A object.
  a2 = new A;
  // a2 was the only reference to the second A object, so setting
  // a2 to the null object will cause the second A object to be
  // deleted.
  a2 = 0;
  Ref<B> b(new B);
  // An object of type Ref<X> can be assigned to an object of type
  // Ref<Y> as long as X* can be assigned to Y*.
  a1 = b;
  // An automatic dynamic cast can be done by using the left shift
  // operator.
  b << a1;
  // The B object will be deleted here because all of the references
  // to it go out of scope and destroyed.
  return 0;
}

Generated at Sun Jan 26 2020 23:24:02 for MPQC 3.0.0-alpha using the documentation package Doxygen 1.8.16.