37 DirtyPSpaceList
The dirty PSpaces form a double linked list, made threadsafe with a std::mutex.
Each PSpace directly stores pointers to the previous and next PSpaces in the list.
This allows a PSpace to very quickly remove itself from the double linked list.
When a PSpace first transitions to dirty it is added to the list with a call to PushBack().
When a PSpace is cleaned by writing the changes to the LSS it is removed from the list with a call to
Remove().
DirtyPSpaceList is a member of the DosWriter.
ProcessAllGroups() is used to process all the PSpace groups.
This is done carefully to avoid getting stuck processing the same PSpaces over and over again.
Note that a naive approach which pops dirty PSpaces until the queue is empty could take a
long time if PSpaces are being pushed back onto the queue as fast as they are being removed.
This implementation retrieves a snapshot of the list of dirty PSpaces then processes all PSpaces
in the list.
Note that this implementation locks the mutex for a long time, during which no
PSpaces can be closed [a thread trying to close a PSpace will block in the call to
DosWriter::OnClosePSpace()].
class DosWriter
{
void ProcessAllGroups(bool flushToDisk)
{
std::lock_guard<std::mutex> lock(mutex_);
xdeque<PSpace*> L;
m_dirtyPSpaces.CopyIntoDeque(L);
// Note that a PSpace cannot be closed while iterating through this deque.
for (auto i : L)
{
PrivateProcessPSpaceTxnGroup(*i,flushToDisk);
}
}
// Double linked list of PSpaces that have dirty objects
DirtyPSpaceList m_dirtyPSpaces;
};
class PSpaceDos
{
void MarkAsDirty() const
{
if (!m_dirty)
{
m_dirty = true;
m_pspace.GetPersistStore()->m_dosWriter.m_dirtyPSpaces.PushBack(&m_pspace);
}
}
void PSpaceDos::WriteChangesToLSS(ILssTransaction& txn)
{
for (auto& i : m_dirtyObjectSet)
{
ptr<const IPersistable> po = i;
OID oid = GetOid(po);
if (GetIObjectFlag(po,DBP_PO_SYNC_DELETED))
{
txn.DeleteSerialElement(oid);
}
else
{
AutoCloser<ICloseableOutputStream> stream(txn.WriteSerialElement(oid));
{
Archive ar(stream.Get());
m_pspace.GetPersistStore()->DynCreateSerialise(ar,po);
}
}
ClearPersistableDirtyFlag(po);
}
m_dirtyObjectSet.clear();
m_dirty = false;
m_pspace.GetPersistStore()->m_dosWriter.m_dirtyPSpaces.Remove(&m_pspace);
}
mutable std::atomic<bool> m_dirty;
};
Implementation
The implementation uses DoubleLinkedList.
Note that mDefineDoubleLinkedList has a flag for whether the double linked list is protected by a
std::mutex.
mDefineDoubleLinkedList(Name,Element,traceFlag,bool threadSafe,bool copyDeque)
This requires that PSpace use mDoubleLinkedListElementPtrs() to declare the prev/next pointers.
$adt+ implement PSpace
{
// The dirty PSpaces form a double linked list.
mDoubleLinkedListElementPtrs(DirtyPSpaceList,PSpace)
};
DirtyPSpaceList.h
Source: Ceda/cxPersistStore/src/DirtyPSpaceList.h
DirtyPSpaceList.cpp
Source: Ceda/cxPersistStore/src/DirtyPSpaceList.cpp
Proposal 1 to simplify
Rather than have a double linked list of dirty PSpaces, consider instead that we simply iterate through
all the open PSpaces using the PSpaceMap and check whether they are dirty using the
PSpaceDos dirty flag.
We don't expect to have large numbers of PSpaces in a given PersistStore, so in practise this solution
is efficient.
Proposal 2 to simplify
Rather than have a double linked list of dirty PSpaces the DosWriter has a std::vector<PSpace*>
protected with a std::mutex. As PSpaces become dirty they are pushed onto the vector.
The PSpaceDos dirty flag is cleared when ProcessAllGroups() swaps the vector with an empty one.