590 lines
13 KiB
C++
590 lines
13 KiB
C++
//+---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1992 - 1996.
|
|
//
|
|
// File: page.cxx
|
|
//
|
|
// Contents: Paging code for MSF
|
|
//
|
|
// Classes: Defined in page.hxx
|
|
//
|
|
// Functions:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include "msfhead.cxx"
|
|
|
|
|
|
|
|
#include "mread.hxx"
|
|
|
|
//+---------------------------------------------------------------------
|
|
//
|
|
// Member: CMSFPage::Byteswap, public
|
|
//
|
|
// Synopsis: Byteswap the elments of the page
|
|
//
|
|
// Algorithm: Call the corresponding byteswap routine depending on the
|
|
// actual type of the Mutli-stream.
|
|
//
|
|
//----------------------------------------------------------------------
|
|
|
|
void CMSFPage::ByteSwap(void)
|
|
{
|
|
CPagedVector* pVect = GetVector();
|
|
if (pVect->GetParent()->GetHeader()->DiffByteOrder())
|
|
{
|
|
switch (_sid)
|
|
{
|
|
case SIDDIR:
|
|
((CDirSect *)_ab)->
|
|
ByteSwap( ((CDirVector*)pVect)->GetSectorSize() );
|
|
break;
|
|
case SIDFAT:
|
|
case SIDMINIFAT:
|
|
case SIDDIF:
|
|
((CFatSect *)_ab)->
|
|
ByteSwap( ((CFatVector*)pVect)->GetSectBlock() );
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMSFPageTable::CMSFPageTable, public
|
|
//
|
|
// Synopsis: CMSFPageTable constructor.
|
|
//
|
|
// Arguments: [pmsParent] -- Pointer to multistream for this page table.
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
CMSFPageTable::CMSFPageTable( CMStream *const pmsParent,
|
|
const ULONG cMinPages,
|
|
const ULONG cMaxPages)
|
|
: _pmsParent(pmsParent), _cActivePages(0), _cPages(0),
|
|
_pmpCurrent(NULL),
|
|
_cbSector(pmsParent->GetSectorSize()),
|
|
_cMinPages(cMinPages), _cMaxPages(cMaxPages),
|
|
_cReferences(1)
|
|
{
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMSFPage::CMSFPage, public
|
|
//
|
|
// Synopsis: CMSFPage default constructor
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
CMSFPage::CMSFPage(CMSFPage *pmp)
|
|
{
|
|
if (pmp == NULL)
|
|
{
|
|
SetChain(this, this);
|
|
}
|
|
else
|
|
{
|
|
SetChain(pmp->GetPrev(), pmp);
|
|
GetPrev()->SetNext(this);
|
|
GetNext()->SetPrev(this);
|
|
}
|
|
|
|
SetSid(NOSTREAM);
|
|
SetOffset(0);
|
|
SetSect(ENDOFCHAIN);
|
|
SetFlags(0);
|
|
SetVector(NULL);
|
|
_cReferences = 0;
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMSFPageTable::~CMSFPageTable, public
|
|
//
|
|
// Synopsis: CMSFPageTable destructor
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
CMSFPageTable::~CMSFPageTable()
|
|
{
|
|
if (_pmpCurrent != NULL)
|
|
{
|
|
CMSFPage *pmp = _pmpCurrent;
|
|
CMSFPage *pmpNext;
|
|
|
|
while (pmp != pmp->GetNext())
|
|
{
|
|
pmpNext = pmp->GetNext();
|
|
#if DBG == 1
|
|
msfAssert(!pmp->IsInUse() &&
|
|
aMsg("Active page left at page table destruct time."));
|
|
|
|
#endif
|
|
delete pmp;
|
|
pmp = pmpNext;
|
|
}
|
|
delete pmp;
|
|
}
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMSFPageTable::Init, public
|
|
//
|
|
// Synopsis: Initialize a CMSFPageTable
|
|
//
|
|
// Arguments: [cPages] -- Number of pages to preallocate.
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
SCODE CMSFPageTable::Init(void)
|
|
{
|
|
SCODE sc = S_OK;
|
|
|
|
msfDebugOut((DEB_ITRACE, "In CMSFPageTable::Init:%p()\n", this));
|
|
|
|
for (ULONG i = 0; i < _cMinPages; i++)
|
|
{
|
|
CMSFPage *pmp;
|
|
|
|
msfMem(pmp = GetNewPage());
|
|
_pmpCurrent = pmp;
|
|
}
|
|
_cPages = _cMinPages;
|
|
_cActivePages = 0;
|
|
|
|
msfDebugOut((DEB_ITRACE, "Out CMSFPageTable::Init\n"));
|
|
|
|
Err:
|
|
|
|
return sc;
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMSFPageTable::FlushPage, public
|
|
//
|
|
// Synopsis: Flush a page
|
|
//
|
|
// Arguments: [pmp] -- Pointer to page to flush
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
SCODE CMSFPageTable::FlushPage(CMSFPage *pmp)
|
|
{
|
|
SCODE sc = S_OK;
|
|
|
|
pmp->AddRef();
|
|
|
|
CMStream *pms;
|
|
pms = pmp->GetVector()->GetParent();
|
|
|
|
//Flush the page, reset the dirty bit.
|
|
|
|
msfAssert((pmp->GetSect() != ENDOFCHAIN) &&
|
|
aMsg("Page location not set - don't know where to flush to."));
|
|
|
|
ULONG ulRet;
|
|
|
|
ILockBytes *pilb;
|
|
ULARGE_INTEGER ul;
|
|
ULISet32(ul, ConvertSectOffset(
|
|
pmp->GetSect(),
|
|
0,
|
|
pms->GetSectorShift()));
|
|
|
|
pilb = pms->GetILB();
|
|
|
|
pmp->ByteSwap(); // convert to disk format
|
|
// (if neccessary)
|
|
msfHChk(pilb->WriteAt(
|
|
ul,
|
|
(BYTE *)(pmp->GetData()),
|
|
_cbSector,
|
|
&ulRet));
|
|
|
|
pmp->ByteSwap(); // convert to back to machine format
|
|
// (if neccessary)
|
|
pmp->ResetDirty();
|
|
|
|
Err:
|
|
pmp->Release();
|
|
return sc;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMSFPageTable::GetFreePage, public
|
|
//
|
|
// Synopsis: Return a pointer to a free page.
|
|
//
|
|
// Arguments: [ppmp] -- Pointer to storage for return pointer
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
SCODE CMSFPageTable::GetFreePage(CMSFPage **ppmp)
|
|
{
|
|
SCODE sc = S_OK;
|
|
CMSFPage *pmp;
|
|
if (_cPages > _cActivePages)
|
|
{
|
|
//We have some unused page already allocated. Find and return it.
|
|
pmp = _pmpCurrent;
|
|
|
|
do
|
|
{
|
|
pmp = pmp->GetNext();
|
|
}
|
|
while ((pmp != _pmpCurrent) && (pmp->GetSid() != NOSTREAM));
|
|
|
|
msfAssert((pmp->GetSid() == NOSTREAM) &&
|
|
aMsg("Expected empty page, none found."));
|
|
|
|
*ppmp = pmp;
|
|
_cActivePages++;
|
|
}
|
|
else if (_cPages == _cMaxPages)
|
|
{
|
|
msfMem(pmp = FindSwapPage());
|
|
msfDebugOut((DEB_IERROR, "Got swap page %p\n",pmp));
|
|
|
|
msfAssert((pmp->GetVector() != NULL) &&
|
|
aMsg("FindSwapPage returned unowned page."));
|
|
|
|
msfDebugOut((DEB_IERROR, "Freeing page %lu from vector %p\n",
|
|
pmp->GetOffset(), pmp->GetVector()));
|
|
|
|
|
|
if (pmp->IsDirty())
|
|
{
|
|
msfChk(FlushPage(pmp));
|
|
msfAssert(!pmp->IsDirty() &&
|
|
aMsg("Page remained dirty after flush call"));
|
|
}
|
|
|
|
pmp->GetVector()->FreeTable(pmp->GetOffset());
|
|
#if DBG == 1
|
|
pmp->SetVector(NULL);
|
|
#endif
|
|
*ppmp = pmp;
|
|
}
|
|
else
|
|
{
|
|
//Create a new page and return it.
|
|
pmp = GetNewPage();
|
|
if (pmp != NULL)
|
|
{
|
|
*ppmp = pmp;
|
|
_cActivePages++;
|
|
_cPages++;
|
|
}
|
|
else
|
|
{
|
|
msfMem(pmp = FindSwapPage());
|
|
if (pmp->IsDirty())
|
|
{
|
|
msfChk(FlushPage(pmp));
|
|
msfAssert(!pmp->IsDirty() &&
|
|
aMsg("Page remained dirty after flush call"));
|
|
}
|
|
pmp->GetVector()->FreeTable(pmp->GetOffset());
|
|
#if DBG == 1
|
|
pmp->SetVector(NULL);
|
|
#endif
|
|
*ppmp = pmp;
|
|
}
|
|
}
|
|
|
|
Err:
|
|
return sc;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMSFPageTable::FindPage, public
|
|
//
|
|
// Synopsis: Find and return a given page
|
|
//
|
|
// Arguments: [ppv] -- Pointer to vector of page to return
|
|
// [sid] -- SID of page to return
|
|
// [ulOffset] -- Offset of page to return
|
|
// [ppmp] -- Location to return pointer
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
SCODE CMSFPageTable::FindPage(
|
|
CPagedVector *ppv,
|
|
SID sid,
|
|
ULONG ulOffset,
|
|
CMSFPage **ppmp)
|
|
{
|
|
SCODE sc;
|
|
CMSFPage *pmp = _pmpCurrent;
|
|
|
|
do
|
|
{
|
|
if ((pmp->GetVector() == ppv) && (pmp->GetOffset() == ulOffset))
|
|
{
|
|
//Bingo!
|
|
|
|
*ppmp = pmp;
|
|
return STG_S_FOUND;
|
|
}
|
|
|
|
pmp = pmp->GetNext();
|
|
}
|
|
while (pmp != _pmpCurrent);
|
|
|
|
//The page isn't currently in memory. Get a free page and
|
|
//bring it into memory.
|
|
|
|
msfChk(GetFreePage(&pmp));
|
|
|
|
msfAssert((pmp->GetVector() == NULL) &&
|
|
aMsg("Attempting to reassign owned page."));
|
|
pmp->SetVector(ppv);
|
|
pmp->SetSid(sid);
|
|
pmp->SetOffset(ulOffset);
|
|
pmp->SetSect(ENDOFCHAIN);
|
|
|
|
*ppmp = pmp;
|
|
|
|
Err:
|
|
return sc;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMSFPageTable::GetPage, public
|
|
//
|
|
// Synopsis: Find and return a given page
|
|
//
|
|
// Arguments: [sid] -- SID of page to return
|
|
// [ulOffset] -- Offset of page to return
|
|
// [ppmp] -- Location to return pointer
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
SCODE CMSFPageTable::GetPage(
|
|
CPagedVector *ppv,
|
|
SID sid,
|
|
ULONG ulOffset,
|
|
CMSFPage **ppmp)
|
|
{
|
|
SCODE sc;
|
|
|
|
*ppmp = NULL;
|
|
msfChk(FindPage(ppv, sid, ulOffset, ppmp));
|
|
|
|
(*ppmp)->AddRef();
|
|
|
|
if (sc != STG_S_FOUND)
|
|
{
|
|
ULONG ulRet;
|
|
SECT sect;
|
|
|
|
msfChk(ppv->GetParent()->GetSect(sid, ulOffset, §));
|
|
(*ppmp)->SetSect(sect);
|
|
|
|
CMStream *pms = (*ppmp)->GetVector()->GetParent();
|
|
|
|
ULARGE_INTEGER ul;
|
|
ULISet32(ul, ConvertSectOffset(
|
|
(*ppmp)->GetSect(),
|
|
0,
|
|
pms->GetSectorShift()));
|
|
|
|
msfAssert(pms->GetILB() != NULL &&
|
|
aMsg("NULL ILockBytes - missing SetAccess?"));
|
|
|
|
msfHChk(pms->GetILB()->ReadAt(ul, (BYTE *)((*ppmp)->GetData()),
|
|
_cbSector, &ulRet));
|
|
(*ppmp)->ByteSwap();
|
|
}
|
|
|
|
Err:
|
|
if (*ppmp != NULL)
|
|
{
|
|
(*ppmp)->Release();
|
|
}
|
|
|
|
return sc;
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMSFPageTable::ReleasePage, public
|
|
//
|
|
// Synopsis: Release a given page
|
|
//
|
|
// Arguments: [sid] -- SID of page to release
|
|
// [ulOffset] -- Offset of page to release
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CMSFPageTable::ReleasePage(CPagedVector *ppv, SID sid, ULONG ulOffset)
|
|
{
|
|
SCODE sc;
|
|
CMSFPage *pmp;
|
|
|
|
sc = FindPage(ppv, sid, ulOffset, &pmp);
|
|
|
|
if (SUCCEEDED(sc))
|
|
{
|
|
pmp->Release();
|
|
}
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMSFPageTable::Flush, public
|
|
//
|
|
// Synopsis: Flush dirty pages to disk
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
SCODE CMSFPageTable::Flush(void)
|
|
{
|
|
SCODE sc = S_OK;
|
|
|
|
CMSFPage *pmp = _pmpCurrent;
|
|
|
|
//We use pmpLast in case FlushPage changes _pmpCurrent.
|
|
CMSFPage *pmpLast = _pmpCurrent;
|
|
|
|
do
|
|
{
|
|
if ((pmp->IsDirty()) && !(pmp->IsInUse()))
|
|
{
|
|
msfChk(FlushPage(pmp));
|
|
}
|
|
|
|
pmp = pmp->GetNext();
|
|
|
|
}
|
|
while (pmp != pmpLast);
|
|
|
|
Err:
|
|
return sc;
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMSFPageTable::FreePages, public
|
|
//
|
|
// Synopsis: Free all the pages associated with a vector.
|
|
//
|
|
// Arguments: [ppv] -- Pointer to vector to free pages for.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CMSFPageTable::FreePages(CPagedVector *ppv)
|
|
{
|
|
CMSFPage *pmp = _pmpCurrent;
|
|
|
|
do
|
|
{
|
|
if (pmp->GetVector() == ppv)
|
|
{
|
|
pmp->SetSid(NOSTREAM);
|
|
pmp->SetVector(NULL);
|
|
pmp->ResetDirty();
|
|
_cActivePages--;
|
|
}
|
|
pmp = pmp->GetNext();
|
|
}
|
|
while (pmp != _pmpCurrent);
|
|
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMSFPageTable::FindSwapPage, private
|
|
//
|
|
// Synopsis: Find a page to swap out.
|
|
//
|
|
// Arguments: None.
|
|
//
|
|
// Returns: Pointer to page to swap out.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
CMSFPage * CMSFPageTable::FindSwapPage(void)
|
|
{
|
|
#if DBG == 1
|
|
ULONG cpInUse = 0;
|
|
#endif
|
|
|
|
while (TRUE)
|
|
{
|
|
if (!_pmpCurrent->IsInUse())
|
|
{
|
|
DWORD dwFlags;
|
|
|
|
dwFlags = _pmpCurrent->GetFlags();
|
|
_pmpCurrent->SetFlags(dwFlags & ~FB_TOUCHED);
|
|
_pmpCurrent = _pmpCurrent->GetNext();
|
|
|
|
if (!(dwFlags & FB_TOUCHED))
|
|
{
|
|
return _pmpCurrent->GetPrev();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_pmpCurrent = _pmpCurrent->GetNext();
|
|
}
|
|
#if DBG == 1
|
|
cpInUse++;
|
|
msfAssert((cpInUse < 3 * _cPages) &&
|
|
aMsg("No swappable pages."));
|
|
#endif
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|