2020-09-30 17:12:29 +02:00

318 lines
9.5 KiB
C++

//+-----------------------------------------------------------------------
//
// File: pagealloc.cxx
//
// Contents: Special fast allocator to allocate fixed-sized entities.
//
// Classes: CPageAllocator
//
// History: 02-Feb-96 Rickhi Created
//
// Notes: All synchronization is the responsibility of the caller.
//
// CODEWORK: faster list managment
// free empty pages
//
//-------------------------------------------------------------------------
#include <ole2int.h>
#include <pgalloc.hxx> // class def'n
#include <locks.hxx> // LOCK/UNLOCK
//+------------------------------------------------------------------------
//
// Member: CPageAllocator::Initialize, public
//
// Synopsis: Initializes the page allocator.
//
// Notes: Instances of this class must be static since this
// function does not init all members to 0.
//
// History: 02-Feb-95 Rickhi Created
//
//-------------------------------------------------------------------------
void CPageAllocator::Initialize(LONG cbPerEntry, LONG cEntriesPerPage)
{
ASSERT_LOCK_HELD
ComDebOut((DEB_PAGE,
"CPageAllocator::Initialize cbPerEntry:%x cEntriesPerPage:%x\n",
cbPerEntry, cEntriesPerPage));
Win4Assert(cbPerEntry >= sizeof(PageEntry));
Win4Assert(cEntriesPerPage > 0);
_cbPerEntry = cbPerEntry;
_cEntriesPerPage = cEntriesPerPage;
}
//+------------------------------------------------------------------------
//
// Member: CPageAllocator::Cleanup, public
//
// Synopsis: Cleanup the page allocator.
//
// History: 02-Feb-95 Rickhi Created
//
//-------------------------------------------------------------------------
void CPageAllocator::Cleanup()
{
ComDebOut((DEB_PAGE, "CPageAllocator::Cleanup\n"));
ASSERT_LOCK_HELD
if (_pPageListStart)
{
PageEntry **pPagePtr = _pPageListStart;
while (pPagePtr < _pPageListEnd)
{
// release each page of the table
PrivMemFree(*pPagePtr);
pPagePtr++;
}
// release the page list
PrivMemFree(_pPageListStart);
// reset the pointers so re-initialization is not needed
_cPages = 0;
_pPageListStart = NULL;
_pPageListEnd = NULL;
_pFirstFreeEntry = NULL;
}
ASSERT_LOCK_HELD
}
//+------------------------------------------------------------------------
//
// Member: CPageAllocator::AllocEntry, public
//
// Synopsis: Finds the first available entry in the table and returns
// a ptr to it. Returns NULL if no space is available and it
// cant grow the list.
//
// History: 02-Feb-95 Rickhi Created
//
//-------------------------------------------------------------------------
PageEntry *CPageAllocator::AllocEntry()
{
ComDebOut((DEB_PAGE, "CPageAllocator::AllocEntry\n"));
ASSERT_LOCK_HELD
if (_pFirstFreeEntry == NULL)
{
// no free entries, grow the list
Grow();
if (_pFirstFreeEntry == NULL)
{
// unable to allocate more
return NULL;
}
}
// get the ptr to return and update the _pFirstFree to the next
// available entry
PageEntry *pEntry = _pFirstFreeEntry;
_pFirstFreeEntry = pEntry->pNext;
ASSERT_LOCK_HELD
ComDebOut((DEB_PAGE, "CPageAllocator::AllocEntry pEntry:%x\n", pEntry));
return pEntry;
}
//+------------------------------------------------------------------------
//
// Member: CPageAllocator::ReleaseEntry, private
//
// Synopsis: returns an entry on the free list.
//
// History: 02-Feb-95 Rickhi Created
//
//-------------------------------------------------------------------------
void CPageAllocator::ReleaseEntry(PageEntry *pEntry)
{
ComDebOut((DEB_PAGE, "CPageAllocator::ReleaseEntry pEntry:%x\n", pEntry));
Win4Assert(pEntry);
ASSERT_LOCK_HELD
// chain it on the free list
pEntry->pNext = _pFirstFreeEntry;
_pFirstFreeEntry = pEntry;
}
//+------------------------------------------------------------------------
//
// Member: CPageAllocator::ReleaseEntryList, private
//
// Synopsis: returns a list of entries to the free list.
//
// History: 02-Feb-95 Rickhi Created
//
//-------------------------------------------------------------------------
void CPageAllocator::ReleaseEntryList(PageEntry *pFirst, PageEntry *pLast)
{
ComDebOut((DEB_PAGE,
"CPageAllocator::ReleaseEntryList pFirst:%x pLast:%x\n",
pFirst, pLast));
Win4Assert(pFirst);
Win4Assert(pLast);
ASSERT_LOCK_HELD
// update the free list
pLast->pNext = _pFirstFreeEntry;
_pFirstFreeEntry = pFirst;
}
//+------------------------------------------------------------------------
//
// Member: CPageAllocator::Grow, private
//
// Synopsis: Grows the table to allow for more Entries.
//
// History: 02-Feb-95 Rickhi Created
//
//-------------------------------------------------------------------------
void CPageAllocator::Grow()
{
Win4Assert(_pFirstFreeEntry == NULL);
ASSERT_LOCK_HELD
// allocate a new page
LONG cbPerPage = _cbPerEntry * _cEntriesPerPage;
PageEntry *pNewPage = (PageEntry *) PrivMemAlloc(cbPerPage);
if (pNewPage == NULL)
{
return;
}
#if DBG==1
// clear the page (only needed in debug)
memset(pNewPage, 0, cbPerPage);
#endif
// compute size of current page list
LONG cbCurListSize = _cPages * sizeof(PageEntry *);
// allocate a new page list to hold the new page ptr.
PageEntry **pNewList = (PageEntry **) PrivMemAlloc(cbCurListSize +
sizeof(PageEntry *));
if (pNewList)
{
// copy old page list into the new page list
memcpy(pNewList, _pPageListStart, cbCurListSize);
// set the new page ptr entry
*(pNewList + _cPages) = pNewPage;
_cPages ++;
// replace old page list with the new page list
PrivMemFree(_pPageListStart);
_pPageListStart = pNewList;
_pPageListEnd = pNewList + _cPages;
// update the first free entry ptr and link all the new entries
// together in a linked list.
_pFirstFreeEntry = pNewPage;
PageEntry *pNextFreeEntry = pNewPage;
PageEntry *pLastFreeEntry = (PageEntry *)(((BYTE *)pNewPage) + cbPerPage - _cbPerEntry);
while (pNextFreeEntry < pLastFreeEntry)
{
pNextFreeEntry->pNext = (PageEntry *)((BYTE *)pNextFreeEntry + _cbPerEntry);
pNextFreeEntry = pNextFreeEntry->pNext;
}
// last entry has an pNextFree of NULL (end of list)
pLastFreeEntry->pNext = NULL;
}
else
{
// release the allocated page.
PrivMemFree(pNewPage);
}
ComDebOut((DEB_PAGE, "CPageAllocator::Grow _pPageListStart:%x _pPageListEnd:%x _pFirstFreeEntry:%x\n",
_pPageListStart, _pPageListEnd, _pFirstFreeEntry));
}
//+------------------------------------------------------------------------
//
// Member: CPageAllocator::GetEntryIndex, public
//
// Synopsis: Converts a PageEntry ptr into an index.
//
// History: 02-Feb-95 Rickhi Created
//
//-------------------------------------------------------------------------
LONG CPageAllocator::GetEntryIndex(PageEntry *pEntry)
{
for (LONG index=0; index<_cPages; index++)
{
PageEntry *pPage = *(_pPageListStart + index); // get page ptr
if (pEntry >= pPage)
{
if (pEntry < (PageEntry *) ((BYTE *)pPage + (_cEntriesPerPage * _cbPerEntry)))
{
// found the page that the entry lives on, compute the index of
// the page and the index of the entry within the page.
return (index << PAGETBL_PAGESHIFT) +
((BYTE *)pEntry - (BYTE *)pPage) / _cbPerEntry;
}
}
}
// not found
return -1;
}
//+------------------------------------------------------------------------
//
// Member: CPageAllocator::IsValidIndex, private
//
// Synopsis: determines if the given DWORD provides a legal index
// into the PageTable.
//
// History: 02-Feb-95 Rickhi Created
//
//-------------------------------------------------------------------------
BOOL CPageAllocator::IsValidIndex(LONG index)
{
// make sure the index is not negative, otherwise the shift will do
// sign extension. check for valid page and valid offset within page
if ( (index >= 0) &&
((index >> PAGETBL_PAGESHIFT) < _cPages) &&
((index & PAGETBL_PAGEMASK) < _cEntriesPerPage) )
return TRUE;
// Don't print errors during shutdown.
if (_cPages != 0)
ComDebOut((DEB_ERROR, "IsValidIndex: Invalid PageTable Index:%x\n", index));
return FALSE;
}
//+------------------------------------------------------------------------
//
// Member: CPageAllocator::GetEntryPtr, public
//
// Synopsis: Converts an entry index into an entry pointer
//
// History: 02-Feb-95 Rickhi Created
//
//-------------------------------------------------------------------------
PageEntry *CPageAllocator::GetEntryPtr(LONG index)
{
Win4Assert(index >= 0);
Win4Assert(_cPages != 0);
Win4Assert(IsValidIndex(index));
PageEntry *pEntry = _pPageListStart[index >> PAGETBL_PAGESHIFT];
pEntry = (PageEntry *) ((BYTE *)pEntry +
((index & PAGETBL_PAGEMASK) * _cbPerEntry));
return pEntry;
}