|
|
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1991 - 2000.
//
// File: PropStor.hxx
//
// Contents: Persistent property store (external to docfile)
//
// Classes: CPropertyStore
//
// History: 27-Dec-19 KyleP Created
//
//----------------------------------------------------------------------------
#if !defined( __PROPSTOR_HXX__ )
#define __PROPSTOR_HXX__
#include <prcstob.hxx>
#include <phystr.hxx>
#include <readwrit.hxx>
#include <proplock.hxx>
#include <rcstxact.hxx>
class CiStorage; class COnDiskPropertyRecord; class CBorrowed; class CPropRecord;
//+-------------------------------------------------------------------------
//
// Class: CPropDesc
//
// Purpose: Description of metadata for a single property
//
// History: 27-Dec-95 KyleP Created
//
//--------------------------------------------------------------------------
class CPropDesc { public:
CPropDesc() { _pid = pidInvalid; }
void Init( PROPID pid, ULONG vt, DWORD oStart, DWORD cbMax, DWORD ordinal, DWORD rec ) { _pid = pid; _vt = vt; _oStart = oStart; _cbMax = cbMax; _ordinal = ordinal; _mask = 1 << ((ordinal % 16) * 2); _rec = rec; }
PROPID Pid() const { return _pid; } ULONG Type() const { return _vt; } DWORD Offset() const { return _oStart; } DWORD Size() const { return _cbMax; } DWORD Ordinal() const { return _ordinal; } DWORD Mask() const { return _mask; } DWORD Record() const { return _rec; }
BOOL IsInUse() const { return (_pid != pidInvalid); } void Free() { _pid = pidInvalid - 1; } BOOL IsFree() const { return (_pid == (pidInvalid - 1) || _pid == pidInvalid); } BOOL IsFixedSize() const { return (_oStart != 0xFFFFFFFF); }
void SetOrdinal( DWORD ordinal ) { _ordinal = ordinal; _mask = 1 << ((ordinal % 16) * 2); } void SetOffset( DWORD oStart ) { _oStart = oStart; } void SetRecord( DWORD rec ) { _rec = rec; }
private:
PROPID _pid; // Propid
ULONG _vt; // Data type (fixed types only)
DWORD _oStart; // Offset in fixed area to property (fixed types only)
DWORD _cbMax; // Max size of property (used to compute record size)
DWORD _ordinal; // Position of property in record. Zero based.
DWORD _mask; // 1 << Ordinal. Stored for efficiency
DWORD _rec; // Position of metadata object in metadata stream.
};
//+-------------------------------------------------------------------------
//
// Class: CPropStoreInfo
//
// Purpose: Global persistent state for property store
//
// History: 27-Dec-95 KyleP Created
//
//--------------------------------------------------------------------------
class CPropStoreInfo : INHERIT_UNWIND { INLINE_UNWIND( CPropStoreInfo ) public:
//
// Constructors and destructor
//
CPropStoreInfo();
CPropStoreInfo( CPropStoreInfo const & psi );
~CPropStoreInfo();
void Init( XPtr<PRcovStorageObj> & xObj ); void Empty();
//
// Global metadata
//
WORKID WorkId() { return _info.widStream; } WORKID NextWorkId(CiStorage & storage);
WORKID MaxWorkId() const { return _info.widMax; } WORKID FreeListHead() const { return _info.widFreeHead; } WORKID FreeListTail() const { return _info.widFreeTail; } ULONG RecordSize() const { return _info.culRecord; } ULONG RecordsPerPage() const { return _cRecPerPage; } ULONG FixedRecordSize() const { return _info.culFixed; } ULONG CountProps() const { return _info.cTotal; } ULONG CountFixedProps() const { return _info.cFixed; } BOOL IsDirty() const { return _info.fDirty; }
inline void MarkDirty(); inline void MarkClean();
void SetMaxWorkId( WORKID wid ) { _info.widMax = wid; MarkDirty(); } void SetFreeListHead( WORKID wid ) { _info.widFreeHead = wid; MarkDirty(); } void SetFreeListTail( WORKID wid ) { _info.widFreeTail = wid; MarkDirty(); }
ULONG CountRecordsInUse() const { return _info.cTopLevel; } void IncRecordsInUse() { _info.cTopLevel++; } void DecRecordsInUse() { _info.cTopLevel--; } void SetRecordsInUse( ULONG count ) { _info.cTopLevel = count; }
//
// Per-property metadata
//
inline BOOL CanStore( PROPID pid );
inline unsigned Size( PROPID pid );
inline ULONG Type( PROPID pid );
inline CPropDesc const * GetDescription( PROPID pid );
inline CPropDesc const * GetDescriptionByOrdinal( ULONG ordinal );
BOOL Add( PROPID pid, ULONG vt, unsigned cbMaxLen, CiStorage & storage );
BOOL Delete( PROPID pid, CiStorage & storage );
void Commit( CPropStoreInfo & psi, CRcovStrmWriteTrans & xact );
PRcovStorageObj * GetRcovObj() { return _prsoPropStore; }
private:
void ChangeDirty( BOOL fDirty ); unsigned Lookup( PROPID pid ); unsigned LookupNew( PROPID pid );
struct SPropInfo { ULONG Version; ULONG culRecord; ULONG culFixed; BOOL fDirty; ULONG cTotal; ULONG cFixed; ULONG cHash; WORKID widStream; WORKID widMax; WORKID widFreeHead; ULONG cTopLevel; WORKID widFreeTail; };
ULONG _cRecPerPage; // # records per 64K page
SPropInfo _info; // Non-repeated info, stored in header
PRcovStorageObj * _prsoPropStore; // The persistent storage itself
BOOL _fOwned; // Set to TRUE if propstore is owned.
XArray<CPropDesc> _aProp;
};
//+-------------------------------------------------------------------------
//
// Class: CPhysPropertyStore
//
// Purpose: Persistent property store
//
// History: 27-Dec-95 KyleP Created
//
//--------------------------------------------------------------------------
class CPhysPropertyStore : public CPhysStorage { INLINE_UNWIND( CPhysPropertyStore ); public:
inline CPhysPropertyStore( PStorage & storage, PStorageObject& obj, WORKID objectId, PMmStream * stream, PStorage::EOpenMode mode ); private:
virtual void ReOpenStream();
int _dummy; };
//+-------------------------------------------------------------------------
//
// Class: CPropertyStore
//
// Purpose: Persistent property store
//
// History: 27-Dec-95 KyleP Created
//
//--------------------------------------------------------------------------
class CPropertyStore : INHERIT_UNWIND { INLINE_UNWIND( CPropertyStore ) public:
//
// Two phase construction (to accomadate late-bound storage)
//
CPropertyStore();
~CPropertyStore();
void FastInit( CiStorage * pStorage ); void LongInit( BOOL & fWasDirty, ULONG & cInconsistencies ); BOOL IsDirty() const { return _PropStoreInfo.IsDirty(); }
void Empty();
//
// Schema manipulation
//
inline BOOL CanStore( PROPID pid );
inline unsigned Size( PROPID pid );
inline ULONG Type( PROPID pid );
ULONG BeginTransaction();
void Setup( PROPID pid, ULONG vt, DWORD cbMaxLen, ULONG ulToken );
void EndTransaction( ULONG ulToken, BOOL fCommit, PROPID pidFixed );
//
// Property storage/retrieval.
//
BOOL WriteProperty( WORKID wid, PROPID pid, CStorageVariant const & var );
inline WORKID WritePropertyInNewRecord( PROPID pid, CStorageVariant const & var );
BOOL ReadProperty( WORKID wid, PROPID pid, PROPVARIANT * pbData, unsigned * pcb);
BOOL ReadProperty( WORKID wid, PROPID pid, PROPVARIANT & var, BYTE * pbExtra, unsigned * pcbExtra );
BOOL ReadProperty( CPropRecord & PropRecord, PROPID pid, PROPVARIANT & var, BYTE * pbExtra, unsigned * pcbExtra );
BOOL ReadProperty( CPropRecord & PropRecord, PROPID pid, PROPVARIANT * pbData, unsigned * pcb);
BOOL ReadProperty( WORKID wid, PROPID pid, PROPVARIANT & var );
CPropRecord * OpenRecord( WORKID wid, BYTE * pb );
void CloseRecord( CPropRecord * pRec );
//
// Special path/wid support
//
inline WORKID MaxWorkId(); inline ULONG RecordsPerPage() const { return _PropStoreInfo.RecordsPerPage(); }
void DeleteRecord( WORKID wid );
inline ULONG CountRecordsInUse() const;
void Shutdown() { _fAbort = TRUE; }
void Flush();
private:
friend class CPropertyStoreWids; friend class CLockRecordForRead; friend class CLockRecordForWrite; friend class CPropRecord; friend class CPropertyStoreRecovery;
CPropertyStore( CPropertyStore & psi );
WORKID CreateStorage();
//
// Record locking.
//
void AcquireRead( CPropertyLockRecord & record ); void SyncRead( CPropertyLockRecord & record, BOOL fDecrementRead = FALSE ); void ReleaseRead( CPropertyLockRecord & record );
void AcquireWrite( CPropertyLockRecord & record ); void ReleaseWrite( CPropertyLockRecord & record );
CPropertyLockMgr & LockMgr() { return _lockMgr; }
CReadWriteAccess & GetReadWriteAccess() { return _rwAccess; }
void InitNewRecord( WORKID wid, ULONG cWid, COnDiskPropertyRecord * prec, BOOL fTopLevel );
void InitFreeList( );
void RecycleFreeList( WORKID wid );
void LokFreeRecord( WORKID wid, ULONG cFree, COnDiskPropertyRecord * prec );
WORKID LokAllocRecord( ULONG cFree );
void WritePropertyInSpecificNewRecord( WORKID wid, PROPID pid, CStorageVariant const & var );
WORKID NewWorkId( ULONG cWid ) { CLock lock( _mtxWrite );
return LokNewWorkId( cWid, TRUE ); }
WORKID LokNewWorkId( ULONG cWid, BOOL fTopLevel );
void Transfer( CPropertyStore & Target, PROPID pidFixed );
CiStorage * _pStorage; // Persistent storage object.
CPropStoreInfo _PropStoreInfo; // Global persistent state for property store.
WORKID * _aFreeBlocks; // Pointers into free list by rec. size
BOOL _fAbort; // Set to TRUE when aborting
BOOL _fIsConsistent; // Set to TRUE as long as the data is
// consistent
//
// Record locking
//
CMutexSem _mtxWrite; // Taken to add/remove/modify records
CMutexSem _mtxRW; // Sometimes used during per-record locking
CEventSem _evtRead; // Sometimes used during per-record locking
CEventSem _evtWrite; // Sometimes used during per-record locking
XPtr<CPhysPropertyStore> _xPhysStore; // Main data stream.
CPropertyLockMgr _lockMgr; // arbitrates record-level locking
CReadWriteAccess _rwAccess; // Controls access to resources
//
// For multi-property changes to metadata
//
CPropertyStore * _ppsNew; // New metadata stored here
BOOL _fNew; // TRUE if something really changed
#if CIDBG==1
_int64 _sigPSDebug;
DWORD _tidReadSet; DWORD _tidReadReset; DWORD _tidWriteSet; DWORD _tidWriteReset;
void _SetReadTid() { _tidReadSet = GetCurrentThreadId(); }
void _ReSetReadTid() { _tidReadReset = GetCurrentThreadId(); }
void _SetWriteTid() { _tidWriteSet = GetCurrentThreadId(); }
void _ReSetWriteTid() { _tidWriteReset = GetCurrentThreadId(); }
//
// Deadlock detection for the first 256 threads.
//
enum { cTrackThreads = 256 };
long * _aPerThreadReadCount; long * _aPerThreadWriteCount;
#else
void _SetReadTid() { } void _ReSetReadTid() { } void _SetWriteTid() { } void _ReSetWriteTid() { }
#endif // CIDBG==1
};
//+---------------------------------------------------------------------------
//
// Member: CPropStoreInfo::CanStore, public
//
// Arguments: [pid] -- Propid to check.
//
// Returns: TRUE if [pid] can exist in property store (e.g. has been
// registered).
//
// History: 27-Dec-95 KyleP Created.
//
//----------------------------------------------------------------------------
inline BOOL CPropStoreInfo::CanStore( PROPID pid ) { return (0 != _info.cHash) && _aProp[Lookup(pid)].IsInUse(); }
//+---------------------------------------------------------------------------
//
// Member: CPropStoreInfo::Size, public
//
// Synopsis: Returns size in cache for this property.
//
// Arguments: [pid] -- Propid to check.
//
// Returns: TRUE if [pid] can exist in property store (e.g. has been
// registered).
//
// History: 27-Dec-95 KyleP Created.
//
//----------------------------------------------------------------------------
inline unsigned CPropStoreInfo::Size( PROPID pid ) { if (0 == _info.cHash) return 0;
unsigned hash = Lookup(pid);
if ( _aProp[hash].IsInUse() ) return _aProp[hash].Size(); else return 0; }
//+---------------------------------------------------------------------------
//
// Member: CPropStoreInfo::Type, public
//
// Synopsis: Returns type in cache for this property.
//
// Arguments: [pid] -- Propid to check.
//
// Returns: TRUE if [pid] can exist in property store (e.g. has been
// registered).
//
// History: 27-Dec-95 KyleP Created.
//
//----------------------------------------------------------------------------
inline ULONG CPropStoreInfo::Type( PROPID pid ) { if (0 == _info.cHash) return VT_EMPTY;
unsigned hash = Lookup(pid);
if ( _aProp[hash].IsInUse() ) return _aProp[hash].Type(); else return VT_EMPTY; }
void CPropStoreInfo::MarkDirty() { if ( !_info.fDirty ) ChangeDirty( TRUE ); }
void CPropStoreInfo::MarkClean() { if ( _info.fDirty ) ChangeDirty( FALSE ); }
inline CPhysPropertyStore::CPhysPropertyStore( PStorage & storage, PStorageObject& obj, WORKID objectId, PMmStream * stream, PStorage::EOpenMode mode ) : CPhysStorage( storage, obj, objectId, stream, mode, TheUserCiParams.GetPropertyStoreMappedCache() ) { END_CONSTRUCTION( CPhysPropertyStore ); }
//+---------------------------------------------------------------------------
//
// Member: CPropertyStore::CanStore, public
//
// Arguments: [pid] -- Propid to check.
//
// Returns: TRUE if [pid] can exist in property store (e.g. has been
// registered).
//
// History: 27-Dec-95 KyleP Created.
//
//----------------------------------------------------------------------------
inline BOOL CPropertyStore::CanStore( PROPID pid ) { return _PropStoreInfo.CanStore( pid ); }
//+---------------------------------------------------------------------------
//
// Member: CPropertyStore::Size, public
//
// Arguments: [pid] -- Propid to check.
//
// Returns: Size of property in store, or 0 if it isn't in store.
//
// History: 27-Dec-95 KyleP Created.
//
//----------------------------------------------------------------------------
inline unsigned CPropertyStore::Size( PROPID pid ) { return _PropStoreInfo.Size( pid ); }
//+---------------------------------------------------------------------------
//
// Member: CPropertyStore::Type, public
//
// Arguments: [pid] -- Propid to check.
//
// Returns: Type of property in store, or VT_EMPTY if it isn't in store.
//
// History: 27-Dec-95 KyleP Created.
//
//----------------------------------------------------------------------------
inline ULONG CPropertyStore::Type( PROPID pid ) { return _PropStoreInfo.Type( pid ); }
//+---------------------------------------------------------------------------
//
// Member: CPropertyStore::WritePropertyInNewRecord, public
//
// Synopsis: Like WriteProperty, but also allocates record.
//
// Arguments: [pid] -- Propid to write.
// [var] -- Property value
//
// Returns: Workid of new record.
//
// History: 27-Dec-95 KyleP Created.
//
//----------------------------------------------------------------------------
inline WORKID CPropertyStore::WritePropertyInNewRecord( PROPID pid, CStorageVariant const & var ) { CLock lock( _mtxWrite );
WORKID wid = NewWorkId( 1 );
ciDebugOut(( DEB_PROPSTORE, "New short record at %d\n", wid ));
WriteProperty( wid, pid, var );
_PropStoreInfo.IncRecordsInUse();
return wid; }
//+---------------------------------------------------------------------------
//
// Member: CPropertyStore::CountRecordsInUse, public
//
// Returns: Count of 'top level' records (correspond to user wids)
//
// History: 15-Feb-96 KyleP Created.
//
//----------------------------------------------------------------------------
inline ULONG CPropertyStore::CountRecordsInUse() const { return _PropStoreInfo.CountRecordsInUse(); }
//+---------------------------------------------------------------------------
//
// Member: CPropertyStore::MaxWorkId, public
//
// Returns: Maximum workid which has been allocated.
//
// History: 28-Dec-95 KyleP Created.
//
//----------------------------------------------------------------------------
inline WORKID CPropertyStore::MaxWorkId() { return _PropStoreInfo.MaxWorkId(); }
//+---------------------------------------------------------------------------
//
// Member: CPropertyStoreInfo::GetDescription, public
//
// Arguments: [pid] -- Propid to check.
//
// Returns: Metadata descriptor for specified property.
//
// History: 28-Dec-95 KyleP Created.
//
//----------------------------------------------------------------------------
inline CPropDesc const * CPropStoreInfo::GetDescription( PROPID pid ) { if (0 == _info.cHash) return 0;
unsigned hash = Lookup( pid );
if ( _aProp[hash].IsInUse() ) return &_aProp[hash]; else return 0; }
//+---------------------------------------------------------------------------
//
// Member: CPropertyStoreInfo::GetDescriptionByOrdinal, public
//
// Arguments: [ordinal] -- Ordinal
//
// Returns: Metadata descriptor for specified property.
//
// History: 16-Jan-96 KyleP Created.
//
//----------------------------------------------------------------------------
inline CPropDesc const * CPropStoreInfo::GetDescriptionByOrdinal( ULONG ordinal ) { Win4Assert( 0 != _info.cHash );
for ( unsigned i = 0; i < _aProp.Count(); i++ ) { if ( _aProp[i].Pid() != pidInvalid && _aProp[i].Ordinal() == ordinal ) return &_aProp[i]; }
return 0; }
//+---------------------------------------------------------------------------
//
// Class: CPropertyStoreRecovery
//
// Purpose: Recovers the property store from a dirty shutdown
//
// History: 4-10-96 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
class CPropertyStoreRecovery : INHERIT_UNWIND { INLINE_UNWIND( CPropertyStoreRecovery )
public:
CPropertyStoreRecovery( CPropertyStore & propStore ); ~CPropertyStoreRecovery( );
void DoRecovery(); ULONG GetInconsistencyCount() const { return _cInconsistencies; }
private:
void _Pass1(); void _Pass2();
void _Complete(); void _SetFree();
BOOL _CheckOverflowChain(); void _FreeChain();
WORKID AddToFreeList( WORKID widFree, ULONG cFree, COnDiskPropertyRecord * precFree, WORKID widListHead );
CPropertyStore & _propStore; CPropStoreInfo & _PropStoreInfo;
CPhysPropertyStore * _pPhysStore;
WORKID _wid; // Wid being processed currently
COnDiskPropertyRecord * _pRec; // Pointer to _wid's on disk rec
WORKID _cRec; // Count of records for this wid
//
// Cumulative values during recovery.
//
WORKID * _aFreeBlocks; // Array of free lists by size
WORKID _widMax; // Current wid max
ULONG _cTopLevel; // Total top level records
ULONG _cRecPerPage; // Records per long page
ULONG _cInconsistencies; // Number of inconsistencies.
ULONG _cForceFreed; // Number of records forecefully
// freed.
};
#endif // __PROPSTOR_HXX__
|