1612 lines
46 KiB
C++
1612 lines
46 KiB
C++
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Microsoft Corporation
|
||
|
// Copyright (C) Microsoft Corporation, 1992 - 1998.
|
||
|
//
|
||
|
// File: PhysIdx.CXX
|
||
|
//
|
||
|
// Contents: FAT Buffer/Index package
|
||
|
//
|
||
|
// Classes: CPhysBuffer -- Buffer
|
||
|
//
|
||
|
// History: 05-Mar-92 KyleP Created
|
||
|
// 07-Aug-92 KyleP Kernel implementation
|
||
|
// 21-Apr-93 BartoszM Rewrote to use memory mapped files
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
#include <pch.cxx>
|
||
|
#pragma hdrstop
|
||
|
|
||
|
#include <pstore.hxx>
|
||
|
#include <phystr.hxx>
|
||
|
#include <eventlog.hxx>
|
||
|
#include <psavtrak.hxx>
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: PageToLow
|
||
|
//
|
||
|
// Returns: Low part of byte count in cPage pages
|
||
|
//
|
||
|
// History: 21-Apr-93 BartoszM Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
inline ULONG PageToLow(ULONG cPage)
|
||
|
{
|
||
|
return(cPage << CI_PAGE_SHIFT);
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: PageToHigh
|
||
|
//
|
||
|
// Returns: High part of byte count in cPage pages
|
||
|
//
|
||
|
// History: 21-Apr-93 BartoszM Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
inline ULONG PageToHigh(ULONG cPage)
|
||
|
{
|
||
|
return(cPage >> (ULONG_BITS - CI_PAGE_SHIFT));
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: ToPages
|
||
|
//
|
||
|
// Returns: Number of pages equivalent to (cbLow, cbHigh) bytes
|
||
|
//
|
||
|
// History: 21-Apr-93 BartoszM Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
inline ULONG ToPages ( ULONG cbLow, ULONG cbHigh )
|
||
|
{
|
||
|
Win4Assert ( cbHigh >> CI_PAGE_SHIFT == 0 );
|
||
|
return (cbLow >> CI_PAGE_SHIFT) + (cbHigh << (ULONG_BITS - CI_PAGE_SHIFT));
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: PgCommonPgTrunc
|
||
|
//
|
||
|
// Returns: Number of pages truncated to multiple of largest common page
|
||
|
//
|
||
|
// History: 21-Apr-93 BartoszM Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
inline ULONG PgCommonPgTrunc(ULONG nPage)
|
||
|
{
|
||
|
return nPage & ~(PAGES_PER_COMMON_PAGE - 1);
|
||
|
}
|
||
|
|
||
|
inline ULONGLONG MAKEULONGLONG( ULONG l, ULONG h )
|
||
|
{
|
||
|
return ( l | ( h << 32 ) );
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: PgCommonPgRound
|
||
|
//
|
||
|
// Returns: Number of pages rounded to multiple of largest common page
|
||
|
//
|
||
|
// History: 21-Apr-93 BartoszM Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
inline ULONG PgCommonPgRound(ULONG nPage)
|
||
|
{
|
||
|
return (nPage + PAGES_PER_COMMON_PAGE - 1) & ~(PAGES_PER_COMMON_PAGE - 1);
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPhysBuffer::~CPhysBuffer
|
||
|
//
|
||
|
// History: 07-Dec-93 BartoszM Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
CPhysBuffer::~CPhysBuffer()
|
||
|
{
|
||
|
Win4Assert( !_fNeedToFlush );
|
||
|
|
||
|
#if CIDBG == 1
|
||
|
Flush( FALSE );
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPhysBuffer::CPhysBuffer, public
|
||
|
//
|
||
|
// Synopsis: Allocates virtual memory
|
||
|
//
|
||
|
// Arguments: [stream] -- stream to use
|
||
|
// [PageNo] -- page number
|
||
|
// [fWrite] -- for writable memory
|
||
|
// [fIntentToWrite] -- When fWrite is TRUE, whether the
|
||
|
// client intends to write to the memory so
|
||
|
// it needs to be flushed.
|
||
|
//
|
||
|
// History: 10-Mar-92 KyleP Created
|
||
|
// 21-Apr-93 BartoszM Rewrote to use memory mapped files
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
CPhysBuffer::CPhysBuffer(
|
||
|
PMmStream& stream,
|
||
|
ULONG PageNo,
|
||
|
BOOL fWrite,
|
||
|
BOOL fIntentToWrite,
|
||
|
BOOL fMap ) :
|
||
|
_cRef( 0 ),
|
||
|
_pphysNext( 0 ),
|
||
|
_fNeedToFlush( fIntentToWrite ),
|
||
|
_fWrite( fWrite ),
|
||
|
_fMap( fMap ),
|
||
|
_stream( stream )
|
||
|
{
|
||
|
_PageNo = PgCommonPgTrunc( PageNo );
|
||
|
|
||
|
if ( _fMap )
|
||
|
stream.Map( _strmBuf,
|
||
|
COMMON_PAGE_SIZE,
|
||
|
PageToLow( _PageNo ),
|
||
|
PageToHigh( _PageNo ),
|
||
|
fWrite );
|
||
|
else
|
||
|
{
|
||
|
DWORD cbRead;
|
||
|
|
||
|
_xBuffer.Init( COMMON_PAGE_SIZE );
|
||
|
_stream.Read( _xBuffer.Get(),
|
||
|
MAKEULONGLONG( PageToLow( _PageNo ),
|
||
|
PageToHigh( _PageNo ) ),
|
||
|
COMMON_PAGE_SIZE,
|
||
|
cbRead );
|
||
|
}
|
||
|
} //CPhysBuffer
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPhysBuffer::Flush, public
|
||
|
//
|
||
|
// Synopsis: Flushes the buffer to disk
|
||
|
//
|
||
|
// Arguments: [fFailFlush] -- If TRUE and flush fails, an exception is
|
||
|
// raised. Otherwise failures to flush are
|
||
|
// ignored.
|
||
|
//
|
||
|
// History: 10/30/98 dlee Moved out of header
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
void CPhysBuffer::Flush( BOOL fFailFlush )
|
||
|
{
|
||
|
if ( _fNeedToFlush )
|
||
|
{
|
||
|
Win4Assert( _fWrite );
|
||
|
|
||
|
//
|
||
|
// Reset the flag first, so subsequent attempts to flush won't
|
||
|
// fail if first attempt fails. This is so we don't throw
|
||
|
// in destructors. If we can't tolerate failures to flush, don't
|
||
|
// even bother trying to write the data if we can't fail.
|
||
|
//
|
||
|
|
||
|
_fNeedToFlush = FALSE;
|
||
|
|
||
|
if ( fFailFlush )
|
||
|
{
|
||
|
if ( _fMap )
|
||
|
{
|
||
|
// Flush the buffer and metadata in one call
|
||
|
|
||
|
_strmBuf.Flush( TRUE );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_stream.Write( _xBuffer.Get(),
|
||
|
MAKEULONGLONG( PageToLow( _PageNo ),
|
||
|
PageToHigh( _PageNo ) ),
|
||
|
COMMON_PAGE_SIZE );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ciDebugOut(( DEB_WARN, "not flushing at %#p, in failure path?\n",
|
||
|
this ));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if CIDBG == 1
|
||
|
|
||
|
//
|
||
|
// Make sure the bits on disk are the same as those in memory, since
|
||
|
// the buffer is marked as not needing to be flushed. Only do this if
|
||
|
// using read/write instead of mapping.
|
||
|
//
|
||
|
|
||
|
else if ( !_fMap && fFailFlush )
|
||
|
{
|
||
|
TRY
|
||
|
{
|
||
|
XArrayVirtual<BYTE> xTmp( COMMON_PAGE_SIZE );
|
||
|
|
||
|
DWORD cbRead;
|
||
|
_stream.Read( xTmp.Get(),
|
||
|
MAKEULONGLONG( PageToLow( _PageNo ),
|
||
|
PageToHigh( _PageNo ) ),
|
||
|
COMMON_PAGE_SIZE,
|
||
|
cbRead );
|
||
|
|
||
|
if ( 0 != memcmp( xTmp.Get(), _xBuffer.Get(), COMMON_PAGE_SIZE ) )
|
||
|
{
|
||
|
ciDebugOut(( DEB_ERROR, "read: %p, have: %p\n",
|
||
|
xTmp.Get(), _xBuffer.Get() ));
|
||
|
Win4Assert( !"writable non-flushed buffer was modified!" );
|
||
|
}
|
||
|
}
|
||
|
CATCH( CException, e )
|
||
|
{
|
||
|
// Ignore -- it's just an assert.
|
||
|
}
|
||
|
END_CATCH;
|
||
|
}
|
||
|
#endif // CIDBG == 1
|
||
|
|
||
|
} //Flush
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CBufferCacheIter::CBufferCacheIter, public
|
||
|
//
|
||
|
// Synopsis: Constructor
|
||
|
//
|
||
|
// History: 18-Mar-93 BartoszM Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
CBufferCacheIter::CBufferCacheIter ( CBufferCache& cache )
|
||
|
: _pPhysBuf(cache._aPhysBuf.GetPointer()), _idx(0), _cHash( cache._cHash )
|
||
|
{
|
||
|
do
|
||
|
{
|
||
|
_pBuf = _pPhysBuf[_idx];
|
||
|
if (_pBuf != 0)
|
||
|
break;
|
||
|
_idx++;
|
||
|
} while ( _idx < _cHash );
|
||
|
} //CBufferCacheIter
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CBufferCacheIter::operator++, public
|
||
|
//
|
||
|
// Synopsis: Increments the iterator
|
||
|
//
|
||
|
// History: 18-Mar-93 BartoszM Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
void CBufferCacheIter::operator++()
|
||
|
{
|
||
|
Win4Assert(_pBuf != 0);
|
||
|
_pBuf = _pBuf->Next();
|
||
|
while (_pBuf == 0 && ++_idx < _cHash )
|
||
|
_pBuf = _pPhysBuf[_idx];
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CBufferCache::CBufferCache, public
|
||
|
//
|
||
|
// Synopsis: Constructor
|
||
|
//
|
||
|
// History: 18-Mar-93 BartoszM Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
CBufferCache::CBufferCache( unsigned cHash )
|
||
|
: _cBuffers( 0 ),
|
||
|
_cHash( cHash ),
|
||
|
_aPhysBuf( cHash )
|
||
|
#if CIDBG == 1
|
||
|
, _cCacheHits( 0 ), _cCacheMisses( 0 )
|
||
|
#endif
|
||
|
|
||
|
{
|
||
|
RtlZeroMemory( _aPhysBuf.GetPointer(), _aPhysBuf.SizeOf() );
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CBufferCache::~CBufferCache, public
|
||
|
//
|
||
|
// Synopsis: Destructor
|
||
|
//
|
||
|
// History: 18-Mar-93 BartoszM Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
CBufferCache::~CBufferCache()
|
||
|
{
|
||
|
Free( FALSE );
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CBufferCache::Free, public
|
||
|
//
|
||
|
// Synopsis: Deletes all buffers
|
||
|
//
|
||
|
// Arguments: [fFailFlush] -- If TRUE, this method will THROW if the flush
|
||
|
// fails. If FALSE, flush failures are
|
||
|
// ignored.
|
||
|
//
|
||
|
// History: 18-Mar-93 BartoszM Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
void CBufferCache::Free( BOOL fFailFlush )
|
||
|
{
|
||
|
if ( 0 != _cBuffers )
|
||
|
{
|
||
|
for ( unsigned i = 0; i < _cHash; i++ )
|
||
|
{
|
||
|
// Keep the cache consistent in case we fail flushing.
|
||
|
|
||
|
while ( 0 != _aPhysBuf[i] )
|
||
|
{
|
||
|
CPhysBuffer * pTemp = _aPhysBuf[i];
|
||
|
|
||
|
pTemp->Flush( fFailFlush );
|
||
|
|
||
|
#if CIDBG == 1
|
||
|
if ( pTemp->IsReferenced() )
|
||
|
ciDebugOut(( DEB_WARN,
|
||
|
"Buffer for page %ld still referenced.\n",
|
||
|
pTemp->PageNumber() ));
|
||
|
#endif // CIDBG == 1
|
||
|
|
||
|
_aPhysBuf[i] = pTemp->Next();
|
||
|
delete pTemp;
|
||
|
_cBuffers--;
|
||
|
}
|
||
|
|
||
|
Win4Assert( 0 == _aPhysBuf[i] );
|
||
|
}
|
||
|
|
||
|
Win4Assert( 0 == _cBuffers );
|
||
|
}
|
||
|
} //Free
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CBufferCache::TryToRemoveBuffers, public
|
||
|
//
|
||
|
// Synopsis: Tries to remove unreferenced buffers from the cache.
|
||
|
// If one is not found, it's not a problem.
|
||
|
//
|
||
|
// Arguments: [cMax] -- Recommended maximum in the cache
|
||
|
//
|
||
|
// History: 15-March-96 dlee Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
void CBufferCache::TryToRemoveBuffers( unsigned cMax )
|
||
|
{
|
||
|
for ( unsigned i = 0; i < 3 && ( Count() > cMax ); i++ )
|
||
|
{
|
||
|
// find an unreferenced page with the lowest (oldest) usn.
|
||
|
|
||
|
ULONG ulPage = ULONG_MAX;
|
||
|
ULONG ulUSN = ULONG_MAX;
|
||
|
|
||
|
for ( CBufferCacheIter iter(*this); !iter.AtEnd(); ++iter )
|
||
|
{
|
||
|
CPhysBuffer * pPhys = iter.Get();
|
||
|
|
||
|
Win4Assert( 0 != pPhys );
|
||
|
|
||
|
if ( ( ulUSN > pPhys->GetUSN() ) &&
|
||
|
( !pPhys->IsReferenced() ) )
|
||
|
{
|
||
|
ulUSN = pPhys->GetUSN();
|
||
|
ulPage = pPhys->PageNumber();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( ULONG_MAX == ulPage )
|
||
|
break;
|
||
|
else
|
||
|
Destroy( ulPage );
|
||
|
}
|
||
|
} //TryToRemoveBuffers
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CBufferCache::Search, public
|
||
|
//
|
||
|
// Synopsis: Searches the buffer hash for the requested buffer.
|
||
|
//
|
||
|
// Arguments: [ulPage] -- Page number of page to be found.
|
||
|
//
|
||
|
// Returns: 0 if page not currently buffered, or the pointer
|
||
|
// to the (physical) buffer.
|
||
|
//
|
||
|
// History: 09-Mar-92 KyleP Created
|
||
|
// 17-Mar-93 BartoszM Rewrote
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
CPhysBuffer * CBufferCache::Search(
|
||
|
ULONG hash,
|
||
|
ULONG commonPage )
|
||
|
{
|
||
|
// ciDebugOut (( DEB_ITRACE, "CBufferCache::Search %d\n", ulPage ));
|
||
|
|
||
|
for ( CPhysBuffer *pPhys = _aPhysBuf[ hash ];
|
||
|
0 != pPhys;
|
||
|
pPhys = pPhys->Next() )
|
||
|
{
|
||
|
if ( pPhys->PageNumber() == commonPage )
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
#if DBG==1 || CIDBG==1
|
||
|
if ( 0 == pPhys )
|
||
|
_cCacheMisses++;
|
||
|
else
|
||
|
_cCacheHits++;
|
||
|
#endif // DBG==1 || CIDBG==1
|
||
|
|
||
|
return pPhys;
|
||
|
} //Search
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: Flush
|
||
|
//
|
||
|
// Synopsis: Flushes all cached pages upto and including the specified
|
||
|
// page to disk.
|
||
|
//
|
||
|
// Arguments: [nHighPage] -- The "CI" page number below which all pages
|
||
|
// must be flushed to disk.
|
||
|
//
|
||
|
// History: 4-20-94 srikants Created
|
||
|
//
|
||
|
// Notes: nHighPage is in units of "CI" page (4K bytes) and not
|
||
|
// CommonPage which is 64K.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
void CBufferCache::Flush( ULONG nHighPage, BOOL fLeaveFlushFlagAlone )
|
||
|
{
|
||
|
nHighPage = PgCommonPgTrunc(nHighPage);
|
||
|
ciDebugOut(( DEB_ITRACE, "CBufferCache : Flushing All Pages <= 0x%x\n",
|
||
|
nHighPage ));
|
||
|
|
||
|
if ( 0 == _cBuffers )
|
||
|
return;
|
||
|
|
||
|
//
|
||
|
// We Must Flush All pages <= the given page number.
|
||
|
//
|
||
|
|
||
|
for ( CBufferCacheIter iter(*this); !iter.AtEnd(); ++iter )
|
||
|
{
|
||
|
CPhysBuffer * pPhys = iter.Get();
|
||
|
|
||
|
Win4Assert( 0 != pPhys );
|
||
|
|
||
|
if ( pPhys->PageNumber() <= nHighPage )
|
||
|
{
|
||
|
// Call flush even if not required to get assertions checked
|
||
|
|
||
|
BOOL fRequiresFlush = pPhys->RequiresFlush();
|
||
|
|
||
|
pPhys->Flush( TRUE );
|
||
|
|
||
|
if ( fLeaveFlushFlagAlone && fRequiresFlush )
|
||
|
pPhys->SetIntentToWrite();
|
||
|
}
|
||
|
}
|
||
|
} //Flush
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CBufferCache::Destroy, public
|
||
|
//
|
||
|
// Synopsis: Deletes a buffer.
|
||
|
//
|
||
|
// Arguments: [ulPage] -- page number to be deleted
|
||
|
//
|
||
|
// History: 18-Mar-93 BartoszM Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
void CBufferCache::Destroy( ULONG ulPage, BOOL fFailFlush )
|
||
|
{
|
||
|
ulPage = PgCommonPgTrunc(ulPage);
|
||
|
|
||
|
ULONG iHash = hash( ulPage );
|
||
|
|
||
|
CPhysBuffer * pPhys = _aPhysBuf[ iHash ];
|
||
|
Win4Assert( 0 != pPhys );
|
||
|
|
||
|
// Find the page. Don't remove from the list until it is flushed.
|
||
|
// Is it first? (common case)
|
||
|
|
||
|
if (pPhys->PageNumber() == ulPage)
|
||
|
{
|
||
|
// This should be flushed before being unmapped. Fix for bug 132655
|
||
|
|
||
|
pPhys->Flush( fFailFlush );
|
||
|
|
||
|
_aPhysBuf[iHash] = pPhys->Next();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Search linked list
|
||
|
|
||
|
CPhysBuffer * pPhysPrev;
|
||
|
do
|
||
|
{
|
||
|
pPhysPrev = pPhys;
|
||
|
pPhys = pPhys->Next();
|
||
|
Win4Assert( 0 != pPhys );
|
||
|
}
|
||
|
while (pPhys->PageNumber() != ulPage );
|
||
|
|
||
|
// This should be flushed before being unmapped. Fix for bug 132655
|
||
|
|
||
|
pPhys->Flush( fFailFlush );
|
||
|
|
||
|
// delete from the linked list
|
||
|
|
||
|
pPhysPrev->Link( pPhys->Next() );
|
||
|
Win4Assert ( !pPhys->IsReferenced() );
|
||
|
}
|
||
|
|
||
|
_cBuffers--;
|
||
|
delete pPhys;
|
||
|
} //Destroy
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CBufferCache::Add, public
|
||
|
//
|
||
|
// Synopsis: Adds a buffer to the buffer hash.
|
||
|
//
|
||
|
// Arguments: [pPhysBuf] -- Buffer to add.
|
||
|
//
|
||
|
// History: 09-Mar-92 KyleP Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
void CBufferCache::Add( CPhysBuffer * pPhysBuf, ULONG hash )
|
||
|
{
|
||
|
_cBuffers++;
|
||
|
|
||
|
pPhysBuf->Reference();
|
||
|
|
||
|
pPhysBuf->Link( _aPhysBuf[ hash ] );
|
||
|
|
||
|
_aPhysBuf[ hash ] = pPhysBuf;
|
||
|
} //Add
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CBufferCache::MinPageInUse, public
|
||
|
//
|
||
|
// Synopsis: Finds the smallest page in use within the cache
|
||
|
//
|
||
|
// Returns : TRUE if any page is in the cache; FALSE o/w
|
||
|
// If TRUE is returned, then minPageInUse will contain the
|
||
|
// minimum page that is present in the cache.
|
||
|
//
|
||
|
// Arguments: minPageInUse (out) Will be set to the minimum page in
|
||
|
// the cache if the return value is TRUE.
|
||
|
//
|
||
|
// History: 02-May-94 DwightKr Created
|
||
|
// 08-Dec-94 SrikantS Fixed to return the correct value for
|
||
|
// empty cache.
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
BOOL CBufferCache::MinPageInUse(ULONG &minPageInUse)
|
||
|
{
|
||
|
ULONG minPage = 0xFFFFFFFF;
|
||
|
|
||
|
for (CBufferCacheIter iter(*this); !iter.AtEnd(); ++iter)
|
||
|
{
|
||
|
minPage = min(minPage, iter.Get()->PageNumber() );
|
||
|
}
|
||
|
|
||
|
if ( minPage == 0xFFFFFFFF )
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
minPageInUse = minPage;
|
||
|
return TRUE;
|
||
|
}
|
||
|
} //MinPageInUse
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPhysStorage::CPhysStorage, public
|
||
|
//
|
||
|
// Effects: Creates a new index.
|
||
|
//
|
||
|
// Arguments: [storage] -- physical storage to create index in
|
||
|
// [obj] -- Storage object
|
||
|
// [objectId] -- Index ID
|
||
|
// [cPageReqSize] --
|
||
|
// [stream] -- Memory mapped stream
|
||
|
// [fThrowCorruptErrorOnFailures -- If true, then errors during
|
||
|
// opening will be marked as
|
||
|
// catalog corruption
|
||
|
// [cCachedItems] -- # of non-referenced pages to cache
|
||
|
// [fAllowReadAhead] -- if TRUE, non-mapped IO will be used
|
||
|
// when the physical storage says it's ok.
|
||
|
//
|
||
|
// Signals: Cannot create.
|
||
|
//
|
||
|
// Modifies: A new index (file) is created on the disk.
|
||
|
//
|
||
|
// History: 07-Mar-92 KyleP Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
CPhysStorage::CPhysStorage( PStorage & storage,
|
||
|
PStorageObject& obj,
|
||
|
WORKID objectId,
|
||
|
UINT cpageReqSize,
|
||
|
PMmStream * stream,
|
||
|
BOOL fThrowCorruptErrorOnFailures,
|
||
|
unsigned cCachedItems,
|
||
|
BOOL fAllowReadAhead )
|
||
|
: _sigPhysStorage(eSigPhysStorage),
|
||
|
_fWritable( TRUE ),
|
||
|
_objectId( objectId ),
|
||
|
_obj ( obj),
|
||
|
_cpageUsedFileSize( 0 ),
|
||
|
_storage(storage),
|
||
|
_stream(stream),
|
||
|
_iFirstNonShrunkPage( 0 ),
|
||
|
_fThrowCorruptErrorOnFailures(fThrowCorruptErrorOnFailures),
|
||
|
_cpageGrowth(1),
|
||
|
_cMaxCachedBuffers( cCachedItems ),
|
||
|
_cache( __max( 17, ( cCachedItems & 1 ) ? cCachedItems :
|
||
|
cCachedItems + 1 ) ),
|
||
|
_usnGen( 0 ),
|
||
|
_fAllowReadAhead( fAllowReadAhead )
|
||
|
{
|
||
|
//
|
||
|
// Use different locking schemes depending on usage
|
||
|
//
|
||
|
|
||
|
if ( 0 == _cMaxCachedBuffers )
|
||
|
_xMutex.Set( new CMutexSem() );
|
||
|
else
|
||
|
_xRWAccess.Set( new CReadWriteAccess() );
|
||
|
|
||
|
if ( _stream.IsNull() || !_stream->Ok() )
|
||
|
{
|
||
|
ciDebugOut((DEB_ERROR, "Index Creation failed %08x\n", objectId ));
|
||
|
|
||
|
if ( _fThrowCorruptErrorOnFailures || _stream->FStatusFileNotFound() )
|
||
|
{
|
||
|
//
|
||
|
// We don't have code to handle such failures, hence mark
|
||
|
// catalog as corrupt; otherwise throw e_fail
|
||
|
//
|
||
|
Win4Assert( !"Corrupt catalog" );
|
||
|
_storage.ReportCorruptComponent( L"PhysStorage1" );
|
||
|
THROW( CException( CI_CORRUPT_DATABASE ));
|
||
|
}
|
||
|
else
|
||
|
THROW( CException( E_FAIL ));
|
||
|
}
|
||
|
|
||
|
if (cpageReqSize == 0)
|
||
|
cpageReqSize = 1;
|
||
|
|
||
|
TRY
|
||
|
{
|
||
|
_GrowFile( cpageReqSize );
|
||
|
}
|
||
|
CATCH( CException, e )
|
||
|
{
|
||
|
//
|
||
|
// There may not be enough space on the disk to allocate the
|
||
|
// requested size. If not enough space, do the best that we
|
||
|
// can and hope space gets freed.
|
||
|
//
|
||
|
|
||
|
_GrowFile( 1 );
|
||
|
}
|
||
|
END_CATCH
|
||
|
|
||
|
ciDebugOut(( DEB_ITRACE, "Physical index %08x created.\n", _objectId ));
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPhysStorage::CPhysStorage, public
|
||
|
//
|
||
|
// Effects: Opens an existing index.
|
||
|
//
|
||
|
// Arguments: [storage] -- physical storage to open index in
|
||
|
// [obj] -- Storage object
|
||
|
// [objectId] -- Index ID.
|
||
|
// [stream] -- Memory mapped stream
|
||
|
// [mode] -- Open mode
|
||
|
// [fThrowCorruptErrorOnFailures -- If true, then errors during
|
||
|
// opening will be marked as
|
||
|
// catalog corruption
|
||
|
// [cCachedItems] -- # of non-referenced pages to cache
|
||
|
// [fAllowReadAhead] -- if TRUE, non-mapped IO will be used
|
||
|
// when the physical storage says it's ok.
|
||
|
//
|
||
|
// History: 07-Mar-92 KyleP Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
CPhysStorage::CPhysStorage( PStorage & storage,
|
||
|
PStorageObject& obj,
|
||
|
WORKID objectId,
|
||
|
PMmStream * stream,
|
||
|
PStorage::EOpenMode mode,
|
||
|
BOOL fThrowCorruptErrorOnFailures,
|
||
|
unsigned cCachedItems,
|
||
|
BOOL fAllowReadAhead )
|
||
|
: _sigPhysStorage(eSigPhysStorage),
|
||
|
_fWritable( PStorage::eOpenForWrite == mode ),
|
||
|
_objectId( objectId ),
|
||
|
_obj ( obj ),
|
||
|
_cpageUsedFileSize( 0 ),
|
||
|
_storage(storage),
|
||
|
_stream(stream),
|
||
|
_fThrowCorruptErrorOnFailures(fThrowCorruptErrorOnFailures),
|
||
|
_cpageGrowth(1),
|
||
|
_iFirstNonShrunkPage( 0 ),
|
||
|
_cMaxCachedBuffers( cCachedItems ),
|
||
|
_cache( __max( 17, ( cCachedItems & 1 ) ? cCachedItems :
|
||
|
cCachedItems + 1 ) ),
|
||
|
_usnGen( 0 ),
|
||
|
_fAllowReadAhead( fAllowReadAhead )
|
||
|
{
|
||
|
//
|
||
|
// Use different locking schemes depending on usage
|
||
|
//
|
||
|
|
||
|
if ( 0 == _cMaxCachedBuffers )
|
||
|
_xMutex.Set( new CMutexSem() );
|
||
|
else
|
||
|
_xRWAccess.Set( new CReadWriteAccess() );
|
||
|
|
||
|
if ( _stream.IsNull() || !_stream->Ok() )
|
||
|
{
|
||
|
ciDebugOut(( DEB_ERROR, "Open of index %08x failed\n", objectId ));
|
||
|
|
||
|
if ( _fThrowCorruptErrorOnFailures || _stream->FStatusFileNotFound() )
|
||
|
{
|
||
|
//
|
||
|
// We don't have code to handle such failures, hence mark
|
||
|
// catalog as corrupt; otherwise throw e_fail
|
||
|
//
|
||
|
Win4Assert( !"Corrupt catalog" );
|
||
|
_storage.ReportCorruptComponent( L"PhysStorage2" );
|
||
|
THROW( CException( CI_CORRUPT_DATABASE ));
|
||
|
}
|
||
|
else
|
||
|
THROW( CException( E_FAIL ));
|
||
|
}
|
||
|
|
||
|
_cpageFileSize = ToPages(_stream->SizeLow(), _stream->SizeHigh());
|
||
|
|
||
|
ciDebugOut(( DEB_ITRACE, "Physical index %08x (%d pages) opened.\n",
|
||
|
_objectId, _cpageFileSize ));
|
||
|
}
|
||
|
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPhysStorage::MakeBackupCopy
|
||
|
//
|
||
|
// Synopsis: Makes a backup copy of the current storage using the
|
||
|
// destination storage.
|
||
|
//
|
||
|
// Arguments: [dst] - Destination storage.
|
||
|
// [progressTracker] - Progress tracking
|
||
|
//
|
||
|
// History: 3-18-97 srikants Created
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
void CPhysStorage::MakeBackupCopy( CPhysStorage & dst,
|
||
|
PSaveProgressTracker & progressTracker )
|
||
|
{
|
||
|
//
|
||
|
// Copy one page at a time.
|
||
|
//
|
||
|
ULONG * pSrcNext = 0;
|
||
|
ULONG * pDstNext = 0;
|
||
|
|
||
|
unsigned iCurrBorrow = 0; // always has the current buffers borrowed.
|
||
|
|
||
|
SCODE sc = S_OK;
|
||
|
|
||
|
TRY
|
||
|
{
|
||
|
//
|
||
|
// Grow the destination to be as big as the current one.
|
||
|
//
|
||
|
dst._GrowFile( _cpageFileSize );
|
||
|
|
||
|
if ( _cpageFileSize > 0 )
|
||
|
{
|
||
|
iCurrBorrow = 0;
|
||
|
|
||
|
pSrcNext = BorrowBuffer(iCurrBorrow, FALSE );
|
||
|
pDstNext = dst.BorrowNewBuffer(iCurrBorrow);
|
||
|
|
||
|
const unsigned iLastPage = _cpageFileSize-1;
|
||
|
|
||
|
for ( unsigned j = 0; j < _cpageFileSize; j++ )
|
||
|
{
|
||
|
ULONG * pSrc = pSrcNext;
|
||
|
ULONG * pDst = pDstNext;
|
||
|
|
||
|
Win4Assert( 0 != pSrc && 0 != pDst );
|
||
|
|
||
|
RtlCopyMemory( pDst, pSrc, CI_PAGE_SIZE );
|
||
|
|
||
|
//
|
||
|
// Before returning buffers, grab the next ones to prevent
|
||
|
// the large page from being taken out of cache.
|
||
|
//
|
||
|
|
||
|
if ( j != iLastPage )
|
||
|
{
|
||
|
iCurrBorrow = j+1;
|
||
|
pSrcNext = BorrowBuffer(iCurrBorrow, FALSE );
|
||
|
pDstNext = dst.BorrowNewBuffer(iCurrBorrow);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pSrcNext = pDstNext = 0;
|
||
|
}
|
||
|
|
||
|
ReturnBuffer( j );
|
||
|
dst.ReturnBuffer( j, TRUE );
|
||
|
|
||
|
if ( progressTracker.IsAbort() )
|
||
|
THROW( CException( STATUS_TOO_LATE ) );
|
||
|
|
||
|
//
|
||
|
// Update the progress every 64 K.
|
||
|
//
|
||
|
if ( (j % PAGES_PER_COMMON_PAGE) == (PAGES_PER_COMMON_PAGE-1) )
|
||
|
progressTracker.UpdateCopyProgress( (ULONG) j, _cpageFileSize );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
dst.Flush();
|
||
|
}
|
||
|
CATCH( CException, e )
|
||
|
{
|
||
|
if ( pSrcNext )
|
||
|
ReturnBuffer( iCurrBorrow );
|
||
|
|
||
|
if ( pDstNext )
|
||
|
dst.ReturnBuffer(iCurrBorrow);
|
||
|
|
||
|
sc = e.GetErrorCode();
|
||
|
}
|
||
|
END_CATCH
|
||
|
|
||
|
if ( S_OK != sc )
|
||
|
{
|
||
|
//
|
||
|
// _GrowFile throws CI_CORRUPT_DATABASE if it cannot create
|
||
|
// the file that big. We don't want to consider that a
|
||
|
// corruption.
|
||
|
//
|
||
|
if ( CI_CORRUPT_DATABASE == sc )
|
||
|
sc = E_FAIL;
|
||
|
|
||
|
THROW( CException( sc ) );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Consider 100% of the copy work is done.
|
||
|
//
|
||
|
progressTracker.UpdateCopyProgress( 1,1 );
|
||
|
}
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPhysStorage::~CPhysStorage, public
|
||
|
//
|
||
|
// Synopsis: closes index.
|
||
|
//
|
||
|
// History: 09-Mar-92 KyleP Created
|
||
|
//
|
||
|
// Notes: Don't write back pages. This is either a read only
|
||
|
// index or we are aborting a merge. Pages are written
|
||
|
// back after a successful merge using Reopen().
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
CPhysStorage::~CPhysStorage()
|
||
|
{
|
||
|
ciDebugOut(( DEB_ITRACE, "Physical index %lx closed.\n", _objectId ));
|
||
|
|
||
|
_cache.Free( FALSE );
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPhysStorage::Close, public
|
||
|
//
|
||
|
// Effects: Closes the stream.
|
||
|
//
|
||
|
// History: 07-Mar-92 KyleP Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
void CPhysStorage::Close()
|
||
|
{
|
||
|
_cache.Free();
|
||
|
_stream.Free();
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: Flush
|
||
|
//
|
||
|
// Synopsis: Flushes all the pages that were unmapped since the last
|
||
|
// flush.
|
||
|
//
|
||
|
// History: 4-29-94 srikants Created
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
void CPhysStorage::Flush( BOOL fLeaveFlushFlagAlone )
|
||
|
{
|
||
|
Win4Assert( _fWritable );
|
||
|
|
||
|
// Grab the lock to make sure the pages aren't deleted while
|
||
|
// they are being flushed.
|
||
|
|
||
|
if ( 0 == _cMaxCachedBuffers )
|
||
|
{
|
||
|
CLock lock( _xMutex.GetReference() );
|
||
|
|
||
|
_cache.Flush( ULONG_MAX, fLeaveFlushFlagAlone );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CReadAccess readLock( _xRWAccess.GetReference() );
|
||
|
|
||
|
_cache.Flush( ULONG_MAX, fLeaveFlushFlagAlone );
|
||
|
}
|
||
|
} //Flush
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: ShrinkToFit
|
||
|
//
|
||
|
// Synopsis: Reduces the size of the stream by giving up pages in the end
|
||
|
// which are not needed.
|
||
|
//
|
||
|
// Arguments: (none)
|
||
|
//
|
||
|
// History: 9-08-94 srikants Created
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
void CPhysStorage::ShrinkToFit()
|
||
|
{
|
||
|
_cache.Free();
|
||
|
_stream->SetSize( _storage,
|
||
|
PageToLow(_cpageUsedFileSize),
|
||
|
PageToHigh(_cpageUsedFileSize) );
|
||
|
_cpageFileSize = _cpageUsedFileSize;
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPhysStorage::Reopen, public
|
||
|
//
|
||
|
// Synopsis: Flushes, closes, and reopens for reading.
|
||
|
//
|
||
|
// History: 09-Mar-92 KyleP Created
|
||
|
// 26-Aug-92 BartoszM Separated from destructor
|
||
|
// 06-May-94 Srikants Add fSetSize as a parameter to truncate
|
||
|
// the stream optionally.
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
void CPhysStorage::Reopen( BOOL fWritable )
|
||
|
{
|
||
|
// No need to lock.
|
||
|
|
||
|
_cache.Free(); // will flush all buffers
|
||
|
|
||
|
if ( _fWritable && !_stream.IsNull() )
|
||
|
{
|
||
|
//
|
||
|
// Give it an accurate used length.
|
||
|
//
|
||
|
|
||
|
_stream->SetSize( _storage,
|
||
|
PageToLow(_cpageUsedFileSize),
|
||
|
PageToHigh(_cpageUsedFileSize) );
|
||
|
|
||
|
_cpageFileSize = _cpageUsedFileSize;
|
||
|
|
||
|
_stream.Free();
|
||
|
|
||
|
//
|
||
|
// reopen shadow indexes as read and master indexes as write to
|
||
|
// support shrink from front.
|
||
|
//
|
||
|
|
||
|
_fWritable = fWritable;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// reopen it with the new mode
|
||
|
//
|
||
|
|
||
|
ReOpenStream();
|
||
|
|
||
|
if ( _stream.IsNull() || !_stream->Ok() )
|
||
|
{
|
||
|
ciDebugOut(( DEB_ERROR, "Re-Open file of index %08x failed\n",
|
||
|
_objectId ));
|
||
|
|
||
|
if ( _fThrowCorruptErrorOnFailures || _stream->FStatusFileNotFound() )
|
||
|
{
|
||
|
//
|
||
|
// We don't have code to handle such failures, hence mark
|
||
|
// catalog as corrupt; otherwise throw e_fail
|
||
|
//
|
||
|
Win4Assert( !"Corrupt catalog" );
|
||
|
_storage.ReportCorruptComponent( L"PhysStorage3" );
|
||
|
THROW( CException( CI_CORRUPT_DATABASE ));
|
||
|
}
|
||
|
else
|
||
|
THROW( CException( E_FAIL ));
|
||
|
}
|
||
|
|
||
|
_cpageFileSize = ToPages(_stream->SizeLow(), _stream->SizeHigh());
|
||
|
|
||
|
ciDebugOut(( DEB_ITRACE, "Physical index %08x opened.\n", _objectId ));
|
||
|
} //Reopen
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPhysStorage::LokBorrowNewBuffer, public
|
||
|
//
|
||
|
// Arguments: [hash] -- hash value for doing the lookup
|
||
|
// [commonPage]] -- the common page of nPage
|
||
|
// [nPage] -- page number
|
||
|
//
|
||
|
// Returns: A buffer for a new (unused) page.
|
||
|
//
|
||
|
// History: 03-Mar-98 dlee Created from existing BorrowNewBuffer
|
||
|
//
|
||
|
// Notes: On creation, the buffer is filled with zeros.
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
ULONG * CPhysStorage::LokBorrowNewBuffer(
|
||
|
ULONG hash,
|
||
|
ULONG commonPage,
|
||
|
ULONG nPage )
|
||
|
{
|
||
|
CPhysBuffer* pBuf = _cache.Search( hash, commonPage );
|
||
|
|
||
|
if (pBuf)
|
||
|
{
|
||
|
pBuf->Reference();
|
||
|
Win4Assert( nPage < _cpageFileSize );
|
||
|
|
||
|
// fIntentToWrite should be TRUE
|
||
|
|
||
|
pBuf->SetIntentToWrite();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// If we've moved beyond the previous end of file, set the new
|
||
|
// size of file.
|
||
|
|
||
|
if ( nPage >= _cpageFileSize )
|
||
|
_GrowFile( nPage + _cpageGrowth );
|
||
|
|
||
|
// First try to release unreferenced buffers if the cache is full.
|
||
|
|
||
|
_cache.TryToRemoveBuffers( _cMaxCachedBuffers );
|
||
|
|
||
|
pBuf = new CPhysBuffer( _stream.GetReference(),
|
||
|
nPage,
|
||
|
TRUE, // writable
|
||
|
TRUE, // intent to write
|
||
|
! ( _fAllowReadAhead &&
|
||
|
_storage.FavorReadAhead() ) );
|
||
|
_cache.Add( pBuf, hash );
|
||
|
}
|
||
|
|
||
|
Win4Assert( nPage + 1 > _cpageUsedFileSize );
|
||
|
|
||
|
_cpageUsedFileSize = nPage + 1;
|
||
|
Win4Assert( _cpageUsedFileSize <= _cpageFileSize );
|
||
|
|
||
|
RtlZeroMemory( pBuf->GetPage( nPage ), CI_PAGE_SIZE );
|
||
|
|
||
|
return pBuf->GetPage( nPage );
|
||
|
} //LokBorrowNewBuffer
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPhysStorage::BorrowNewBuffer, public
|
||
|
//
|
||
|
// Arguments: [nPage] -- page number
|
||
|
//
|
||
|
// Returns: A buffer for a new (unused) page.
|
||
|
//
|
||
|
// Signals: No more disk space.
|
||
|
//
|
||
|
// History: 09-Mar-92 KyleP Created
|
||
|
//
|
||
|
// Notes: On creation, the buffer is filled with zeros.
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
ULONG * CPhysStorage::BorrowNewBuffer( ULONG nPage )
|
||
|
{
|
||
|
Win4Assert( _fWritable );
|
||
|
|
||
|
ULONG commonPage = PgCommonPgTrunc( nPage );
|
||
|
ULONG hash = _cache.hash( commonPage );
|
||
|
|
||
|
// Use appropriate locking based on whether any pages are in the cache.
|
||
|
|
||
|
if ( 0 == _cMaxCachedBuffers )
|
||
|
{
|
||
|
CLock lock( _xMutex.GetReference() );
|
||
|
|
||
|
return LokBorrowNewBuffer( hash, commonPage, nPage );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CWriteAccess writeLock( _xRWAccess.GetReference() );
|
||
|
|
||
|
return LokBorrowNewBuffer( hash, commonPage, nPage );
|
||
|
}
|
||
|
} //BorrowNewBuffer
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPhysStorage::LokBorrowOrAddBuffer, public
|
||
|
//
|
||
|
// Arguments: [hash] -- hash value for doing the lookup
|
||
|
// [commonPage]] -- the common page of nPage
|
||
|
// [nPage] -- page number
|
||
|
// [fAdd] -- if TRUE, create the page if not found
|
||
|
// if FALSE and not found, return 0
|
||
|
// [fWritable] -- if TRUE, the page is writable
|
||
|
// [fIntentToWrite] -- TRUE if the caller intends to write
|
||
|
//
|
||
|
// Returns: A buffer loaded with page [nPage]
|
||
|
//
|
||
|
// History: 09-Mar-92 KyleP Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
ULONG * CPhysStorage::LokBorrowOrAddBuffer(
|
||
|
ULONG hash,
|
||
|
ULONG commonPage,
|
||
|
ULONG nPage,
|
||
|
BOOL fAdd,
|
||
|
BOOL fWritable,
|
||
|
BOOL fIntentToWrite )
|
||
|
{
|
||
|
// First, look in the cache. Either we haven't looked yet (if no
|
||
|
// caching) or another thread may have added it between the time the
|
||
|
// read lock was released and the write lock taken.
|
||
|
|
||
|
Win4Assert( commonPage >= _iFirstNonShrunkPage );
|
||
|
CPhysBuffer * pbuf = _cache.Search( hash, commonPage );
|
||
|
|
||
|
if ( 0 != pbuf )
|
||
|
{
|
||
|
Win4Assert( !fWritable || pbuf->IsWritable() );
|
||
|
|
||
|
pbuf->Reference();
|
||
|
if ( fIntentToWrite )
|
||
|
pbuf->SetIntentToWrite();
|
||
|
|
||
|
return pbuf->GetPage( nPage );
|
||
|
}
|
||
|
|
||
|
ULONG * pulRet = 0;
|
||
|
|
||
|
if ( fAdd )
|
||
|
{
|
||
|
pbuf = new CPhysBuffer( _stream.GetReference(),
|
||
|
nPage,
|
||
|
fWritable,
|
||
|
fIntentToWrite,
|
||
|
! ( _fAllowReadAhead &&
|
||
|
_storage.FavorReadAhead() ) );
|
||
|
|
||
|
_cache.Add( pbuf, hash );
|
||
|
pulRet = pbuf->GetPage( nPage );
|
||
|
|
||
|
// Try to release unreferenced buffers if the cache is full.
|
||
|
|
||
|
_cache.TryToRemoveBuffers( _cMaxCachedBuffers );
|
||
|
}
|
||
|
|
||
|
return pulRet;
|
||
|
} //LokBorrowOrAddBuffer
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPhysStorage::BorrowBuffer, public
|
||
|
//
|
||
|
// Arguments: [nPage] -- Page number of buffer to find.
|
||
|
//
|
||
|
// Returns: A buffer loaded with page [nPage]
|
||
|
//
|
||
|
// Signals: Out of memory? Buffer not found?
|
||
|
//
|
||
|
// History: 09-Mar-92 KyleP Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
ULONG * CPhysStorage::BorrowBuffer(
|
||
|
ULONG nPage,
|
||
|
BOOL fWritable,
|
||
|
BOOL fIntentToWrite )
|
||
|
{
|
||
|
ULONG commonPage = PgCommonPgTrunc( nPage );
|
||
|
ULONG hash = _cache.hash( commonPage );
|
||
|
|
||
|
Win4Assert( fWritable || !fIntentToWrite );
|
||
|
|
||
|
// Make sure we didn't walk off the end.
|
||
|
|
||
|
if ( nPage >= _cpageFileSize )
|
||
|
{
|
||
|
ciDebugOut(( DEB_WARN, "Asking for page %d, file size %d pages\n",
|
||
|
nPage, _cpageFileSize ));
|
||
|
Win4Assert( !"BorrowBuffer walked off end of file" );
|
||
|
|
||
|
_storage.ReportCorruptComponent( L"PhysStorage4" );
|
||
|
|
||
|
THROW( CException( CI_CORRUPT_DATABASE ) );
|
||
|
}
|
||
|
|
||
|
// If caching, try finding it in the cache first under a read lock
|
||
|
// This is the fast path for when the entire property cache is in memory.
|
||
|
|
||
|
if ( 0 != _cMaxCachedBuffers )
|
||
|
{
|
||
|
CReadAccess readLock( _xRWAccess.GetReference() );
|
||
|
|
||
|
ULONG * p = LokBorrowOrAddBuffer( hash, commonPage, nPage, FALSE,
|
||
|
fWritable, fIntentToWrite );
|
||
|
if ( 0 != p )
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
// Ok, maybe we have to add it.
|
||
|
|
||
|
if ( 0 == _cMaxCachedBuffers )
|
||
|
{
|
||
|
CLock lock( _xMutex.GetReference() );
|
||
|
|
||
|
return LokBorrowOrAddBuffer( hash, commonPage, nPage, TRUE,
|
||
|
fWritable, fIntentToWrite );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CWriteAccess writeLock( _xRWAccess.GetReference() );
|
||
|
|
||
|
return LokBorrowOrAddBuffer( hash, commonPage, nPage, TRUE,
|
||
|
fWritable, fIntentToWrite );
|
||
|
}
|
||
|
} //BorrowBuffer
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPhysStorage::ReturnBuffer, public
|
||
|
//
|
||
|
// Synopsis: Dereference and return the buffer to the pool.
|
||
|
//
|
||
|
// Arguments: [nPage] -- Page number of buffer being returned.
|
||
|
// [fFlush] -- TRUE if the buffer should be flushed if this
|
||
|
// is the last reference to the buffer.
|
||
|
// [fFailFlush] -- TRUE if an exception should be raised if
|
||
|
// the flush fails. Note that even if fFlush
|
||
|
// is FALSE, the buffer may still be flushed
|
||
|
// because previously it was borrowed with
|
||
|
// intent to write.
|
||
|
//
|
||
|
// Notes: If flush throws, this call doesn't return the buffer.
|
||
|
//
|
||
|
// History: 16-Mar-93 BartoszM Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
void CPhysStorage::ReturnBuffer(
|
||
|
ULONG nPage,
|
||
|
BOOL fFlush,
|
||
|
BOOL fFailFlush )
|
||
|
{
|
||
|
ULONG commonPage = PgCommonPgTrunc( nPage );
|
||
|
ULONG hash = _cache.hash( commonPage );
|
||
|
|
||
|
// if caching is off, grab the write lock, else the read lock
|
||
|
|
||
|
if ( 0 == _cMaxCachedBuffers )
|
||
|
{
|
||
|
CLock lock( _xMutex.GetReference() );
|
||
|
|
||
|
Win4Assert( commonPage >= _iFirstNonShrunkPage );
|
||
|
CPhysBuffer *pbuf = _cache.Search( hash, commonPage );
|
||
|
|
||
|
Win4Assert( 0 != pbuf );
|
||
|
Win4Assert( pbuf->IsReferenced() );
|
||
|
|
||
|
// If the refcount is 1, it's about to be destroyed. Do the Flush
|
||
|
// now, in case it fails, before mucking with any data structures.
|
||
|
|
||
|
BOOL fDestroy = pbuf->IsRefCountOne();
|
||
|
|
||
|
if ( fDestroy && fFlush )
|
||
|
{
|
||
|
Win4Assert( pbuf->IsWritable() );
|
||
|
pbuf->Flush( fFailFlush );
|
||
|
}
|
||
|
|
||
|
pbuf->DeReference( _usnGen++ );
|
||
|
|
||
|
if ( fDestroy )
|
||
|
{
|
||
|
Win4Assert( !pbuf->IsReferenced() );
|
||
|
|
||
|
_cache.Destroy( nPage, fFailFlush );
|
||
|
Win4Assert( !_cache.Search( hash, commonPage ) );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CReadAccess readLock( _xRWAccess.GetReference() );
|
||
|
|
||
|
Win4Assert( commonPage >= _iFirstNonShrunkPage );
|
||
|
CPhysBuffer *pbuf = _cache.Search( hash, commonPage );
|
||
|
|
||
|
Win4Assert( 0 != pbuf );
|
||
|
Win4Assert( pbuf->IsReferenced() );
|
||
|
|
||
|
if ( fFlush )
|
||
|
{
|
||
|
Win4Assert( pbuf->IsWritable() );
|
||
|
|
||
|
if ( pbuf->IsRefCountOne() )
|
||
|
pbuf->Flush( fFailFlush );
|
||
|
}
|
||
|
|
||
|
pbuf->DeReference( _usnGen++ );
|
||
|
|
||
|
// leave it in the cache -- it'll be cleared out by BorrowBuffer
|
||
|
}
|
||
|
} //ReturnBuffer
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPhysStorage::RequiresFlush, public
|
||
|
//
|
||
|
// Synopsis: Determines if the page is scheduled for flushing
|
||
|
//
|
||
|
// Arguments: [nPage] -- Page number to check
|
||
|
//
|
||
|
// Returns: TRUE if the buffer will be flushed if Flush or Destroy is
|
||
|
// called.
|
||
|
//
|
||
|
// History: 3-Nov-98 dlee Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
BOOL CPhysStorage::RequiresFlush( ULONG nPage )
|
||
|
{
|
||
|
ULONG commonPage = PgCommonPgTrunc( nPage );
|
||
|
ULONG hash = _cache.hash( commonPage );
|
||
|
|
||
|
// if caching is off, grab the write lock, else the read lock
|
||
|
|
||
|
if ( 0 == _cMaxCachedBuffers )
|
||
|
{
|
||
|
CLock lock( _xMutex.GetReference() );
|
||
|
|
||
|
Win4Assert( commonPage >= _iFirstNonShrunkPage );
|
||
|
CPhysBuffer *pbuf = _cache.Search( hash, commonPage );
|
||
|
|
||
|
Win4Assert( 0 != pbuf );
|
||
|
Win4Assert( pbuf->IsReferenced() );
|
||
|
|
||
|
return pbuf->RequiresFlush();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CReadAccess readLock( _xRWAccess.GetReference() );
|
||
|
|
||
|
Win4Assert( commonPage >= _iFirstNonShrunkPage );
|
||
|
CPhysBuffer *pbuf = _cache.Search( hash, commonPage );
|
||
|
|
||
|
Win4Assert( 0 != pbuf );
|
||
|
Win4Assert( pbuf->IsReferenced() );
|
||
|
|
||
|
return pbuf->RequiresFlush();
|
||
|
}
|
||
|
} //RequiresFlush
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPhysStorage::_GrowFile, private
|
||
|
//
|
||
|
// Synopsis: Increases the physical (disk) size of the file.
|
||
|
//
|
||
|
// Arguments: [cpageSize] -- New file size, in pages.
|
||
|
//
|
||
|
// Signals: Out of space.
|
||
|
//
|
||
|
// History: 09-Mar-92 KyleP Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
void CPhysStorage::_GrowFile( ULONG cpageSize )
|
||
|
{
|
||
|
Win4Assert( cpageSize > 0 );
|
||
|
|
||
|
cpageSize = PgCommonPgRound(cpageSize);
|
||
|
|
||
|
ciDebugOut(( DEB_ITRACE, " Growing Index to %d pages\n", cpageSize ));
|
||
|
|
||
|
_stream->SetSize( _storage,
|
||
|
PageToLow(cpageSize),
|
||
|
PageToHigh(cpageSize) );
|
||
|
|
||
|
if (!_stream->Ok())
|
||
|
{
|
||
|
ciDebugOut(( DEB_ERROR, "GrowFile of index %08x failed: %d\n",
|
||
|
_objectId ));
|
||
|
|
||
|
if ( _fThrowCorruptErrorOnFailures )
|
||
|
{
|
||
|
//
|
||
|
// We don't have code to handle such failures, hence mark
|
||
|
// catalog as corrupt; otherwise throw e_fail
|
||
|
//
|
||
|
Win4Assert( !"Corrupt catalog" );
|
||
|
_storage.ReportCorruptComponent( L"PhysStorage5" );
|
||
|
THROW( CException( CI_CORRUPT_DATABASE ) );
|
||
|
}
|
||
|
else
|
||
|
THROW( CException( E_FAIL ) );
|
||
|
}
|
||
|
|
||
|
_cpageFileSize = cpageSize;
|
||
|
} //_GrowFile
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPhysStorage::ShrinkFromFront, public
|
||
|
//
|
||
|
// Synopsis: Makes the front part of a file sparse
|
||
|
//
|
||
|
// Arguments: [iFirstPage] -- first 4k page -- 64k granular
|
||
|
// [cPages] -- number of 4k pages
|
||
|
//
|
||
|
// Returns: The # of 4k pages actually shrunk, maybe 0
|
||
|
//
|
||
|
// History: 09-Jan-97 dlee Moved from .hxx
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
ULONG CPhysStorage::ShrinkFromFront(
|
||
|
ULONG iFirstPage,
|
||
|
ULONG cPages )
|
||
|
{
|
||
|
ULONG cShrunk = 0;
|
||
|
|
||
|
if ( _storage.SupportsShrinkFromFront() )
|
||
|
{
|
||
|
//
|
||
|
// Make sure the caller isn't leaving any gaps and was paying
|
||
|
// attention to the return value from previous calls.
|
||
|
//
|
||
|
|
||
|
Win4Assert( iFirstPage == _iFirstNonShrunkPage );
|
||
|
|
||
|
//
|
||
|
// We must shrink on common page boundaries since we borrow on
|
||
|
// common pages (even though the api is page granular)
|
||
|
//
|
||
|
|
||
|
ULONG cPagesToShrink = PgCommonPgTrunc( cPages );
|
||
|
|
||
|
if ( 0 == cPagesToShrink )
|
||
|
return 0;
|
||
|
|
||
|
Win4Assert( _iFirstNonShrunkPage ==
|
||
|
PgCommonPgTrunc( _iFirstNonShrunkPage ) );
|
||
|
Win4Assert( iFirstPage == PgCommonPgTrunc( iFirstPage ) );
|
||
|
|
||
|
// Take a lock so no one else tries to borrow or free any pages
|
||
|
|
||
|
Win4Assert ( 0 == _cMaxCachedBuffers );
|
||
|
|
||
|
CLock lock( _xMutex.GetReference() );
|
||
|
|
||
|
cShrunk = _stream->ShrinkFromFront( iFirstPage, cPagesToShrink );
|
||
|
Win4Assert( cShrunk == PgCommonPgTrunc( cShrunk ) );
|
||
|
|
||
|
_iFirstNonShrunkPage += cShrunk;
|
||
|
}
|
||
|
|
||
|
return cShrunk;
|
||
|
} //ShrinkFromFront
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPhysStorage::MinPageInUse, public
|
||
|
//
|
||
|
// Synopsis: Finds the smallest page in use within the cache
|
||
|
//
|
||
|
// Arguments: [minPage] -- returns the result
|
||
|
//
|
||
|
// Returns : TRUE if any page is in the cache; FALSE o/w
|
||
|
// If TRUE is returned, then minPage will contain the
|
||
|
// minimum page that is present in the cache.
|
||
|
//
|
||
|
// History: 26-Mar-98 dlee Moved from .hxx
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
BOOL CPhysStorage::MinPageInUse( ULONG &minPage )
|
||
|
{
|
||
|
if ( 0 == _cMaxCachedBuffers )
|
||
|
{
|
||
|
CLock lock( _xMutex.GetReference() );
|
||
|
|
||
|
return _cache.MinPageInUse(minPage);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CReadAccess readLock( _xRWAccess.GetReference() );
|
||
|
|
||
|
return _cache.MinPageInUse(minPage);
|
||
|
}
|
||
|
} //MinPageInUse
|
||
|
|