IPersistStore.h
// IPersistStore.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2006
@import "cxPersistStore.h"
@import "IPersistable.h"
@import "IPrefVisitor.h"
@import "TypeInfo.h"
@import "Ceda/cxObject/WCSpace.h"
@import "Ceda/cxObject/ThreadPtr.h"
#include "Ceda/cxLss/LssSettings.h"
#include "Ceda/cxUtils/CedaAssert.h"
#include "Ceda/cxUtils/AutoCloser.h"
#include "Ceda/cxUtils/OpenMode.h"
namespace ceda
{
/*
External interface to clients of the persistent store
-----------------------------------------------------
We will try to make the public API as simple as possible, and see where it leads...
* Clients can't tell whether an IPersistable is dirty.
* Clients shouldn't generally be interested in the OID. [But hard to avoid this]
* No persistence GC. So clients must explicitly delete POs
* Clients can't tell what objects are currently resident
* When a PO is marked as dirty, can't find the set of new POs that were found by the trace.
* No support for GOIDs
* prefs are never allowed to dangle.
IDEA: When an operation is created, the operation can more directly find the objects in the
assembly that are being inserted. Therefore there is no need for clients to be able to see the
results of a trace.
*/
// PO = Persistent object
$interface IPersistable;
$adt PersistStore;
$adt PSpace;
$adt PersistStoreTxn;
struct ILogStructuredStore;
struct ICloseableInputStream;
$extern+ ILogStructuredStore;
// Defined in cxLss. We repeat the definition in order to reflect it
$struct+ LssSettings <<extern>>
{
$int32 flushTimeMilliSec;
$float64 cleanerUtilisationPercent;
$bool enableFileBuffering;
$bool enableWriteThrough;
$int32 maxNumSegmentsInCache;
$int32 numSegmentsPerCheckPoint;
$int32 segmentSize;
$bool forceIncrementMSSN;
$bool validateSUTDuringCheckPoint;
};
// This is defined in cxUtils so it's not reflected. Unfortunately we need to repeat the
// declaration in order to reflect it.
$enum+ EOpenMode <<extern>>
{
OM_CREATE_NEW,
OM_CREATE_ALWAYS,
OM_OPEN_EXISTING,
OM_OPEN_EXISTING_READ_ONLY,
OM_OPEN_EXISTING_SHARED_READ,
OM_OPEN_ALWAYS,
OM_DELETE_EXISTING,
};
/*
The PersistStore creates exactly two special serial elements:
RootPersistStoreObject : oid = 00000001.00000000 (was 01010101.01010101)
PSpaceMap : oid = 00000001.00000001 (was 01010101.01010102)
*/
const ssize_t NUM_PERSISTSTORE_SERIAL_ELEMENTS = 2;
/*
Each PSpace creates exactly two special serial elements. E.g.
PSpaceRoots : oid = 00000001.00000002 (was 01010101.01010103)
DeletionQueue : oid = 00000001.00000003 (was 01010101.01010104)
*/
const ssize_t NUM_PSPACE_SERIAL_ELEMENTS = 2;
inline const ssize_t GetNumSerialElementsInStore(ssize_t numPSpaces, ssize_t numObjects)
{
return NUM_PERSISTSTORE_SERIAL_ELEMENTS + numPSpaces*NUM_PSPACE_SERIAL_ELEMENTS + numObjects;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
$struct+ ClassNameTranslation
{
ConstStringZ oldName;
ConstStringZ newName;
};
$struct+ ClassNameTranslationTable
{
const ClassNameTranslation* entries;
ssize_t count;
};
///////////////////////////////////////////////////////////////////////////////////////////////////
// PersistStore
$adt+ PersistStore
{
void Close();
bool CreatedNew() const;
ILogStructuredStore* GetLss();
void StartObjectDeletion();
void WriteAllDirtyObjectsToLss(bool flushToDisk);
int32 GetDosPeriod() const;
void SetDosPeriod(int32 time);
ssize_t GetNumPSpaces() const;
ssize_t GetNumPersistableObjects() const;
TypeOpsId GetTypeOpsId(const ReflectionByteCodeValue& rbcv, const TypeOps& typeOps);
BcTypeOps GetTypeOps(TypeOpsId tid);
void DisableWritesToDisk();
};
$struct+ PersistStoreSettings
{
PersistStoreSettings() :
deltasDirPath(nullptr),
translationTable(nullptr),
startObjectDeletion(true)
{
}
$ConstStringZ deltasDirPath;
$LssSettings lssSettings;
$ClassNameTranslationTable* translationTable;
$bool startObjectDeletion;
};
$function+ PersistStore* OpenPersistStore(
ConstStringZ lssPath,
EOpenMode openMode = OM_OPEN_ALWAYS,
const PersistStoreSettings& settings = PersistStoreSettings());
///////////////////////////////////////////////////////////////////////////////////////////////////
$enum+ EAsyncBindResult
{
Bound,
Pending,
DoesNotExist,
SerialElementCorupt,
DeserialisationError,
NotHandled
};
$model+ MAsyncBind
{
EAsyncBindResult result;
// not null if and only if result == EAsyncBindResult::Bound
ptr<IPersistable> po;
};
$interface+ IAsyncBindRequestHandler
{
MAsyncBind BeginAsyncBindRequest(OID oid);
void EndAsyncBindRequest(OID oid);
};
///////////////////////////////////////////////////////////////////////////////////////////////////
// PSpace
$adt+ PSpace
{
void Close();
bool CreatedNew() const;
CSpace* GetCSpace();
PersistStore* GetPersistStore();
ConstStringZ GetName() const;
void WriteDirtyObjectsToLss(bool flushToDisk);
void MarkPersistableAsDirtyWithoutTrace(ptr<const IPersistable> po);
///////////////////////////////////////////////////////////////////////////////////////////////////
// Bind to object using the oid
ptr<IPersistable> BindObject(OID oid);
ptr<IPersistable> BindObjectIfMemoryResident(OID oid);
ptr<IPersistable> TryBindObject(OID oid);
MAsyncBind AsyncBind2(OID oid);
ptr<IPersistable> AsyncBind(OID oid);
ptr<IAsyncBindRequestHandler> GetAsyncBindRequestHandler() const;
void SetAsyncBindRequestHandler(ptr<IAsyncBindRequestHandler> h);
void DeclareObjectDoesNotExist(OID oid);
///////////////////////////////////////////////////////////////////////////////////////////////////
// Allocating OIDs to new objects in the PSpace
void MakeObjectPersist(OidLow affiliateOidLow, ptr<IPersistable> po);
OID AllocateOid(OidHigh oidHigh, bool useIncrementalOidAllocations);
OidHigh GetOidHighForOidAllocations() const;
void SetOidHighForOidAllocations(OidHigh oidHigh, bool useIncrementalOidAllocations);
bool MakeObjectPersistWithGivenOID(ptr<IPersistable> po, OID oid, bool trace);
void DeclareReachable(ptr<const IPersistable> parent, ptr<const IPersistable> child, bool trace);
void MakeReachableObjectsPersist(ptr<IPersistable> po);
///////////////////////////////////////////////////////////////////////////////////////////////////
// Inserting and removing roots from a PSpace
ptr<IPersistable> GetRoot(ConstStringZ name);
bool AddRoot(ConstStringZ name, ptr<IPersistable> root);
bool RemoveRoot(ConstStringZ name);
///////////////////////////////////////////////////////////////////////////////////////////////////
// Deleting objects
void AsyncPermanentlyDeleteSubTree(ptr<IPersistable> obj);
void AsyncPermanentlyDeleteObject(ptr<IPersistable> obj);
void SyncPermanentlyDeleteSubTree(ptr<IPersistable> obj);
void SyncPermanentlyDeleteObject(ptr<IPersistable> obj);
};
// Returns nullptr if CSpace doesn't have an associated PSpace
$function+ PSpace* GetPSpaceOfCSpace(CSpace* cs);
// Get the PSpace associated with the given object, or else nullptr.
$function+ PSpace* GetAssociatedPSpace(ptr<const IObject> po);
$function+ inline PersistStore* GetAssociatedPersistStore(ptr<const IObject> po)
{
if (PSpace* ps = GetAssociatedPSpace(po))
{
return GetPersistStore(ps);
}
else
{
return NULL;
}
}
$function+ inline ILogStructuredStore* GetAssociatedLss(ptr<const IObject> po)
{
if (PSpace* pspace = GetAssociatedPSpace(po))
{
PersistStore* ps = GetPersistStore(pspace);
cxAssert(ps);
return GetLss(ps);
}
else
{
return nullptr;
}
}
$function+ inline bool ObjectsBelongToDifferentPSpaces(ptr<const IObject> po1, ptr<const IObject> po2)
{
if (PSpace* ps1 = GetAssociatedPSpace(po1))
{
if (PSpace* ps2 = GetAssociatedPSpace(po2))
{
return ps1 != ps2;
}
}
return false;
}
$function+ inline bool ObjectsBelongToDifferentPersistStores(ptr<const IObject> po1, ptr<const IObject> po2)
{
if (PersistStore* ps1 = GetAssociatedPersistStore(po1))
{
if (PersistStore* ps2 = GetAssociatedPersistStore(po2))
{
return ps1 != ps2;
}
}
return false;
}
// Open or create a PSpace with the given name on the given PersistStore
$function+ PSpace* OpenPSpace(PersistStore* ps, ConstStringZ name, EOpenMode openMode = OM_OPEN_ALWAYS, CSpace* cspace = nullptr);
$function+ void AsyncPermanentlyDeleteSubTree(ptr<IPersistable> obj);
$function+ void AsyncPermanentlyDeleteObject(ptr<IPersistable> obj);
$function+ void SyncPermanentlyDeleteSubTree(ptr<IPersistable> obj);
$function+ void SyncPermanentlyDeleteObject(ptr<IPersistable> obj);
///////////////////////////////////////////////////////////////////////////////////////////////////
// Thread affinity for a PSpace
mDefineThreadPtr(PSpace)
// Each of the following functions assumes the relevant PSpace is set in thread local storage
$function+ ptr<IPersistable> GetPSpaceRoot(ConstStringZ name);
$function+ bool AddPSpaceRoot(ConstStringZ name, ptr<IPersistable> root);
$function+ bool RemovePSpaceRoot(ConstStringZ name);
inline PersistStore* GetThreadPersistStore()
{
if (PSpace* pspace = GetThreadPtr<PSpace>())
{
PersistStore* ps = GetPersistStore(pspace);
cxAssert(ps);
return ps;
}
else
{
return nullptr;
}
}
$function+ inline void SetTlsForPSpace(PSpace* pspace)
{
cxAssert(pspace);
SetThreadPtr(pspace);
SetThreadPtr(GetCSpace(pspace));
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// DeclareThreadPSpace
class DeclareThreadPSpace
{
public:
DeclareThreadPSpace(PSpace* p = nullptr) { if (p) Set(p); }
static void Set(PSpace* p)
{
DeclareThreadPtr<PSpace>::Set(p);
DeclareThreadPtr<CSpace>::Set(GetCSpace(p));
}
private:
DeclareThreadPtr<PSpace> ps_;
DeclareThreadPtr<CSpace> cs_;
};
///////////////////////////////////////////////////////////////////////////////////////////////////
// WPSpace
// Can't use a typedef CloserAndSetterInTLS<PSpace> WPSpace; because DeclareThreadPSpace is not used
class WPSpace
{
cxNotCloneable(WPSpace)
public:
WPSpace(PSpace* pspace = nullptr) : m_pspace(pspace), m_dt(pspace) {}
void Set(PSpace* pspace)
{
m_pspace = pspace;
m_dt.Set(pspace);
}
~WPSpace()
{
Close(m_pspace);
}
operator PSpace*() const { return m_pspace; }
private:
PSpace* m_pspace;
DeclareThreadPSpace m_dt;
};
#ifdef CEDA_CHECK_ASSERTIONS
// The following functions assert:
// 1) there is a PSpace set in TLS
// 2) there is a CSpace set in TLS
// 3) The CSpace in TLS is associated with the PSpace in TLS
// Returns information about the kind of lock which is held on the CSpace.
@api bool HaveExclusiveLockedPSpace();
@api bool HaveSharedReadLockedPSpace();
@api bool HaveLockedPSpace();
// The following functions assert:
// 1) the given object is not null
// 2) the given object is registered in a CSpace which has an associated PSpace
// 3) the CSpace associated with the object is set in TLS
// 4) the PSpace associated with the object is set in TLS
@api bool HaveExclusiveLockedPSpace(ptr<const IPersistable> p);
@api bool HaveSharedReadLockedPSpace(ptr<const IPersistable> p);
@api bool HaveLockedPSpace(ptr<const IPersistable> p);
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////
// PermanentlyDeleteReachablePrefVisitor
/*
Invokes AsyncPermanentlyDeleteSubTree() on each visited pref.
*/
struct @api PermanentlyDeleteReachablePrefVisitor : public IPrefVisitor
{
PermanentlyDeleteReachablePrefVisitor(PSpace* pspace = GetThreadPtr<PSpace>()) : m_pspace(pspace) {}
// Calling these functions requires that the PSPace of the persistable pointed to by p
// has been affiliated with the calling thread.
virtual void VisitPref(const prefbase& p);
virtual void VisitParentPref(const prefbase& p) {}
PSpace* m_pspace;
};
///////////////////////////////////////////////////////////////////////////////////////////////////
// AllocateOidsReachablePrefVisitor
struct @api AllocateOidsReachablePrefVisitor : public IPrefVisitor
{
AllocateOidsReachablePrefVisitor(PSpace* pspace, OID affiliate)
: m_pspace(pspace),
m_affiliateOidLow(affiliate.low_)
{
cxAssert(pspace);
}
// Calling these functions requires that the PSPace of the persistable pointed to p
// has been affiliated with the calling thread.
virtual void VisitPref(const prefbase& p);
virtual void VisitParentPref(const prefbase& p) {}
PSpace* m_pspace;
SeidLow m_affiliateOidLow;
};
/*
DeclareReachableX(parent,child) is used to declare that child is reachable from parent. This
function is quite generic because the type of the child is templatised and the only assumption is
that operator<<() to a IPrefVisitor is defined.
*/
template<typename T>
void DeclareReachableX(ptr<const IPersistable> parent, const T& child)
{
cxAssert(parent);
if (OID oid = GetOid(parent))
{
PSpace* ps = GetAssociatedPSpace(parent);
cxAssert(ps);
AllocateOidsReachablePrefVisitor aor(ps,oid);
aor << child;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
@api void* BootstrapPSpaceRoot(const ReflectedClass& rc, ConstStringZ rootName);
template<typename T>
T* BootstrapPSpaceRoot(ConstStringZ rootName)
{
return (T*) BootstrapPSpaceRoot(GetReflectedClass<T>(), rootName);
}
@api void* BootstrapPSpaceRoot2(const ReflectedClass& rc, ConstStringZ rootName);
template<typename T>
T* BootstrapPSpaceRoot2(ConstStringZ rootName)
{
return (T*) BootstrapPSpaceRoot2(GetReflectedClass<T>(), rootName);
}
} // namespace ceda