//+--------------------------------------------------------------------------- // // 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 #include #include #include #include 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 & 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 _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 _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__