32 PSpace implementation

This describes the implementation of the PSpace.

Member variables

$adt+ PSpace
{
    PersistStoreTxn* txn_;
    PersistStore& m_ps;
    ptr<IAsyncBindRequestHandler> asyncBindRequestHandler_;
    PSpaceRoots m_roots;
    DeletionQueue m_dq;
    xstring m_name;
    ROT rot_;
    bool m_useIncrementalOidAllocations;
    bool createdNew_;
    OidHigh m_oidHighForOidAllocations;
    SeidLow m_affiliateOidLow;
    bool m_ensureCommitted;
    PSpaceDos m_dos;
    CSpace* m_cspace;
    PSpace* m_nextInTxnGrp;
    bool m_inGrp;
    mDoubleLinkedListElementPtrs(DirtyPSpaceList,PSpace)
    mDoubleLinkedListElementPtrs(NedqList,PSpace)
};
m_name The name of the PSpace (a non-empty UTF-8 string). The PSpaces in a PersistStore are uniquely identified by their name.
asyncBindRequestHandler_ Allows for notification if/when attempt to load the object from the LSS fails because no serial with the given oid exists. May be null. Protected by the CSpace mutex
m_useIncrementalOidAllocations If set then allocate oids from the LSS with calls to AllocateSeid(). Otherwise use calls to AllocateAffiliateSeid().
m_ensureCommitted Used by the DosWriter to help ensure that all created PSpaces are committed. A PSpace can only be created using a PersistStoreTxn. The transaction is committed when the associated group of PSpaces are processed by the DosWriter. All the PSpace changes are written atomically to the LSS using a single LSS transaction. This includes writing the PSpaceMap (if it's dirty). It is important to only persist the PSpace map entries that have been committed by the transaction. This flag is initially false, and simply ensures that for every PSpace a single call is made to the PSpaceMap to commit the PSpace if it hasn't been committed already, at the time the group of dirty PSpaces are being processed by the DosWriter.
m_cspace The CSpace associated with this PSpace
m_nextInTxnGrp Used to allow PSpaces to be partitioned into groups. A group must have all the dirty objects written to the LSS in a single transaction.
m_inGrp Used to support an efficient implementation of PSpaceTxnGroupMgr::RetrieveGroup(). Normally false.
m_affiliateOidLow Low 32 bits of OID for the purpose of allocating affiliate OIDs. This is set when an object in the PSpace is marked as dirty. It is assumed that new objects found by tracing from the object marked as dirty should be affiliated with that object. Note that m_affiliateOidLow is only read or written by a thread that has exclusive locked the CSpace associated with this PSpace.
ROT Each PSpace employs a Resident Object Table (ROT) to keep track of the set of persistent objects that are currently resident in memory. When binding an OID to an object in memory the ROT is consulted. This ensures there is only one rendition of an object in memory for a given OID.
DOS Each PSpace employs a Dirty Object Set (DOS) to keep track of the set of persistent objects that have been marked as dirty and therefore need to be written to disk. All dirty objects must be written to disk using a single LSS transaction to ensure atomicity.

Associated CSpace

When a PSpace is created or opened, it is necessary to create the associated CSpace. This thread is affiliated with the PSpace/CSpace.

Oid allocations

There is no special relationship between OIDs and PSpaces - i.e. objects can be easily (and cheaply) migrated across PSpace boundaries. Nevertheless, a PSpace stores a member m_affiliateOidLow used for OID allocations. This is only used to help cluster related OIDs.

The high part of the OID for OID allocations (the OidHigh) is stored in the PersistStore and is used by all PSpaces. This allows different computers to have completely independent OID spaces. We assume that all OIDs allocated by the process use the same OidHigh, until either that space becomes full or else there was a non-graceful shutdown.

OID clustering is based on the OID lows. When MarkPersistableAsDirty() is called on a persistent object, m_affiliateOidLow is set so that when a local VisitObjects trace is subsequently performed to find new objects, OIDs will be allocated that cluster with the original object that was marked as dirty.

Note that the LSS provides threadsafe OID allocation functions.

Anonymous PSpaces

An anonymous PSpace is useful because it is automatically deleted when the PersistStore is next opened.

Thread safety of the PSpace roots

The roots are accessed as follows:-

  • A thread with shared read access to the CSpace calls PSpace::GetRoot()
  • A thread with exclusive write access to the CSpace calls PSpace::InsertRoot() or PSpace::RemoveRoot()
  • The GC thread with shared read access to the CSpace calls PSpace::VisitObjects()
  • PSpace::WritePSpaceIfDirty() is called. This is only called by PSpaceDos::WriteChangesToLSS() which is only called by PSpace::WriteDirtyObjectsToLss() which is only called by the DosWriter after it has obtained a shared read lock on the CSpace.
  • PSpaceMap::OpenPSpace() opens existing PSpace and calls PSpace::Serialise() to deserialise the PSpace

Note that PSpace::Serialise() is only called from two places. PSpace::WritePSpaceIfDirty() or PSpaceMap::OpenPSpace().

We conclude that the PSpace roots are protected correctly by the CSpace mutex.

Links

PSpace.h

Source: Ceda/cxPersistStore/src/PSpace.h

PSpace.cpp

Source: Ceda/cxPersistStore/src/PSpace.cpp

Old comment on PersistOperationCallbacks.h

[These comments are no longer relevant. PersistOperationCallbacks is no longer needed, instead PSpace implements IOperationCallbacks]

Provides an implementation of IOperationCallBacks that deals with persistent objects appropriately. For example, ensures that the containing object is marked as dirty as required. Also ensures that OIDs are allocated as required and subtrees are async deleted correctly.

Note that this implementation assumes that the current PSpace and CSpace have both been set in thread local storage at the time the IOperationCallbacks are received. It would be possible to relax this assumption because it is possible to navigate efficiently from an IObject to its CSpace, and from its CSpace to its PSpace (using the user-defined-buffer area of the CSpace). This is basically 2 indirections so very fast. However there is no great reason to do it yet because so much of the existing code deals with thread local storage. E.g. MarkAsDirty() on a IPersistable object!

This is "installed" into ThreadLocalStorage using DeclareThreadPtr within a PersistStoreTxn. Therefore by default all users of a PersistStore have these callbacks installed without any extra effort by the programmer.