Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

773 lines
21 KiB

//+---------------------------------------------------------------------------
//
// 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__