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.

1611 lines
46 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Corporation
  4. // Copyright (C) Microsoft Corporation, 1992 - 1998.
  5. //
  6. // File: PhysIdx.CXX
  7. //
  8. // Contents: FAT Buffer/Index package
  9. //
  10. // Classes: CPhysBuffer -- Buffer
  11. //
  12. // History: 05-Mar-92 KyleP Created
  13. // 07-Aug-92 KyleP Kernel implementation
  14. // 21-Apr-93 BartoszM Rewrote to use memory mapped files
  15. //
  16. //----------------------------------------------------------------------------
  17. #include <pch.cxx>
  18. #pragma hdrstop
  19. #include <pstore.hxx>
  20. #include <phystr.hxx>
  21. #include <eventlog.hxx>
  22. #include <psavtrak.hxx>
  23. //+-------------------------------------------------------------------------
  24. //
  25. // Function: PageToLow
  26. //
  27. // Returns: Low part of byte count in cPage pages
  28. //
  29. // History: 21-Apr-93 BartoszM Created
  30. //
  31. //--------------------------------------------------------------------------
  32. inline ULONG PageToLow(ULONG cPage)
  33. {
  34. return(cPage << CI_PAGE_SHIFT);
  35. }
  36. //+-------------------------------------------------------------------------
  37. //
  38. // Function: PageToHigh
  39. //
  40. // Returns: High part of byte count in cPage pages
  41. //
  42. // History: 21-Apr-93 BartoszM Created
  43. //
  44. //--------------------------------------------------------------------------
  45. inline ULONG PageToHigh(ULONG cPage)
  46. {
  47. return(cPage >> (ULONG_BITS - CI_PAGE_SHIFT));
  48. }
  49. //+-------------------------------------------------------------------------
  50. //
  51. // Function: ToPages
  52. //
  53. // Returns: Number of pages equivalent to (cbLow, cbHigh) bytes
  54. //
  55. // History: 21-Apr-93 BartoszM Created
  56. //
  57. //--------------------------------------------------------------------------
  58. inline ULONG ToPages ( ULONG cbLow, ULONG cbHigh )
  59. {
  60. Win4Assert ( cbHigh >> CI_PAGE_SHIFT == 0 );
  61. return (cbLow >> CI_PAGE_SHIFT) + (cbHigh << (ULONG_BITS - CI_PAGE_SHIFT));
  62. }
  63. //+-------------------------------------------------------------------------
  64. //
  65. // Function: PgCommonPgTrunc
  66. //
  67. // Returns: Number of pages truncated to multiple of largest common page
  68. //
  69. // History: 21-Apr-93 BartoszM Created
  70. //
  71. //--------------------------------------------------------------------------
  72. inline ULONG PgCommonPgTrunc(ULONG nPage)
  73. {
  74. return nPage & ~(PAGES_PER_COMMON_PAGE - 1);
  75. }
  76. inline ULONGLONG MAKEULONGLONG( ULONG l, ULONG h )
  77. {
  78. return ( l | ( h << 32 ) );
  79. }
  80. //+-------------------------------------------------------------------------
  81. //
  82. // Function: PgCommonPgRound
  83. //
  84. // Returns: Number of pages rounded to multiple of largest common page
  85. //
  86. // History: 21-Apr-93 BartoszM Created
  87. //
  88. //--------------------------------------------------------------------------
  89. inline ULONG PgCommonPgRound(ULONG nPage)
  90. {
  91. return (nPage + PAGES_PER_COMMON_PAGE - 1) & ~(PAGES_PER_COMMON_PAGE - 1);
  92. }
  93. //+-------------------------------------------------------------------------
  94. //
  95. // Member: CPhysBuffer::~CPhysBuffer
  96. //
  97. // History: 07-Dec-93 BartoszM Created
  98. //
  99. //--------------------------------------------------------------------------
  100. CPhysBuffer::~CPhysBuffer()
  101. {
  102. Win4Assert( !_fNeedToFlush );
  103. #if CIDBG == 1
  104. Flush( FALSE );
  105. #endif
  106. }
  107. //+-------------------------------------------------------------------------
  108. //
  109. // Member: CPhysBuffer::CPhysBuffer, public
  110. //
  111. // Synopsis: Allocates virtual memory
  112. //
  113. // Arguments: [stream] -- stream to use
  114. // [PageNo] -- page number
  115. // [fWrite] -- for writable memory
  116. // [fIntentToWrite] -- When fWrite is TRUE, whether the
  117. // client intends to write to the memory so
  118. // it needs to be flushed.
  119. //
  120. // History: 10-Mar-92 KyleP Created
  121. // 21-Apr-93 BartoszM Rewrote to use memory mapped files
  122. //
  123. //--------------------------------------------------------------------------
  124. CPhysBuffer::CPhysBuffer(
  125. PMmStream& stream,
  126. ULONG PageNo,
  127. BOOL fWrite,
  128. BOOL fIntentToWrite,
  129. BOOL fMap ) :
  130. _cRef( 0 ),
  131. _pphysNext( 0 ),
  132. _fNeedToFlush( fIntentToWrite ),
  133. _fWrite( fWrite ),
  134. _fMap( fMap ),
  135. _stream( stream )
  136. {
  137. _PageNo = PgCommonPgTrunc( PageNo );
  138. if ( _fMap )
  139. stream.Map( _strmBuf,
  140. COMMON_PAGE_SIZE,
  141. PageToLow( _PageNo ),
  142. PageToHigh( _PageNo ),
  143. fWrite );
  144. else
  145. {
  146. DWORD cbRead;
  147. _xBuffer.Init( COMMON_PAGE_SIZE );
  148. _stream.Read( _xBuffer.Get(),
  149. MAKEULONGLONG( PageToLow( _PageNo ),
  150. PageToHigh( _PageNo ) ),
  151. COMMON_PAGE_SIZE,
  152. cbRead );
  153. }
  154. } //CPhysBuffer
  155. //+-------------------------------------------------------------------------
  156. //
  157. // Member: CPhysBuffer::Flush, public
  158. //
  159. // Synopsis: Flushes the buffer to disk
  160. //
  161. // Arguments: [fFailFlush] -- If TRUE and flush fails, an exception is
  162. // raised. Otherwise failures to flush are
  163. // ignored.
  164. //
  165. // History: 10/30/98 dlee Moved out of header
  166. //
  167. //--------------------------------------------------------------------------
  168. void CPhysBuffer::Flush( BOOL fFailFlush )
  169. {
  170. if ( _fNeedToFlush )
  171. {
  172. Win4Assert( _fWrite );
  173. //
  174. // Reset the flag first, so subsequent attempts to flush won't
  175. // fail if first attempt fails. This is so we don't throw
  176. // in destructors. If we can't tolerate failures to flush, don't
  177. // even bother trying to write the data if we can't fail.
  178. //
  179. _fNeedToFlush = FALSE;
  180. if ( fFailFlush )
  181. {
  182. if ( _fMap )
  183. {
  184. // Flush the buffer and metadata in one call
  185. _strmBuf.Flush( TRUE );
  186. }
  187. else
  188. {
  189. _stream.Write( _xBuffer.Get(),
  190. MAKEULONGLONG( PageToLow( _PageNo ),
  191. PageToHigh( _PageNo ) ),
  192. COMMON_PAGE_SIZE );
  193. }
  194. }
  195. else
  196. {
  197. ciDebugOut(( DEB_WARN, "not flushing at %#p, in failure path?\n",
  198. this ));
  199. }
  200. }
  201. #if CIDBG == 1
  202. //
  203. // Make sure the bits on disk are the same as those in memory, since
  204. // the buffer is marked as not needing to be flushed. Only do this if
  205. // using read/write instead of mapping.
  206. //
  207. else if ( !_fMap && fFailFlush )
  208. {
  209. TRY
  210. {
  211. XArrayVirtual<BYTE> xTmp( COMMON_PAGE_SIZE );
  212. DWORD cbRead;
  213. _stream.Read( xTmp.Get(),
  214. MAKEULONGLONG( PageToLow( _PageNo ),
  215. PageToHigh( _PageNo ) ),
  216. COMMON_PAGE_SIZE,
  217. cbRead );
  218. if ( 0 != memcmp( xTmp.Get(), _xBuffer.Get(), COMMON_PAGE_SIZE ) )
  219. {
  220. ciDebugOut(( DEB_ERROR, "read: %p, have: %p\n",
  221. xTmp.Get(), _xBuffer.Get() ));
  222. Win4Assert( !"writable non-flushed buffer was modified!" );
  223. }
  224. }
  225. CATCH( CException, e )
  226. {
  227. // Ignore -- it's just an assert.
  228. }
  229. END_CATCH;
  230. }
  231. #endif // CIDBG == 1
  232. } //Flush
  233. //+-------------------------------------------------------------------------
  234. //
  235. // Member: CBufferCacheIter::CBufferCacheIter, public
  236. //
  237. // Synopsis: Constructor
  238. //
  239. // History: 18-Mar-93 BartoszM Created
  240. //
  241. //--------------------------------------------------------------------------
  242. CBufferCacheIter::CBufferCacheIter ( CBufferCache& cache )
  243. : _pPhysBuf(cache._aPhysBuf.GetPointer()), _idx(0), _cHash( cache._cHash )
  244. {
  245. do
  246. {
  247. _pBuf = _pPhysBuf[_idx];
  248. if (_pBuf != 0)
  249. break;
  250. _idx++;
  251. } while ( _idx < _cHash );
  252. } //CBufferCacheIter
  253. //+-------------------------------------------------------------------------
  254. //
  255. // Member: CBufferCacheIter::operator++, public
  256. //
  257. // Synopsis: Increments the iterator
  258. //
  259. // History: 18-Mar-93 BartoszM Created
  260. //
  261. //--------------------------------------------------------------------------
  262. void CBufferCacheIter::operator++()
  263. {
  264. Win4Assert(_pBuf != 0);
  265. _pBuf = _pBuf->Next();
  266. while (_pBuf == 0 && ++_idx < _cHash )
  267. _pBuf = _pPhysBuf[_idx];
  268. }
  269. //+-------------------------------------------------------------------------
  270. //
  271. // Member: CBufferCache::CBufferCache, public
  272. //
  273. // Synopsis: Constructor
  274. //
  275. // History: 18-Mar-93 BartoszM Created
  276. //
  277. //--------------------------------------------------------------------------
  278. CBufferCache::CBufferCache( unsigned cHash )
  279. : _cBuffers( 0 ),
  280. _cHash( cHash ),
  281. _aPhysBuf( cHash )
  282. #if CIDBG == 1
  283. , _cCacheHits( 0 ), _cCacheMisses( 0 )
  284. #endif
  285. {
  286. RtlZeroMemory( _aPhysBuf.GetPointer(), _aPhysBuf.SizeOf() );
  287. }
  288. //+-------------------------------------------------------------------------
  289. //
  290. // Member: CBufferCache::~CBufferCache, public
  291. //
  292. // Synopsis: Destructor
  293. //
  294. // History: 18-Mar-93 BartoszM Created
  295. //
  296. //--------------------------------------------------------------------------
  297. CBufferCache::~CBufferCache()
  298. {
  299. Free( FALSE );
  300. }
  301. //+-------------------------------------------------------------------------
  302. //
  303. // Member: CBufferCache::Free, public
  304. //
  305. // Synopsis: Deletes all buffers
  306. //
  307. // Arguments: [fFailFlush] -- If TRUE, this method will THROW if the flush
  308. // fails. If FALSE, flush failures are
  309. // ignored.
  310. //
  311. // History: 18-Mar-93 BartoszM Created
  312. //
  313. //--------------------------------------------------------------------------
  314. void CBufferCache::Free( BOOL fFailFlush )
  315. {
  316. if ( 0 != _cBuffers )
  317. {
  318. for ( unsigned i = 0; i < _cHash; i++ )
  319. {
  320. // Keep the cache consistent in case we fail flushing.
  321. while ( 0 != _aPhysBuf[i] )
  322. {
  323. CPhysBuffer * pTemp = _aPhysBuf[i];
  324. pTemp->Flush( fFailFlush );
  325. #if CIDBG == 1
  326. if ( pTemp->IsReferenced() )
  327. ciDebugOut(( DEB_WARN,
  328. "Buffer for page %ld still referenced.\n",
  329. pTemp->PageNumber() ));
  330. #endif // CIDBG == 1
  331. _aPhysBuf[i] = pTemp->Next();
  332. delete pTemp;
  333. _cBuffers--;
  334. }
  335. Win4Assert( 0 == _aPhysBuf[i] );
  336. }
  337. Win4Assert( 0 == _cBuffers );
  338. }
  339. } //Free
  340. //+-------------------------------------------------------------------------
  341. //
  342. // Member: CBufferCache::TryToRemoveBuffers, public
  343. //
  344. // Synopsis: Tries to remove unreferenced buffers from the cache.
  345. // If one is not found, it's not a problem.
  346. //
  347. // Arguments: [cMax] -- Recommended maximum in the cache
  348. //
  349. // History: 15-March-96 dlee Created
  350. //
  351. //--------------------------------------------------------------------------
  352. void CBufferCache::TryToRemoveBuffers( unsigned cMax )
  353. {
  354. for ( unsigned i = 0; i < 3 && ( Count() > cMax ); i++ )
  355. {
  356. // find an unreferenced page with the lowest (oldest) usn.
  357. ULONG ulPage = ULONG_MAX;
  358. ULONG ulUSN = ULONG_MAX;
  359. for ( CBufferCacheIter iter(*this); !iter.AtEnd(); ++iter )
  360. {
  361. CPhysBuffer * pPhys = iter.Get();
  362. Win4Assert( 0 != pPhys );
  363. if ( ( ulUSN > pPhys->GetUSN() ) &&
  364. ( !pPhys->IsReferenced() ) )
  365. {
  366. ulUSN = pPhys->GetUSN();
  367. ulPage = pPhys->PageNumber();
  368. }
  369. }
  370. if ( ULONG_MAX == ulPage )
  371. break;
  372. else
  373. Destroy( ulPage );
  374. }
  375. } //TryToRemoveBuffers
  376. //+-------------------------------------------------------------------------
  377. //
  378. // Member: CBufferCache::Search, public
  379. //
  380. // Synopsis: Searches the buffer hash for the requested buffer.
  381. //
  382. // Arguments: [ulPage] -- Page number of page to be found.
  383. //
  384. // Returns: 0 if page not currently buffered, or the pointer
  385. // to the (physical) buffer.
  386. //
  387. // History: 09-Mar-92 KyleP Created
  388. // 17-Mar-93 BartoszM Rewrote
  389. //
  390. //--------------------------------------------------------------------------
  391. CPhysBuffer * CBufferCache::Search(
  392. ULONG hash,
  393. ULONG commonPage )
  394. {
  395. // ciDebugOut (( DEB_ITRACE, "CBufferCache::Search %d\n", ulPage ));
  396. for ( CPhysBuffer *pPhys = _aPhysBuf[ hash ];
  397. 0 != pPhys;
  398. pPhys = pPhys->Next() )
  399. {
  400. if ( pPhys->PageNumber() == commonPage )
  401. break;
  402. }
  403. #if DBG==1 || CIDBG==1
  404. if ( 0 == pPhys )
  405. _cCacheMisses++;
  406. else
  407. _cCacheHits++;
  408. #endif // DBG==1 || CIDBG==1
  409. return pPhys;
  410. } //Search
  411. //+---------------------------------------------------------------------------
  412. //
  413. // Function: Flush
  414. //
  415. // Synopsis: Flushes all cached pages upto and including the specified
  416. // page to disk.
  417. //
  418. // Arguments: [nHighPage] -- The "CI" page number below which all pages
  419. // must be flushed to disk.
  420. //
  421. // History: 4-20-94 srikants Created
  422. //
  423. // Notes: nHighPage is in units of "CI" page (4K bytes) and not
  424. // CommonPage which is 64K.
  425. //
  426. //----------------------------------------------------------------------------
  427. void CBufferCache::Flush( ULONG nHighPage, BOOL fLeaveFlushFlagAlone )
  428. {
  429. nHighPage = PgCommonPgTrunc(nHighPage);
  430. ciDebugOut(( DEB_ITRACE, "CBufferCache : Flushing All Pages <= 0x%x\n",
  431. nHighPage ));
  432. if ( 0 == _cBuffers )
  433. return;
  434. //
  435. // We Must Flush All pages <= the given page number.
  436. //
  437. for ( CBufferCacheIter iter(*this); !iter.AtEnd(); ++iter )
  438. {
  439. CPhysBuffer * pPhys = iter.Get();
  440. Win4Assert( 0 != pPhys );
  441. if ( pPhys->PageNumber() <= nHighPage )
  442. {
  443. // Call flush even if not required to get assertions checked
  444. BOOL fRequiresFlush = pPhys->RequiresFlush();
  445. pPhys->Flush( TRUE );
  446. if ( fLeaveFlushFlagAlone && fRequiresFlush )
  447. pPhys->SetIntentToWrite();
  448. }
  449. }
  450. } //Flush
  451. //+-------------------------------------------------------------------------
  452. //
  453. // Member: CBufferCache::Destroy, public
  454. //
  455. // Synopsis: Deletes a buffer.
  456. //
  457. // Arguments: [ulPage] -- page number to be deleted
  458. //
  459. // History: 18-Mar-93 BartoszM Created
  460. //
  461. //--------------------------------------------------------------------------
  462. void CBufferCache::Destroy( ULONG ulPage, BOOL fFailFlush )
  463. {
  464. ulPage = PgCommonPgTrunc(ulPage);
  465. ULONG iHash = hash( ulPage );
  466. CPhysBuffer * pPhys = _aPhysBuf[ iHash ];
  467. Win4Assert( 0 != pPhys );
  468. // Find the page. Don't remove from the list until it is flushed.
  469. // Is it first? (common case)
  470. if (pPhys->PageNumber() == ulPage)
  471. {
  472. // This should be flushed before being unmapped. Fix for bug 132655
  473. pPhys->Flush( fFailFlush );
  474. _aPhysBuf[iHash] = pPhys->Next();
  475. }
  476. else
  477. {
  478. // Search linked list
  479. CPhysBuffer * pPhysPrev;
  480. do
  481. {
  482. pPhysPrev = pPhys;
  483. pPhys = pPhys->Next();
  484. Win4Assert( 0 != pPhys );
  485. }
  486. while (pPhys->PageNumber() != ulPage );
  487. // This should be flushed before being unmapped. Fix for bug 132655
  488. pPhys->Flush( fFailFlush );
  489. // delete from the linked list
  490. pPhysPrev->Link( pPhys->Next() );
  491. Win4Assert ( !pPhys->IsReferenced() );
  492. }
  493. _cBuffers--;
  494. delete pPhys;
  495. } //Destroy
  496. //+-------------------------------------------------------------------------
  497. //
  498. // Member: CBufferCache::Add, public
  499. //
  500. // Synopsis: Adds a buffer to the buffer hash.
  501. //
  502. // Arguments: [pPhysBuf] -- Buffer to add.
  503. //
  504. // History: 09-Mar-92 KyleP Created
  505. //
  506. //--------------------------------------------------------------------------
  507. void CBufferCache::Add( CPhysBuffer * pPhysBuf, ULONG hash )
  508. {
  509. _cBuffers++;
  510. pPhysBuf->Reference();
  511. pPhysBuf->Link( _aPhysBuf[ hash ] );
  512. _aPhysBuf[ hash ] = pPhysBuf;
  513. } //Add
  514. //+-------------------------------------------------------------------------
  515. //
  516. // Member: CBufferCache::MinPageInUse, public
  517. //
  518. // Synopsis: Finds the smallest page in use within the cache
  519. //
  520. // Returns : TRUE if any page is in the cache; FALSE o/w
  521. // If TRUE is returned, then minPageInUse will contain the
  522. // minimum page that is present in the cache.
  523. //
  524. // Arguments: minPageInUse (out) Will be set to the minimum page in
  525. // the cache if the return value is TRUE.
  526. //
  527. // History: 02-May-94 DwightKr Created
  528. // 08-Dec-94 SrikantS Fixed to return the correct value for
  529. // empty cache.
  530. //
  531. //--------------------------------------------------------------------------
  532. BOOL CBufferCache::MinPageInUse(ULONG &minPageInUse)
  533. {
  534. ULONG minPage = 0xFFFFFFFF;
  535. for (CBufferCacheIter iter(*this); !iter.AtEnd(); ++iter)
  536. {
  537. minPage = min(minPage, iter.Get()->PageNumber() );
  538. }
  539. if ( minPage == 0xFFFFFFFF )
  540. {
  541. return FALSE;
  542. }
  543. else
  544. {
  545. minPageInUse = minPage;
  546. return TRUE;
  547. }
  548. } //MinPageInUse
  549. //+-------------------------------------------------------------------------
  550. //
  551. // Member: CPhysStorage::CPhysStorage, public
  552. //
  553. // Effects: Creates a new index.
  554. //
  555. // Arguments: [storage] -- physical storage to create index in
  556. // [obj] -- Storage object
  557. // [objectId] -- Index ID
  558. // [cPageReqSize] --
  559. // [stream] -- Memory mapped stream
  560. // [fThrowCorruptErrorOnFailures -- If true, then errors during
  561. // opening will be marked as
  562. // catalog corruption
  563. // [cCachedItems] -- # of non-referenced pages to cache
  564. // [fAllowReadAhead] -- if TRUE, non-mapped IO will be used
  565. // when the physical storage says it's ok.
  566. //
  567. // Signals: Cannot create.
  568. //
  569. // Modifies: A new index (file) is created on the disk.
  570. //
  571. // History: 07-Mar-92 KyleP Created
  572. //
  573. //--------------------------------------------------------------------------
  574. CPhysStorage::CPhysStorage( PStorage & storage,
  575. PStorageObject& obj,
  576. WORKID objectId,
  577. UINT cpageReqSize,
  578. PMmStream * stream,
  579. BOOL fThrowCorruptErrorOnFailures,
  580. unsigned cCachedItems,
  581. BOOL fAllowReadAhead )
  582. : _sigPhysStorage(eSigPhysStorage),
  583. _fWritable( TRUE ),
  584. _objectId( objectId ),
  585. _obj ( obj),
  586. _cpageUsedFileSize( 0 ),
  587. _storage(storage),
  588. _stream(stream),
  589. _iFirstNonShrunkPage( 0 ),
  590. _fThrowCorruptErrorOnFailures(fThrowCorruptErrorOnFailures),
  591. _cpageGrowth(1),
  592. _cMaxCachedBuffers( cCachedItems ),
  593. _cache( __max( 17, ( cCachedItems & 1 ) ? cCachedItems :
  594. cCachedItems + 1 ) ),
  595. _usnGen( 0 ),
  596. _fAllowReadAhead( fAllowReadAhead )
  597. {
  598. //
  599. // Use different locking schemes depending on usage
  600. //
  601. if ( 0 == _cMaxCachedBuffers )
  602. _xMutex.Set( new CMutexSem() );
  603. else
  604. _xRWAccess.Set( new CReadWriteAccess() );
  605. if ( _stream.IsNull() || !_stream->Ok() )
  606. {
  607. ciDebugOut((DEB_ERROR, "Index Creation failed %08x\n", objectId ));
  608. if ( _fThrowCorruptErrorOnFailures || _stream->FStatusFileNotFound() )
  609. {
  610. //
  611. // We don't have code to handle such failures, hence mark
  612. // catalog as corrupt; otherwise throw e_fail
  613. //
  614. Win4Assert( !"Corrupt catalog" );
  615. _storage.ReportCorruptComponent( L"PhysStorage1" );
  616. THROW( CException( CI_CORRUPT_DATABASE ));
  617. }
  618. else
  619. THROW( CException( E_FAIL ));
  620. }
  621. if (cpageReqSize == 0)
  622. cpageReqSize = 1;
  623. TRY
  624. {
  625. _GrowFile( cpageReqSize );
  626. }
  627. CATCH( CException, e )
  628. {
  629. //
  630. // There may not be enough space on the disk to allocate the
  631. // requested size. If not enough space, do the best that we
  632. // can and hope space gets freed.
  633. //
  634. _GrowFile( 1 );
  635. }
  636. END_CATCH
  637. ciDebugOut(( DEB_ITRACE, "Physical index %08x created.\n", _objectId ));
  638. }
  639. //+-------------------------------------------------------------------------
  640. //
  641. // Member: CPhysStorage::CPhysStorage, public
  642. //
  643. // Effects: Opens an existing index.
  644. //
  645. // Arguments: [storage] -- physical storage to open index in
  646. // [obj] -- Storage object
  647. // [objectId] -- Index ID.
  648. // [stream] -- Memory mapped stream
  649. // [mode] -- Open mode
  650. // [fThrowCorruptErrorOnFailures -- If true, then errors during
  651. // opening will be marked as
  652. // catalog corruption
  653. // [cCachedItems] -- # of non-referenced pages to cache
  654. // [fAllowReadAhead] -- if TRUE, non-mapped IO will be used
  655. // when the physical storage says it's ok.
  656. //
  657. // History: 07-Mar-92 KyleP Created
  658. //
  659. //--------------------------------------------------------------------------
  660. CPhysStorage::CPhysStorage( PStorage & storage,
  661. PStorageObject& obj,
  662. WORKID objectId,
  663. PMmStream * stream,
  664. PStorage::EOpenMode mode,
  665. BOOL fThrowCorruptErrorOnFailures,
  666. unsigned cCachedItems,
  667. BOOL fAllowReadAhead )
  668. : _sigPhysStorage(eSigPhysStorage),
  669. _fWritable( PStorage::eOpenForWrite == mode ),
  670. _objectId( objectId ),
  671. _obj ( obj ),
  672. _cpageUsedFileSize( 0 ),
  673. _storage(storage),
  674. _stream(stream),
  675. _fThrowCorruptErrorOnFailures(fThrowCorruptErrorOnFailures),
  676. _cpageGrowth(1),
  677. _iFirstNonShrunkPage( 0 ),
  678. _cMaxCachedBuffers( cCachedItems ),
  679. _cache( __max( 17, ( cCachedItems & 1 ) ? cCachedItems :
  680. cCachedItems + 1 ) ),
  681. _usnGen( 0 ),
  682. _fAllowReadAhead( fAllowReadAhead )
  683. {
  684. //
  685. // Use different locking schemes depending on usage
  686. //
  687. if ( 0 == _cMaxCachedBuffers )
  688. _xMutex.Set( new CMutexSem() );
  689. else
  690. _xRWAccess.Set( new CReadWriteAccess() );
  691. if ( _stream.IsNull() || !_stream->Ok() )
  692. {
  693. ciDebugOut(( DEB_ERROR, "Open of index %08x failed\n", objectId ));
  694. if ( _fThrowCorruptErrorOnFailures || _stream->FStatusFileNotFound() )
  695. {
  696. //
  697. // We don't have code to handle such failures, hence mark
  698. // catalog as corrupt; otherwise throw e_fail
  699. //
  700. Win4Assert( !"Corrupt catalog" );
  701. _storage.ReportCorruptComponent( L"PhysStorage2" );
  702. THROW( CException( CI_CORRUPT_DATABASE ));
  703. }
  704. else
  705. THROW( CException( E_FAIL ));
  706. }
  707. _cpageFileSize = ToPages(_stream->SizeLow(), _stream->SizeHigh());
  708. ciDebugOut(( DEB_ITRACE, "Physical index %08x (%d pages) opened.\n",
  709. _objectId, _cpageFileSize ));
  710. }
  711. //+---------------------------------------------------------------------------
  712. //
  713. // Member: CPhysStorage::MakeBackupCopy
  714. //
  715. // Synopsis: Makes a backup copy of the current storage using the
  716. // destination storage.
  717. //
  718. // Arguments: [dst] - Destination storage.
  719. // [progressTracker] - Progress tracking
  720. //
  721. // History: 3-18-97 srikants Created
  722. //
  723. //----------------------------------------------------------------------------
  724. void CPhysStorage::MakeBackupCopy( CPhysStorage & dst,
  725. PSaveProgressTracker & progressTracker )
  726. {
  727. //
  728. // Copy one page at a time.
  729. //
  730. ULONG * pSrcNext = 0;
  731. ULONG * pDstNext = 0;
  732. unsigned iCurrBorrow = 0; // always has the current buffers borrowed.
  733. SCODE sc = S_OK;
  734. TRY
  735. {
  736. //
  737. // Grow the destination to be as big as the current one.
  738. //
  739. dst._GrowFile( _cpageFileSize );
  740. if ( _cpageFileSize > 0 )
  741. {
  742. iCurrBorrow = 0;
  743. pSrcNext = BorrowBuffer(iCurrBorrow, FALSE );
  744. pDstNext = dst.BorrowNewBuffer(iCurrBorrow);
  745. const unsigned iLastPage = _cpageFileSize-1;
  746. for ( unsigned j = 0; j < _cpageFileSize; j++ )
  747. {
  748. ULONG * pSrc = pSrcNext;
  749. ULONG * pDst = pDstNext;
  750. Win4Assert( 0 != pSrc && 0 != pDst );
  751. RtlCopyMemory( pDst, pSrc, CI_PAGE_SIZE );
  752. //
  753. // Before returning buffers, grab the next ones to prevent
  754. // the large page from being taken out of cache.
  755. //
  756. if ( j != iLastPage )
  757. {
  758. iCurrBorrow = j+1;
  759. pSrcNext = BorrowBuffer(iCurrBorrow, FALSE );
  760. pDstNext = dst.BorrowNewBuffer(iCurrBorrow);
  761. }
  762. else
  763. {
  764. pSrcNext = pDstNext = 0;
  765. }
  766. ReturnBuffer( j );
  767. dst.ReturnBuffer( j, TRUE );
  768. if ( progressTracker.IsAbort() )
  769. THROW( CException( STATUS_TOO_LATE ) );
  770. //
  771. // Update the progress every 64 K.
  772. //
  773. if ( (j % PAGES_PER_COMMON_PAGE) == (PAGES_PER_COMMON_PAGE-1) )
  774. progressTracker.UpdateCopyProgress( (ULONG) j, _cpageFileSize );
  775. }
  776. }
  777. dst.Flush();
  778. }
  779. CATCH( CException, e )
  780. {
  781. if ( pSrcNext )
  782. ReturnBuffer( iCurrBorrow );
  783. if ( pDstNext )
  784. dst.ReturnBuffer(iCurrBorrow);
  785. sc = e.GetErrorCode();
  786. }
  787. END_CATCH
  788. if ( S_OK != sc )
  789. {
  790. //
  791. // _GrowFile throws CI_CORRUPT_DATABASE if it cannot create
  792. // the file that big. We don't want to consider that a
  793. // corruption.
  794. //
  795. if ( CI_CORRUPT_DATABASE == sc )
  796. sc = E_FAIL;
  797. THROW( CException( sc ) );
  798. }
  799. //
  800. // Consider 100% of the copy work is done.
  801. //
  802. progressTracker.UpdateCopyProgress( 1,1 );
  803. }
  804. //+-------------------------------------------------------------------------
  805. //
  806. // Member: CPhysStorage::~CPhysStorage, public
  807. //
  808. // Synopsis: closes index.
  809. //
  810. // History: 09-Mar-92 KyleP Created
  811. //
  812. // Notes: Don't write back pages. This is either a read only
  813. // index or we are aborting a merge. Pages are written
  814. // back after a successful merge using Reopen().
  815. //
  816. //--------------------------------------------------------------------------
  817. CPhysStorage::~CPhysStorage()
  818. {
  819. ciDebugOut(( DEB_ITRACE, "Physical index %lx closed.\n", _objectId ));
  820. _cache.Free( FALSE );
  821. }
  822. //+-------------------------------------------------------------------------
  823. //
  824. // Member: CPhysStorage::Close, public
  825. //
  826. // Effects: Closes the stream.
  827. //
  828. // History: 07-Mar-92 KyleP Created
  829. //
  830. //--------------------------------------------------------------------------
  831. void CPhysStorage::Close()
  832. {
  833. _cache.Free();
  834. _stream.Free();
  835. }
  836. //+---------------------------------------------------------------------------
  837. //
  838. // Function: Flush
  839. //
  840. // Synopsis: Flushes all the pages that were unmapped since the last
  841. // flush.
  842. //
  843. // History: 4-29-94 srikants Created
  844. //
  845. // Notes:
  846. //
  847. //----------------------------------------------------------------------------
  848. void CPhysStorage::Flush( BOOL fLeaveFlushFlagAlone )
  849. {
  850. Win4Assert( _fWritable );
  851. // Grab the lock to make sure the pages aren't deleted while
  852. // they are being flushed.
  853. if ( 0 == _cMaxCachedBuffers )
  854. {
  855. CLock lock( _xMutex.GetReference() );
  856. _cache.Flush( ULONG_MAX, fLeaveFlushFlagAlone );
  857. }
  858. else
  859. {
  860. CReadAccess readLock( _xRWAccess.GetReference() );
  861. _cache.Flush( ULONG_MAX, fLeaveFlushFlagAlone );
  862. }
  863. } //Flush
  864. //+---------------------------------------------------------------------------
  865. //
  866. // Function: ShrinkToFit
  867. //
  868. // Synopsis: Reduces the size of the stream by giving up pages in the end
  869. // which are not needed.
  870. //
  871. // Arguments: (none)
  872. //
  873. // History: 9-08-94 srikants Created
  874. //
  875. // Notes:
  876. //
  877. //----------------------------------------------------------------------------
  878. void CPhysStorage::ShrinkToFit()
  879. {
  880. _cache.Free();
  881. _stream->SetSize( _storage,
  882. PageToLow(_cpageUsedFileSize),
  883. PageToHigh(_cpageUsedFileSize) );
  884. _cpageFileSize = _cpageUsedFileSize;
  885. }
  886. //+-------------------------------------------------------------------------
  887. //
  888. // Member: CPhysStorage::Reopen, public
  889. //
  890. // Synopsis: Flushes, closes, and reopens for reading.
  891. //
  892. // History: 09-Mar-92 KyleP Created
  893. // 26-Aug-92 BartoszM Separated from destructor
  894. // 06-May-94 Srikants Add fSetSize as a parameter to truncate
  895. // the stream optionally.
  896. //
  897. //--------------------------------------------------------------------------
  898. void CPhysStorage::Reopen( BOOL fWritable )
  899. {
  900. // No need to lock.
  901. _cache.Free(); // will flush all buffers
  902. if ( _fWritable && !_stream.IsNull() )
  903. {
  904. //
  905. // Give it an accurate used length.
  906. //
  907. _stream->SetSize( _storage,
  908. PageToLow(_cpageUsedFileSize),
  909. PageToHigh(_cpageUsedFileSize) );
  910. _cpageFileSize = _cpageUsedFileSize;
  911. _stream.Free();
  912. //
  913. // reopen shadow indexes as read and master indexes as write to
  914. // support shrink from front.
  915. //
  916. _fWritable = fWritable;
  917. }
  918. //
  919. // reopen it with the new mode
  920. //
  921. ReOpenStream();
  922. if ( _stream.IsNull() || !_stream->Ok() )
  923. {
  924. ciDebugOut(( DEB_ERROR, "Re-Open file of index %08x failed\n",
  925. _objectId ));
  926. if ( _fThrowCorruptErrorOnFailures || _stream->FStatusFileNotFound() )
  927. {
  928. //
  929. // We don't have code to handle such failures, hence mark
  930. // catalog as corrupt; otherwise throw e_fail
  931. //
  932. Win4Assert( !"Corrupt catalog" );
  933. _storage.ReportCorruptComponent( L"PhysStorage3" );
  934. THROW( CException( CI_CORRUPT_DATABASE ));
  935. }
  936. else
  937. THROW( CException( E_FAIL ));
  938. }
  939. _cpageFileSize = ToPages(_stream->SizeLow(), _stream->SizeHigh());
  940. ciDebugOut(( DEB_ITRACE, "Physical index %08x opened.\n", _objectId ));
  941. } //Reopen
  942. //+-------------------------------------------------------------------------
  943. //
  944. // Member: CPhysStorage::LokBorrowNewBuffer, public
  945. //
  946. // Arguments: [hash] -- hash value for doing the lookup
  947. // [commonPage]] -- the common page of nPage
  948. // [nPage] -- page number
  949. //
  950. // Returns: A buffer for a new (unused) page.
  951. //
  952. // History: 03-Mar-98 dlee Created from existing BorrowNewBuffer
  953. //
  954. // Notes: On creation, the buffer is filled with zeros.
  955. //
  956. //--------------------------------------------------------------------------
  957. ULONG * CPhysStorage::LokBorrowNewBuffer(
  958. ULONG hash,
  959. ULONG commonPage,
  960. ULONG nPage )
  961. {
  962. CPhysBuffer* pBuf = _cache.Search( hash, commonPage );
  963. if (pBuf)
  964. {
  965. pBuf->Reference();
  966. Win4Assert( nPage < _cpageFileSize );
  967. // fIntentToWrite should be TRUE
  968. pBuf->SetIntentToWrite();
  969. }
  970. else
  971. {
  972. // If we've moved beyond the previous end of file, set the new
  973. // size of file.
  974. if ( nPage >= _cpageFileSize )
  975. _GrowFile( nPage + _cpageGrowth );
  976. // First try to release unreferenced buffers if the cache is full.
  977. _cache.TryToRemoveBuffers( _cMaxCachedBuffers );
  978. pBuf = new CPhysBuffer( _stream.GetReference(),
  979. nPage,
  980. TRUE, // writable
  981. TRUE, // intent to write
  982. ! ( _fAllowReadAhead &&
  983. _storage.FavorReadAhead() ) );
  984. _cache.Add( pBuf, hash );
  985. }
  986. Win4Assert( nPage + 1 > _cpageUsedFileSize );
  987. _cpageUsedFileSize = nPage + 1;
  988. Win4Assert( _cpageUsedFileSize <= _cpageFileSize );
  989. RtlZeroMemory( pBuf->GetPage( nPage ), CI_PAGE_SIZE );
  990. return pBuf->GetPage( nPage );
  991. } //LokBorrowNewBuffer
  992. //+-------------------------------------------------------------------------
  993. //
  994. // Member: CPhysStorage::BorrowNewBuffer, public
  995. //
  996. // Arguments: [nPage] -- page number
  997. //
  998. // Returns: A buffer for a new (unused) page.
  999. //
  1000. // Signals: No more disk space.
  1001. //
  1002. // History: 09-Mar-92 KyleP Created
  1003. //
  1004. // Notes: On creation, the buffer is filled with zeros.
  1005. //
  1006. //--------------------------------------------------------------------------
  1007. ULONG * CPhysStorage::BorrowNewBuffer( ULONG nPage )
  1008. {
  1009. Win4Assert( _fWritable );
  1010. ULONG commonPage = PgCommonPgTrunc( nPage );
  1011. ULONG hash = _cache.hash( commonPage );
  1012. // Use appropriate locking based on whether any pages are in the cache.
  1013. if ( 0 == _cMaxCachedBuffers )
  1014. {
  1015. CLock lock( _xMutex.GetReference() );
  1016. return LokBorrowNewBuffer( hash, commonPage, nPage );
  1017. }
  1018. else
  1019. {
  1020. CWriteAccess writeLock( _xRWAccess.GetReference() );
  1021. return LokBorrowNewBuffer( hash, commonPage, nPage );
  1022. }
  1023. } //BorrowNewBuffer
  1024. //+-------------------------------------------------------------------------
  1025. //
  1026. // Member: CPhysStorage::LokBorrowOrAddBuffer, public
  1027. //
  1028. // Arguments: [hash] -- hash value for doing the lookup
  1029. // [commonPage]] -- the common page of nPage
  1030. // [nPage] -- page number
  1031. // [fAdd] -- if TRUE, create the page if not found
  1032. // if FALSE and not found, return 0
  1033. // [fWritable] -- if TRUE, the page is writable
  1034. // [fIntentToWrite] -- TRUE if the caller intends to write
  1035. //
  1036. // Returns: A buffer loaded with page [nPage]
  1037. //
  1038. // History: 09-Mar-92 KyleP Created
  1039. //
  1040. //--------------------------------------------------------------------------
  1041. ULONG * CPhysStorage::LokBorrowOrAddBuffer(
  1042. ULONG hash,
  1043. ULONG commonPage,
  1044. ULONG nPage,
  1045. BOOL fAdd,
  1046. BOOL fWritable,
  1047. BOOL fIntentToWrite )
  1048. {
  1049. // First, look in the cache. Either we haven't looked yet (if no
  1050. // caching) or another thread may have added it between the time the
  1051. // read lock was released and the write lock taken.
  1052. Win4Assert( commonPage >= _iFirstNonShrunkPage );
  1053. CPhysBuffer * pbuf = _cache.Search( hash, commonPage );
  1054. if ( 0 != pbuf )
  1055. {
  1056. Win4Assert( !fWritable || pbuf->IsWritable() );
  1057. pbuf->Reference();
  1058. if ( fIntentToWrite )
  1059. pbuf->SetIntentToWrite();
  1060. return pbuf->GetPage( nPage );
  1061. }
  1062. ULONG * pulRet = 0;
  1063. if ( fAdd )
  1064. {
  1065. pbuf = new CPhysBuffer( _stream.GetReference(),
  1066. nPage,
  1067. fWritable,
  1068. fIntentToWrite,
  1069. ! ( _fAllowReadAhead &&
  1070. _storage.FavorReadAhead() ) );
  1071. _cache.Add( pbuf, hash );
  1072. pulRet = pbuf->GetPage( nPage );
  1073. // Try to release unreferenced buffers if the cache is full.
  1074. _cache.TryToRemoveBuffers( _cMaxCachedBuffers );
  1075. }
  1076. return pulRet;
  1077. } //LokBorrowOrAddBuffer
  1078. //+-------------------------------------------------------------------------
  1079. //
  1080. // Member: CPhysStorage::BorrowBuffer, public
  1081. //
  1082. // Arguments: [nPage] -- Page number of buffer to find.
  1083. //
  1084. // Returns: A buffer loaded with page [nPage]
  1085. //
  1086. // Signals: Out of memory? Buffer not found?
  1087. //
  1088. // History: 09-Mar-92 KyleP Created
  1089. //
  1090. //--------------------------------------------------------------------------
  1091. ULONG * CPhysStorage::BorrowBuffer(
  1092. ULONG nPage,
  1093. BOOL fWritable,
  1094. BOOL fIntentToWrite )
  1095. {
  1096. ULONG commonPage = PgCommonPgTrunc( nPage );
  1097. ULONG hash = _cache.hash( commonPage );
  1098. Win4Assert( fWritable || !fIntentToWrite );
  1099. // Make sure we didn't walk off the end.
  1100. if ( nPage >= _cpageFileSize )
  1101. {
  1102. ciDebugOut(( DEB_WARN, "Asking for page %d, file size %d pages\n",
  1103. nPage, _cpageFileSize ));
  1104. Win4Assert( !"BorrowBuffer walked off end of file" );
  1105. _storage.ReportCorruptComponent( L"PhysStorage4" );
  1106. THROW( CException( CI_CORRUPT_DATABASE ) );
  1107. }
  1108. // If caching, try finding it in the cache first under a read lock
  1109. // This is the fast path for when the entire property cache is in memory.
  1110. if ( 0 != _cMaxCachedBuffers )
  1111. {
  1112. CReadAccess readLock( _xRWAccess.GetReference() );
  1113. ULONG * p = LokBorrowOrAddBuffer( hash, commonPage, nPage, FALSE,
  1114. fWritable, fIntentToWrite );
  1115. if ( 0 != p )
  1116. return p;
  1117. }
  1118. // Ok, maybe we have to add it.
  1119. if ( 0 == _cMaxCachedBuffers )
  1120. {
  1121. CLock lock( _xMutex.GetReference() );
  1122. return LokBorrowOrAddBuffer( hash, commonPage, nPage, TRUE,
  1123. fWritable, fIntentToWrite );
  1124. }
  1125. else
  1126. {
  1127. CWriteAccess writeLock( _xRWAccess.GetReference() );
  1128. return LokBorrowOrAddBuffer( hash, commonPage, nPage, TRUE,
  1129. fWritable, fIntentToWrite );
  1130. }
  1131. } //BorrowBuffer
  1132. //+-------------------------------------------------------------------------
  1133. //
  1134. // Member: CPhysStorage::ReturnBuffer, public
  1135. //
  1136. // Synopsis: Dereference and return the buffer to the pool.
  1137. //
  1138. // Arguments: [nPage] -- Page number of buffer being returned.
  1139. // [fFlush] -- TRUE if the buffer should be flushed if this
  1140. // is the last reference to the buffer.
  1141. // [fFailFlush] -- TRUE if an exception should be raised if
  1142. // the flush fails. Note that even if fFlush
  1143. // is FALSE, the buffer may still be flushed
  1144. // because previously it was borrowed with
  1145. // intent to write.
  1146. //
  1147. // Notes: If flush throws, this call doesn't return the buffer.
  1148. //
  1149. // History: 16-Mar-93 BartoszM Created
  1150. //
  1151. //--------------------------------------------------------------------------
  1152. void CPhysStorage::ReturnBuffer(
  1153. ULONG nPage,
  1154. BOOL fFlush,
  1155. BOOL fFailFlush )
  1156. {
  1157. ULONG commonPage = PgCommonPgTrunc( nPage );
  1158. ULONG hash = _cache.hash( commonPage );
  1159. // if caching is off, grab the write lock, else the read lock
  1160. if ( 0 == _cMaxCachedBuffers )
  1161. {
  1162. CLock lock( _xMutex.GetReference() );
  1163. Win4Assert( commonPage >= _iFirstNonShrunkPage );
  1164. CPhysBuffer *pbuf = _cache.Search( hash, commonPage );
  1165. Win4Assert( 0 != pbuf );
  1166. Win4Assert( pbuf->IsReferenced() );
  1167. // If the refcount is 1, it's about to be destroyed. Do the Flush
  1168. // now, in case it fails, before mucking with any data structures.
  1169. BOOL fDestroy = pbuf->IsRefCountOne();
  1170. if ( fDestroy && fFlush )
  1171. {
  1172. Win4Assert( pbuf->IsWritable() );
  1173. pbuf->Flush( fFailFlush );
  1174. }
  1175. pbuf->DeReference( _usnGen++ );
  1176. if ( fDestroy )
  1177. {
  1178. Win4Assert( !pbuf->IsReferenced() );
  1179. _cache.Destroy( nPage, fFailFlush );
  1180. Win4Assert( !_cache.Search( hash, commonPage ) );
  1181. }
  1182. }
  1183. else
  1184. {
  1185. CReadAccess readLock( _xRWAccess.GetReference() );
  1186. Win4Assert( commonPage >= _iFirstNonShrunkPage );
  1187. CPhysBuffer *pbuf = _cache.Search( hash, commonPage );
  1188. Win4Assert( 0 != pbuf );
  1189. Win4Assert( pbuf->IsReferenced() );
  1190. if ( fFlush )
  1191. {
  1192. Win4Assert( pbuf->IsWritable() );
  1193. if ( pbuf->IsRefCountOne() )
  1194. pbuf->Flush( fFailFlush );
  1195. }
  1196. pbuf->DeReference( _usnGen++ );
  1197. // leave it in the cache -- it'll be cleared out by BorrowBuffer
  1198. }
  1199. } //ReturnBuffer
  1200. //+-------------------------------------------------------------------------
  1201. //
  1202. // Member: CPhysStorage::RequiresFlush, public
  1203. //
  1204. // Synopsis: Determines if the page is scheduled for flushing
  1205. //
  1206. // Arguments: [nPage] -- Page number to check
  1207. //
  1208. // Returns: TRUE if the buffer will be flushed if Flush or Destroy is
  1209. // called.
  1210. //
  1211. // History: 3-Nov-98 dlee Created
  1212. //
  1213. //--------------------------------------------------------------------------
  1214. BOOL CPhysStorage::RequiresFlush( ULONG nPage )
  1215. {
  1216. ULONG commonPage = PgCommonPgTrunc( nPage );
  1217. ULONG hash = _cache.hash( commonPage );
  1218. // if caching is off, grab the write lock, else the read lock
  1219. if ( 0 == _cMaxCachedBuffers )
  1220. {
  1221. CLock lock( _xMutex.GetReference() );
  1222. Win4Assert( commonPage >= _iFirstNonShrunkPage );
  1223. CPhysBuffer *pbuf = _cache.Search( hash, commonPage );
  1224. Win4Assert( 0 != pbuf );
  1225. Win4Assert( pbuf->IsReferenced() );
  1226. return pbuf->RequiresFlush();
  1227. }
  1228. else
  1229. {
  1230. CReadAccess readLock( _xRWAccess.GetReference() );
  1231. Win4Assert( commonPage >= _iFirstNonShrunkPage );
  1232. CPhysBuffer *pbuf = _cache.Search( hash, commonPage );
  1233. Win4Assert( 0 != pbuf );
  1234. Win4Assert( pbuf->IsReferenced() );
  1235. return pbuf->RequiresFlush();
  1236. }
  1237. } //RequiresFlush
  1238. //+-------------------------------------------------------------------------
  1239. //
  1240. // Member: CPhysStorage::_GrowFile, private
  1241. //
  1242. // Synopsis: Increases the physical (disk) size of the file.
  1243. //
  1244. // Arguments: [cpageSize] -- New file size, in pages.
  1245. //
  1246. // Signals: Out of space.
  1247. //
  1248. // History: 09-Mar-92 KyleP Created
  1249. //
  1250. //--------------------------------------------------------------------------
  1251. void CPhysStorage::_GrowFile( ULONG cpageSize )
  1252. {
  1253. Win4Assert( cpageSize > 0 );
  1254. cpageSize = PgCommonPgRound(cpageSize);
  1255. ciDebugOut(( DEB_ITRACE, " Growing Index to %d pages\n", cpageSize ));
  1256. _stream->SetSize( _storage,
  1257. PageToLow(cpageSize),
  1258. PageToHigh(cpageSize) );
  1259. if (!_stream->Ok())
  1260. {
  1261. ciDebugOut(( DEB_ERROR, "GrowFile of index %08x failed: %d\n",
  1262. _objectId ));
  1263. if ( _fThrowCorruptErrorOnFailures )
  1264. {
  1265. //
  1266. // We don't have code to handle such failures, hence mark
  1267. // catalog as corrupt; otherwise throw e_fail
  1268. //
  1269. Win4Assert( !"Corrupt catalog" );
  1270. _storage.ReportCorruptComponent( L"PhysStorage5" );
  1271. THROW( CException( CI_CORRUPT_DATABASE ) );
  1272. }
  1273. else
  1274. THROW( CException( E_FAIL ) );
  1275. }
  1276. _cpageFileSize = cpageSize;
  1277. } //_GrowFile
  1278. //+-------------------------------------------------------------------------
  1279. //
  1280. // Member: CPhysStorage::ShrinkFromFront, public
  1281. //
  1282. // Synopsis: Makes the front part of a file sparse
  1283. //
  1284. // Arguments: [iFirstPage] -- first 4k page -- 64k granular
  1285. // [cPages] -- number of 4k pages
  1286. //
  1287. // Returns: The # of 4k pages actually shrunk, maybe 0
  1288. //
  1289. // History: 09-Jan-97 dlee Moved from .hxx
  1290. //
  1291. //--------------------------------------------------------------------------
  1292. ULONG CPhysStorage::ShrinkFromFront(
  1293. ULONG iFirstPage,
  1294. ULONG cPages )
  1295. {
  1296. ULONG cShrunk = 0;
  1297. if ( _storage.SupportsShrinkFromFront() )
  1298. {
  1299. //
  1300. // Make sure the caller isn't leaving any gaps and was paying
  1301. // attention to the return value from previous calls.
  1302. //
  1303. Win4Assert( iFirstPage == _iFirstNonShrunkPage );
  1304. //
  1305. // We must shrink on common page boundaries since we borrow on
  1306. // common pages (even though the api is page granular)
  1307. //
  1308. ULONG cPagesToShrink = PgCommonPgTrunc( cPages );
  1309. if ( 0 == cPagesToShrink )
  1310. return 0;
  1311. Win4Assert( _iFirstNonShrunkPage ==
  1312. PgCommonPgTrunc( _iFirstNonShrunkPage ) );
  1313. Win4Assert( iFirstPage == PgCommonPgTrunc( iFirstPage ) );
  1314. // Take a lock so no one else tries to borrow or free any pages
  1315. Win4Assert ( 0 == _cMaxCachedBuffers );
  1316. CLock lock( _xMutex.GetReference() );
  1317. cShrunk = _stream->ShrinkFromFront( iFirstPage, cPagesToShrink );
  1318. Win4Assert( cShrunk == PgCommonPgTrunc( cShrunk ) );
  1319. _iFirstNonShrunkPage += cShrunk;
  1320. }
  1321. return cShrunk;
  1322. } //ShrinkFromFront
  1323. //+-------------------------------------------------------------------------
  1324. //
  1325. // Member: CPhysStorage::MinPageInUse, public
  1326. //
  1327. // Synopsis: Finds the smallest page in use within the cache
  1328. //
  1329. // Arguments: [minPage] -- returns the result
  1330. //
  1331. // Returns : TRUE if any page is in the cache; FALSE o/w
  1332. // If TRUE is returned, then minPage will contain the
  1333. // minimum page that is present in the cache.
  1334. //
  1335. // History: 26-Mar-98 dlee Moved from .hxx
  1336. //
  1337. //--------------------------------------------------------------------------
  1338. BOOL CPhysStorage::MinPageInUse( ULONG &minPage )
  1339. {
  1340. if ( 0 == _cMaxCachedBuffers )
  1341. {
  1342. CLock lock( _xMutex.GetReference() );
  1343. return _cache.MinPageInUse(minPage);
  1344. }
  1345. else
  1346. {
  1347. CReadAccess readLock( _xRWAccess.GetReference() );
  1348. return _cache.MinPageInUse(minPage);
  1349. }
  1350. } //MinPageInUse