prefs and crefs

A serial element means a list of octets.

A CEDA Log Structured Store (LSS) allows for persisting many arbitrary length serial elements identified by 64 bit Serial Element Identifiers (SEIDs) in a single file on non-volatile storage.

A PersistStore uses an LSS to allow objects in volatile memory to persist as it were.

This is achieved by serialising each object in memory as a list of octets - i.e. a serial element in the LSS, and which is uniquely identified by a SEID.

As such, where a SEID identifies an object we call the SEID an Object Identifier (OID)

The objects in a PersistStore are partitioned into mutually exclusive PSpaces (meaning that a persistent object belongs to exactly one PSpace).

Each object in memory which can persist in a serial element and be identified by an OID must implement the interface IPersistable.

Each PSpace has a Resident Object Table (ROT) which is a map from OID to a pointer in memory. The ROT records pointers to the objects in the PSpace that are currently resident in memory.

An object may have previously been serialised as a serial element in the LSS, but is not currently resident in memory.

There is a concept of binding an OID to obtain the address of the object in memory. This involves first consulting the ROT, and if not found then loading the serial element from the LSS, creating a default constructed object in memory of the appropriate type, and deserialising its state from the serial element.

A PSpace in a PersistStore contains a set of persistent objects that implement the interface IPersistable.

Persistent objects in memory can reference each other to form arbitrary graphs. This is achieved using member variables of type pref<T> or cref<T> which represent a typed pointer to persistent objects.

There are two kinds of persistent pointers because there are two kinds of transient pointers (T* and ptr<T>):

A pref<T> or cref<T> has two members:

There are three mutually exclusive cases:

In the third case, where the pref<T> is pointing at a persistent object, if the the ptr<IPersistable> member isn't null then of course that implies the object is resident in memory. However the converse is false. In other words, the object may be resident in memory and yet the ptr<IPersistable> member of the pref<T> is null.

pref<T>

Let T be a subinterface of IObject. A pref<T> is a smart pointer typically used within one persistent object to allow it to store a (typed) pointer to another persistent object.

Note that it is not required that T be a subinterface of IPersistable.

Naturally the referenced persistent object must implement IPersistable (as well as T).

A pref<T> can represent a null pointer. In fact the default constructor initialises a pref<T> to a null pointer.

The following code shows an example of a pref<T>

A pref<T> can be compared to any raw IObject ptr, or any pref<S> for any S. This will correctly perform object identity testing.

When a pref<T> is serialised to an Archive, the 64 bit OID is written (or a null OID if the pref<T> is a null pointer).

Typically, the memory pointer only binds to the object in memory when the pref<T> is dereferenced.

A pref<T> can be de-referenced, using operator*(). This performs the following steps

In the literature this is called "software swizzling".

A class that has pref<T> members must ensure it visits them in its implementation of VisitObjects() and VisitPrefs().

cref<T>

This is an alternative to a pref<T> that can be used when there is no need or desire to deal with a class indirectly through an interface. For example, this is used in the implementation of a PDeque to allow the PDeque pages to form a double linked list without any need to define or implement interfaces.

A cref<T> is actually implemented as a pref<IPersistable>. T is assumed to be the concrete class of the persistent object referenced by the cref<T>. The void* self pointer stored within the ptr<IPersistable> is assumed to be a pointer to a T. This assumption complies with the way interfaces are implemented.