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