windows-nt/Source/XPSP1/NT/com/rpc/runtime/mtrt/cellheap.cxx
2020-09-26 16:20:57 +08:00

1406 lines
44 KiB
C++

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
CellHeap.cxx
Abstract:
The functions for the cell heap. Implements a heap
of cells, each one of equal size with high locality
of reference.
Author:
Kamen Moutafov (kamenm) Dec 99 - Feb 2000
Revision History:
--*/
#include <precomp.hxx>
#include <CellHeap.hxx>
#include <MutexWrp.hxx>
// explicit placement new operator
inline
PVOID __cdecl
operator new(
size_t size,
PVOID pPlacement
)
{
return pPlacement;
}
const int NumberOfSectionNameRetries = 301;
CellHeap *g_pCellHeap = NULL;
BOOL g_fServerSideCellHeapInitialized = FALSE;
CellSection *pCachedCellSection = NULL;
MUTEX *CellHeap::EffectiveCellHeapMutex = NULL;
#define MAJOR_CELLHEAP_DEBUG
#if DBG
int CellHeap::NumberOfCellsPerFirstPageInSection = 0;
int CellHeap::NumberOfCellsPerPageInSection = 0;
#endif
CellSection *CellSection::AllocateCellSection(OUT RPC_STATUS *Status,
IN BOOL fFirstSection, IN SECURITY_DESCRIPTOR *pSecDescriptor,
IN CellHeap *pCellHeap)
{
HANDLE hFileMapping;
PVOID SectionPointer;
BOOL bRes;
int SectionSize = NumberOfPagesPerSection * gPageSize;
RPC_CHAR SectionName[RpcSectionNameMaxSize]; // 3*8 is the max hex representation
// of three DWORDS
DWORD RandomNumber[2];
RPC_CHAR *SectionNamePointer;
int i;
CellSection *pCellSection;
UNICODE_STRING SectionNameString;
OBJECT_ATTRIBUTES ObjectAttributes;
ACCESS_MASK DesiredAccess;
LARGE_INTEGER SectionSizeParam;
NTSTATUS NtStatus;
DWORD ProcessID = GetCurrentProcessId();
for (i = 0; i < NumberOfSectionNameRetries; i ++)
{
// we'll try creating a named object until the last try, when
// we're content with creating any object
if (i == (NumberOfSectionNameRetries - 1))
{
SectionNamePointer = NULL;
}
else
{
// the first section is named with the prefix and PID only,
// which makes the other stuff unnecessary
if (!fFirstSection)
{
// generate the random numbers
*Status = GenerateRandomNumber((unsigned char *)RandomNumber, 8);
if (*Status != RPC_S_OK)
return NULL;
GenerateSectionName(SectionName, sizeof(SectionName), ProcessID, RandomNumber);
}
else
{
GenerateSectionName(SectionName, sizeof(SectionName), ProcessID, NULL);
// ensure there are no retries for the first section
i = NumberOfSectionNameRetries - 2;
}
SectionNamePointer = SectionName;
}
DesiredAccess = STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ | SECTION_MAP_WRITE;
RtlInitUnicodeString(&SectionNameString, SectionNamePointer);
InitializeObjectAttributes(&ObjectAttributes,
&SectionNameString,
OBJ_CASE_INSENSITIVE,
0,
pSecDescriptor);
SectionSizeParam.LowPart = SectionSize;
SectionSizeParam.HighPart = 0;
NtStatus = NtCreateSection(&hFileMapping, DesiredAccess, &ObjectAttributes, &SectionSizeParam,
PAGE_READWRITE, SEC_RESERVE, NULL);
if (!NT_SUCCESS(NtStatus))
{
if (NtStatus == STATUS_NO_MEMORY)
*Status = RPC_S_OUT_OF_MEMORY;
else if ((NtStatus == STATUS_INSUFFICIENT_RESOURCES) || (NtStatus == STATUS_QUOTA_EXCEEDED))
*Status = RPC_S_OUT_OF_RESOURCES;
else if ((NtStatus == STATUS_OBJECT_PATH_INVALID)
|| (NtStatus == STATUS_OBJECT_PATH_NOT_FOUND)
|| (NtStatus == STATUS_OBJECT_NAME_INVALID)
|| (NtStatus == STATUS_OBJECT_NAME_COLLISION))
{
*Status = RPC_S_INTERNAL_ERROR;
}
else if (NtStatus == STATUS_OBJECT_TYPE_MISMATCH)
{
// somebody is attacking us, or there is a collision - try again
continue;
}
else
{
ASSERT(0);
*Status = RPC_S_OUT_OF_MEMORY;
}
return NULL;
}
else if (NtStatus == STATUS_OBJECT_NAME_EXISTS)
{
CloseHandle(hFileMapping);
hFileMapping = NULL;
}
else
{
ASSERT(hFileMapping != NULL);
break;
}
// name conflict - keep trying
}
SectionPointer = MapViewOfFileEx(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, SectionSize, NULL);
if (SectionPointer == NULL)
{
*Status = GetLastError();
CloseHandle(hFileMapping);
return NULL;
}
if (VirtualAlloc(SectionPointer, 1, MEM_COMMIT, PAGE_READWRITE) == NULL)
{
*Status = GetLastError();
CloseDbgSection(hFileMapping, SectionPointer);
return NULL;
}
*Status = RPC_S_OK;
// explicit placement - can't fail with NULL return value
pCellSection = new (SectionPointer) CellSection(Status, hFileMapping, pCellHeap, RandomNumber);
if (*Status != RPC_S_OK)
{
return NULL;
}
return pCellSection;
}
#if DBG
void CellSection::AssertValid(CellHeap *pCellHeap)
{
int i;
DWORD Ignored;
BOOL fAccessTestSucceeded;
int LocalCountOfUsedCells;
int NumberOfCellsInSection;
DebugFreeCell *pCurrentCell;
pCellHeap->CellHeapMutex.VerifyOwned();
ASSERT(Signature == 0xdada);
ASSERT(LastCommittedPage >= 1);
ASSERT(LastCommittedPage <= NumberOfPagesPerSection);
// check that the pages claimed committed are indeed committed
for (i = 0; i < LastCommittedPage; i ++)
{
fAccessTestSucceeded = TRUE;
__try
{
Ignored = *(DWORD *)(((unsigned char *)this) + gPageSize * i);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
fAccessTestSucceeded = FALSE;
}
ASSERT(fAccessTestSucceeded == TRUE);
}
if (SectionID == -1)
{
ASSERT(pCachedCellSection == this);
}
else
{
ASSERT(pCellHeap->CellHeapSections.Find(SectionID) == this);
ASSERT(pCachedCellSection != this);
}
#ifdef MAJOR_CELLHEAP_DEBUG
NumberOfCellsInSection = pCellHeap->GetSectionCapacity(this);
pCurrentCell = (DebugFreeCell *)(this + 1);
LocalCountOfUsedCells = 0;
// count that the number of used cells is indeed what the header says it is
for (i = 0; i < NumberOfCellsInSection; i ++)
{
if (pCurrentCell->Type != dctFree)
LocalCountOfUsedCells ++;
ASSERT(pCurrentCell->Type >= dctFirstEntry);
ASSERT(pCurrentCell->Type <= dctLastEntry);
pCurrentCell ++;
}
ASSERT(LocalCountOfUsedCells == NumberOfUsedCells);
#endif
// if this is the cached section, make sure all cells are free
if (SectionID == -1)
{
ASSERT(NumberOfUsedCells == 0);
}
// the NextSectionId is checked by the CellHeap validate function
ASSERT(hFileMapping != NULL);
// the sections list chain and the free cell chain are
// verified by the CellHeap::AssertValid
}
#endif
CellSection::CellSection(IN OUT RPC_STATUS *Status, IN OUT HANDLE hNewFileMapping,
IN CellHeap *pCellHeap, IN DWORD *pRandomNumbers)
{
#if defined(_WIN64)
ASSERT(sizeof(CellSection) == 64);
#else
ASSERT(sizeof(CellSection) == 32);
#endif
// initialize variables to well known values
Signature = 0xdada;
LastCommittedPage = 1;
SectionID = -1;
NumberOfUsedCells = 0;
NextSectionId[0] = 0;
NextSectionId[1] = 0;
hFileMapping = hNewFileMapping;
pFirstFreeCell = (DebugFreeCell *)(this + 1);
InitializeListHead(&SectionListEntry);
#if defined(_WIN64)
Reserved[0] = 0;
Reserved[1] = 0;
Reserved[2] = 0;
Reserved[3] = 0;
#endif
if (*Status != RPC_S_OK)
return;
InitializeNewPage(this);
*Status = pCellHeap->SectionCreatedNotify(this, pRandomNumbers, NULL, NULL);
if (*Status != RPC_S_OK)
{
// unmap this - don't touch any data member afterwards!
CloseDbgSection(hNewFileMapping, this);
}
else
{
#if DBG
pCellHeap->CellHeapMutex.Request();
ASSERT_VALID1(this, pCellHeap);
pCellHeap->CellHeapMutex.Clear();
#endif
}
}
void CellSection::Free(void)
{
HANDLE hLocalFileMapping = hFileMapping;
ASSERT(hLocalFileMapping);
// unmaps this - don't touch data members after this!
CloseDbgSection(hLocalFileMapping, this);
}
RPC_STATUS CellSection::ExtendSection(IN CellHeap *pCellHeap)
{
PVOID NewCommitPointer;
ASSERT(LastCommittedPage < NumberOfPagesPerSection);
pCellHeap->CellHeapMutex.VerifyOwned();
NewCommitPointer = (unsigned char *)this + gPageSize * LastCommittedPage;
if (VirtualAlloc(NewCommitPointer, 1, MEM_COMMIT, PAGE_READWRITE) == NULL)
return RPC_S_OUT_OF_MEMORY;
LastCommittedPage ++;
InitializeNewPage(NewCommitPointer);
// the pFirstFreeCell should be NULL - otherwise we shouldn't be
// extending the section
ASSERT(pFirstFreeCell == NULL);
pCellHeap->InsertNewPageSegmentInChain((DebugFreeCell *)NewCommitPointer,
(DebugFreeCell *)((unsigned char *)NewCommitPointer + gPageSize - sizeof(DebugFreeCell)));
pFirstFreeCell = (DebugFreeCell *)NewCommitPointer;
return RPC_S_OK;
}
void CellSection::InitializeNewPage(PVOID NewPage)
{
DebugFreeCell *pCurrentFreeCell, *pPrevFreeCell;
PVOID EndAddress;
// initialize the cells within the heap and chain them for quick insertion
// if this is the first page in the section, skip the section header
if (NewPage == this)
pCurrentFreeCell = (DebugFreeCell *)(this + 1);
else
pCurrentFreeCell = (DebugFreeCell *)NewPage;
pCurrentFreeCell->FreeCellsChain.Blink = NULL;
EndAddress = (unsigned char *)NewPage + gPageSize;
pPrevFreeCell = NULL;
while (pCurrentFreeCell < (DebugFreeCell *)EndAddress)
{
pCurrentFreeCell->TypeHeader = 0;
pCurrentFreeCell->Type = dctFree;
pCurrentFreeCell->pOwnerSection = this;
if (pPrevFreeCell)
{
pCurrentFreeCell->FreeCellsChain.Blink = &(pPrevFreeCell->FreeCellsChain);
pPrevFreeCell->FreeCellsChain.Flink = &(pCurrentFreeCell->FreeCellsChain);
}
pPrevFreeCell = pCurrentFreeCell;
pCurrentFreeCell ++;
}
pPrevFreeCell->FreeCellsChain.Flink = NULL;
}
CellHeap::CellHeap(IN OUT RPC_STATUS *Status)
{
// initialize data members to well known values
InitializeListHead(&FreeCellsList);
InitializeListHead(&SectionsList);
SecurityDescriptor = NULL;
#if DBG
NumberOfCellsPerFirstPageInSection = (gPageSize - sizeof(CellSection)) / sizeof(DebugFreeCell);
NumberOfCellsPerPageInSection = (gPageSize / sizeof(DebugFreeCell));
#endif
if (*Status != RPC_S_OK)
return;
EffectiveCellHeapMutex = new MUTEX(Status, TRUE, 8000);
if (EffectiveCellHeapMutex == NULL)
{
*Status = RPC_S_OUT_OF_MEMORY;
return;
}
if (*Status != RPC_S_OK)
{
delete EffectiveCellHeapMutex;
return;
}
*Status = CreateSecurityDescriptor();
if (*Status != RPC_S_OK)
return;
}
CellHeap::~CellHeap(void)
{
PACL pdacl;
BOOL DaclPresent;
BOOL DaclDefaulted;
BOOL bRes;
CellSection *pCurrentSection, *pNextSection;
if (SecurityDescriptor != NULL)
{
bRes = GetSecurityDescriptorDacl(SecurityDescriptor, &DaclPresent,
&pdacl, &DaclDefaulted);
ASSERT(bRes);
ASSERT(DaclPresent);
ASSERT(DaclDefaulted == FALSE);
ASSERT(pdacl);
delete pdacl;
delete SecurityDescriptor;
}
// nuke all sections in the list
pCurrentSection = (CellSection *) SectionsList.Flink;
while (pCurrentSection != (CellSection *)&SectionsList)
{
CellHeapSections.Delete(pCurrentSection->SectionID);
pNextSection = (CellSection *) pCurrentSection->SectionListEntry.Flink;
pCurrentSection->Free();
pCurrentSection = pNextSection;
}
}
DebugFreeCell *CellHeap::AllocateCell(OUT CellTag *pCellTag)
{
DebugFreeCell *pCurrentCell, *pNextCell, *pLastCell;
LIST_ENTRY *pCurrentEntry;
CellSection *pCurrentSection;
RPC_STATUS Status;
int RetryCount;
// get the mutex
CellHeapMutex.Request();
ASSERT_VALID(this);
// is there something on the list?
if (IsListEmpty(&FreeCellsList))
{
// no, need to extend the cell heap
// first, try to extend some section, if there is space for it
// the list must not be empty
ASSERT(!IsListEmpty(&SectionsList));
// this operation is fast, so we can do it inside the mutex and
// gain simpler code
pCurrentEntry = SectionsList.Flink;
while (pCurrentEntry != &SectionsList)
{
pCurrentSection = CONTAINING_RECORD(pCurrentEntry, CellSection, SectionListEntry);
if (pCurrentSection->LastCommittedPage < NumberOfPagesPerSection)
{
// try to extend the section
Status = pCurrentSection->ExtendSection(this);
if (Status == RPC_S_OK)
goto PopFreeDebugCell;
ASSERT_VALID(this);
// we're truly out of memory
CellHeapMutex.Clear();
return NULL;
}
pCurrentEntry = pCurrentEntry->Flink;
}
// if we are here, all sections are full - try the cached section
if (pCachedCellSection)
{
pLastCell = CONTAINING_RECORD(pCachedCellSection->pFirstFreeCell->FreeCellsChain.Blink,
DebugFreeCell, FreeCellsChain);
pCachedCellSection->pFirstFreeCell->FreeCellsChain.Blink = NULL;
Status = SectionCreatedNotify(pCachedCellSection, pCachedCellSection->NextSectionId,
pCachedCellSection->pFirstFreeCell, pLastCell);
if (Status != RPC_S_OK)
{
ASSERT_VALID(this);
CellHeapMutex.Clear();
return NULL;
}
// terminate the name chain for the just inserted cached section
pCachedCellSection->NextSectionId[0] = 0;
pCachedCellSection->NextSectionId[1] = 0;
pCachedCellSection = NULL;
goto PopFreeDebugCell;
}
ASSERT_VALID(this);
// This is going to be slow -
// release the mutex and we will claim it later, when we're done
CellHeapMutex.Clear();
RetryCount = 0;
while (TRUE)
{
// try to allocate a new section
Status = AllocateCellSection(FALSE);
if (Status == RPC_S_OK)
{
CellHeapMutex.Request();
ASSERT_VALID(this);
if (!IsListEmpty(&FreeCellsList))
goto PopFreeDebugCell;
// it is possible, though very unlikely that all allocated
// cells have been used by the other threads. Retry a limited
// number of times
CellHeapMutex.Clear();
RetryCount ++;
ASSERT(RetryCount < 3);
}
else
return NULL;
}
}
else
{
PopFreeDebugCell:
CellHeapMutex.VerifyOwned();
// pop off the list
pCurrentEntry = RemoveHeadList(&FreeCellsList);
pCurrentCell = (DebugFreeCell *) CONTAINING_RECORD(pCurrentEntry, DebugFreeCell, FreeCellsChain);
// if there are more entries in the list ...
if (!IsListEmpty(&FreeCellsList))
{
pNextCell = (DebugFreeCell *) CONTAINING_RECORD(FreeCellsList.Flink, DebugFreeCell, FreeCellsChain);
// ... and the next cell is from this section ...
if (pCurrentCell->pOwnerSection == pNextCell->pOwnerSection)
{
// ... mark the next free cell as the first free cell for that section
pCurrentCell->pOwnerSection->pFirstFreeCell = pNextCell;
}
else
{
// ... the current section has no more free cells
pCurrentCell->pOwnerSection->pFirstFreeCell = NULL;
ASSERT(pCurrentCell->pOwnerSection->NumberOfUsedCells + 1
== GetSectionCapacity(pCurrentCell->pOwnerSection));
}
}
else
{
// ... the current section has no more free cells
pCurrentCell->pOwnerSection->pFirstFreeCell = NULL;
ASSERT(pCurrentCell->pOwnerSection->NumberOfUsedCells + 1
== GetSectionCapacity(pCurrentCell->pOwnerSection));
}
pCurrentCell->pOwnerSection->NumberOfUsedCells ++;
}
pCurrentCell->Type = dctUsedGeneric;
ASSERT_VALID(this);
CellHeapMutex.Clear();
*pCellTag = pCurrentCell->pOwnerSection->SectionID;
return pCurrentCell;
}
void CellHeap::FreeCell(IN void *cell, IN OUT CellTag *pCellTag)
{
CellSection *pSection;
DebugFreeCell *pFreeCell;
LIST_ENTRY *pCurrentEntry;
CellSection *pCurrentSection;
CellSection *pCachedSection;
CellSection *pPrevSection, *pNextSection;
BOOL fFreeCurrentSection;
DebugFreeCell *pFirstCell, *pLastCell;
DWORD SectionNumbers[2];
// guard against double frees
ASSERT(*pCellTag != -1);
CellHeapMutex.Request();
ASSERT_VALID(this);
pSection = CellHeapSections.Find(*pCellTag);
// make sure the cell is indeed from that section
ASSERT((unsigned char *)cell >= (unsigned char *)pSection);
ASSERT((unsigned char *)cell < ((unsigned char *)pSection) + gPageSize * pSection->LastCommittedPage);
ASSERT(pSection->NumberOfUsedCells > 0);
pFreeCell = (DebugFreeCell *) cell;
// push on the list for the section the cell is from
if (pSection->pFirstFreeCell)
{
InsertHeadList(pSection->pFirstFreeCell->FreeCellsChain.Blink, &pFreeCell->FreeCellsChain);
// the pSection->pFirstFreeCell will be updated below
}
else
{
// find the place in the free list this goes to
// the way we do this is walk the rest of the sections list
// and try to insert it before the first section we find
// if we don't find anything, we insert it in the list tail
pCurrentEntry = pSection->SectionListEntry.Flink;
while (pCurrentEntry != &SectionsList)
{
pCurrentSection = CONTAINING_RECORD(pCurrentEntry, CellSection, SectionListEntry);
if (pCurrentSection->pFirstFreeCell)
{
// we have found our place - use it
InsertHeadList(pCurrentSection->pFirstFreeCell->FreeCellsChain.Blink, &pFreeCell->FreeCellsChain);
// the pSection->pFirstFreeCell will be updated below
break;
}
pCurrentEntry = pCurrentEntry->Flink;
}
// did we pass through everything?
if (pCurrentEntry == &SectionsList)
{
// if yes, just insert in the tail
InsertTailList(&FreeCellsList, &pFreeCell->FreeCellsChain);
// the pSection->pFirstFreeCell will be updated below
}
}
pSection->pFirstFreeCell = pFreeCell;
pSection->NumberOfUsedCells --;
pFreeCell->Type = dctFree;
pFreeCell->pOwnerSection = pSection;
if ((pSection->NumberOfUsedCells == 0) && (pSection != pFirstSection))
{
// unlink this section's segment from the cell free list
pFirstCell = pFreeCell;
// find the next section that has something on
// the free list
pCurrentEntry = pSection->SectionListEntry.Flink;
while (pCurrentEntry != &SectionsList)
{
pCurrentSection = CONTAINING_RECORD(pCurrentEntry, CellSection, SectionListEntry);
if (pCurrentSection->pFirstFreeCell)
{
pLastCell = CONTAINING_RECORD(pCurrentSection->pFirstFreeCell->FreeCellsChain.Blink, DebugFreeCell, FreeCellsChain);
ASSERT(pLastCell->pOwnerSection == pSection);
break;
}
pCurrentEntry = pCurrentEntry->Flink;
}
// if we didn't find anything, we're the last segment on the free list
if (pCurrentEntry == &SectionsList)
{
pLastCell = CONTAINING_RECORD(FreeCellsList.Blink, DebugFreeCell, FreeCellsChain);
pFirstCell->FreeCellsChain.Blink->Flink = &FreeCellsList;
FreeCellsList.Blink = pFirstCell->FreeCellsChain.Blink;
}
else
{
pFirstCell->FreeCellsChain.Blink->Flink = pLastCell->FreeCellsChain.Flink;
pLastCell->FreeCellsChain.Flink->Blink = pFirstCell->FreeCellsChain.Blink;
}
// chain the cells within the segment
pFirstCell->FreeCellsChain.Blink = &pLastCell->FreeCellsChain;
pLastCell->FreeCellsChain.Flink = NULL;
// remove the section from the dictionary
CellHeapSections.Delete(pSection->SectionID);
pSection->SectionID = -1;
// restore the name chain
ASSERT(pSection->SectionListEntry.Blink != &SectionsList);
pPrevSection
= CONTAINING_RECORD(pSection->SectionListEntry.Blink, CellSection, SectionListEntry);
SectionNumbers[0] = pPrevSection->NextSectionId[0];
SectionNumbers[1] = pPrevSection->NextSectionId[1];
pPrevSection->NextSectionId[0] = pSection->NextSectionId[0];
pPrevSection->NextSectionId[1] = pSection->NextSectionId[1];
pSection->NextSectionId[0] = SectionNumbers[0];
pSection->NextSectionId[1] = SectionNumbers[1];
// unlink the chain from the sections list
RemoveEntryList(&pSection->SectionListEntry);
fFreeCurrentSection = TRUE;
}
else
{
fFreeCurrentSection = FALSE;
}
if (pSection->NumberOfUsedCells <= 100)
{
// the low water mark has been reached - dispose of the cached section
pCachedSection = pCachedCellSection;
// if we are freeing the current section, put it as the cached section
// instead
if (fFreeCurrentSection)
{
pCachedCellSection = pSection;
}
if (pCachedSection != NULL)
{
if (!fFreeCurrentSection)
{
pCachedCellSection = NULL;
}
// the first section should not go away
ASSERT(pCachedSection != pFirstSection);
ASSERT_VALID(this);
// do the unmapping outside the mutex since it's slow
CellHeapMutex.Clear();
pCachedSection->Free();
goto FreeCellCleanup;
}
}
ASSERT_VALID(this);
CellHeapMutex.Clear();
FreeCellCleanup:
*pCellTag = -1;
}
void CellHeap::RelocateCellIfPossible(IN OUT void **ppCell, IN OUT CellTag *pCellTag)
{
DebugFreeCell *pNewCell;
CellTag NewCellTag;
// if we are not on the first section and there are free cells
// on the first section ...
if ((*pCellTag != 0) && pFirstSection->pFirstFreeCell)
{
CellHeapMutex.Request();
if (pFirstSection->pFirstFreeCell == NULL)
{
// somebody beat us to it
CellHeapMutex.Clear();
return;
}
pNewCell = AllocateCell(&NewCellTag);
// this should succeed - we are doing it in a mutex, and we checked
// that there are free elements
ASSERT(pNewCell);
// we can release the mutex now
CellHeapMutex.Clear();
memcpy(pNewCell, *ppCell, sizeof(DebugFreeCell));
FreeCell(*ppCell, pCellTag);
*pCellTag = NewCellTag;
*ppCell = pNewCell;
}
}
RPC_STATUS CellHeap::SectionCreatedNotify(IN CellSection *pCellSection, IN DWORD *pRandomNumbers,
IN DebugFreeCell *pFirstCell OPTIONAL, IN DebugFreeCell *pLastCell OPTIONAL)
{
int Key;
CellSection *pLastSection;
PVOID pLastSectionListEntry;
LIST_ENTRY *EX_Blink;
CellHeapMutex.Request();
Key = CellHeapSections.Insert(pCellSection);
if (Key == -1)
{
CellHeapMutex.Clear();
return RPC_S_OUT_OF_MEMORY;
}
pCellSection->SectionID = (short) Key;
pLastSectionListEntry = SectionsList.Blink;
// if there is last section, chain the names
if (pLastSectionListEntry != &SectionsList)
{
pLastSection = (CellSection *)(CONTAINING_RECORD(pLastSectionListEntry, CellSection, SectionListEntry));
ASSERT(pLastSection->NextSectionId[0] == 0);
ASSERT(pLastSection->NextSectionId[1] == 0);
pLastSection->NextSectionId[0] = pRandomNumbers[0];
pLastSection->NextSectionId[1] = pRandomNumbers[1];
}
InsertTailList(&SectionsList, &(pCellSection->SectionListEntry));
if (pFirstCell == NULL)
{
ASSERT(pLastCell == NULL);
pFirstCell = (DebugFreeCell *)(pCellSection + 1);
pLastCell = (DebugFreeCell *)((unsigned char *)pCellSection + gPageSize - sizeof(DebugFreeCell));
}
// chain the cells in the section to the free list
InsertNewPageSegmentInChain(pFirstCell, pLastCell);
CellHeapMutex.Clear();
return RPC_S_OK;
}
RPC_STATUS CellHeap::InitializeServerSideCellHeap(void)
{
RPC_STATUS Status = RPC_S_OK;
CellHeapMutex.Request();
if (g_fServerSideCellHeapInitialized)
{
CellHeapMutex.Clear();
return RPC_S_OK;
}
// there is no race free way to create a first section - do
// it in the mutex
Status = AllocateCellSection(
TRUE // First Section
);
if (Status == RPC_S_OK)
{
g_fServerSideCellHeapInitialized = TRUE;
}
CellHeapMutex.Clear();
return Status;
}
#if DBG
void CellHeap::AssertValid(void)
{
CellSection *pSection, *pPrevSection, *pNextSection;
CellSection *pPrevSection2;
DebugFreeCell *pCurrentCell = NULL;
LIST_ENTRY *pCurrentEntry, *pPrevEntry;
LIST_ENTRY *pPrevFreeEntry;
RPC_STATUS Status;
HANDLE hSection;
PVOID pMappedSection;
int SectionsInDictionary;
int LocalSectionFreeCellsCount;
CellHeapMutex.VerifyOwned();
ASSERT(IsValidSecurityDescriptor(SecurityDescriptor));
// there must be at least one section
ASSERT(!IsListEmpty(&SectionsList));
pCurrentEntry = SectionsList.Flink;
pSection = CONTAINING_RECORD(pCurrentEntry, CellSection, SectionListEntry);
ASSERT(pSection == pFirstSection);
SectionsInDictionary = CellHeapSections.Size();
pPrevSection = NULL;
pPrevEntry = &SectionsList;
pPrevFreeEntry = &FreeCellsList;
while (pCurrentEntry != &SectionsList)
{
pSection = CONTAINING_RECORD(pCurrentEntry, CellSection, SectionListEntry);
ASSERT(pCurrentEntry->Blink == pPrevEntry);
if (pPrevSection)
{
// make sure opening the next section from the previous section
// yields this section
Status = OpenSection(&hSection, &pMappedSection, pPrevSection->NextSectionId);
// it is possible for this operation to fail
// handle just the success case
if (Status == RPC_S_OK)
{
pNextSection = (CellSection *)pMappedSection;
ASSERT(pNextSection->SectionID == pSection->SectionID);
CloseDbgSection(hSection, pNextSection);
}
}
pSection->AssertValid(this);
SectionsInDictionary --;
// walk the free list pointers and make sure the free list is correct
// we do this only if this section has something in the list
if (pSection->pFirstFreeCell)
{
// there is previous section to verify only if we're not at the beginning
if (pPrevFreeEntry != &FreeCellsList)
{
pCurrentCell = CONTAINING_RECORD(pPrevFreeEntry, DebugFreeCell, FreeCellsChain);
pPrevSection2 = pCurrentCell->pOwnerSection;
LocalSectionFreeCellsCount = 1;
}
else
{
pPrevSection2 = NULL;
}
// there must be at least one element difference
// between the previous and this - that is, two
// sections cannot point to the same cell as their
// first free cell
pPrevFreeEntry = pPrevFreeEntry->Flink;
while (pPrevFreeEntry != &pSection->pFirstFreeCell->FreeCellsChain)
{
// make sure we don't wrap around
ASSERT (pPrevFreeEntry != &FreeCellsList);
pCurrentCell = CONTAINING_RECORD(pPrevFreeEntry, DebugFreeCell, FreeCellsChain);
if (pPrevSection2)
{
// make sure all cells from the segment belong to the same section
ASSERT(pCurrentCell->pOwnerSection == pPrevSection2);
LocalSectionFreeCellsCount ++;
}
ASSERT(pPrevFreeEntry->Flink->Blink == pPrevFreeEntry);
pPrevFreeEntry = pPrevFreeEntry->Flink;
}
if (pPrevSection2)
{
ASSERT(LocalSectionFreeCellsCount
== GetSectionCapacity(pPrevSection2) - pPrevSection2->NumberOfUsedCells)
}
}
pPrevSection = pSection;
pPrevEntry = pCurrentEntry;
pCurrentEntry = pCurrentEntry->Flink;
}
// we have iterated through all the sections
// check the free list for the last section
// but don't do it if none of the sections had free cells
if (pPrevFreeEntry != &FreeCellsList)
{
pCurrentCell = CONTAINING_RECORD(pPrevFreeEntry, DebugFreeCell, FreeCellsChain);
pPrevSection2 = pCurrentCell->pOwnerSection;
LocalSectionFreeCellsCount = 1;
// there must be at least one element difference
// between the previous and this - that is, two
// sections cannot point to the same cell as their
// first free cell
pPrevFreeEntry = pPrevFreeEntry->Flink;
while (pPrevFreeEntry != &FreeCellsList)
{
pCurrentCell = CONTAINING_RECORD(pPrevFreeEntry, DebugFreeCell, FreeCellsChain);
// make sure all cells from the segment belong to the same section
ASSERT(pCurrentCell->pOwnerSection == pPrevSection2);
LocalSectionFreeCellsCount ++;
ASSERT(pPrevFreeEntry->Flink->Blink == pPrevFreeEntry);
pPrevFreeEntry = pPrevFreeEntry->Flink;
}
ASSERT(LocalSectionFreeCellsCount
== GetSectionCapacity(pPrevSection2) - pPrevSection2->NumberOfUsedCells)
}
// do some final checks
// we have wrapped around to the beginning of the list
ASSERT(pPrevFreeEntry == &FreeCellsList);
// all of the sections in the list must have been in the dictionary also
ASSERT(SectionsInDictionary == 0);
// the names list must be properly terminated
ASSERT(pSection->NextSectionId[0] == 0);
ASSERT(pSection->NextSectionId[1] == 0);
// verify the cached section (if any)
if (pCachedCellSection)
{
pCachedCellSection->AssertValid(this);
Status = OpenSection(&hSection, &pMappedSection, pCachedCellSection->NextSectionId);
if (Status == RPC_S_OK)
{
pNextSection = (CellSection *)pMappedSection;
ASSERT(pCachedCellSection->NextSectionId[0] == pNextSection->NextSectionId[0]);
ASSERT(pCachedCellSection->NextSectionId[1] == pNextSection->NextSectionId[1]);
CloseDbgSection(hSection, pMappedSection);
}
// walk the free list for the cached section and make sure
// it is linked properly
ASSERT(pCachedCellSection->pFirstFreeCell);
pCurrentEntry = &pCachedCellSection->pFirstFreeCell->FreeCellsChain;
while(pCurrentEntry->Flink != NULL)
{
pCurrentEntry = pCurrentEntry->Flink;
}
// pCurrentEntry should be the last cell here
ASSERT(pCachedCellSection->pFirstFreeCell->FreeCellsChain.Blink == pCurrentEntry);
}
}
#endif
// trick the compiler into statically initializing SID with two SubAuthorities
typedef struct _RPC_SID2 {
UCHAR Revision;
UCHAR SubAuthorityCount;
SID_IDENTIFIER_AUTHORITY IdentifierAuthority;
ULONG SubAuthority[2];
} RPC_SID2;
RPC_STATUS CellHeap::CreateSecurityDescriptor(void)
{
const SID LocalSystem = { 1, 1, SECURITY_NT_AUTHORITY, SECURITY_LOCAL_SYSTEM_RID};
const RPC_SID2 Admin1 = { 1, 2, SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS};
// const RPC_SID2 Admin2 = { 1, 2, SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_GROUP_RID_ADMINS};
// const RPC_SID2 Admin3 = { 1, 2, SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_USER_RID_ADMIN};
DWORD size = 4 * sizeof(ACCESS_ALLOWED_ACE) + sizeof(LocalSystem) + sizeof(Admin1) ;
// + sizeof(Admin2) + sizeof(Admin3);
BOOL bRes;
SecurityDescriptor = new SECURITY_DESCRIPTOR;
if (SecurityDescriptor == NULL)
{
return RPC_S_OUT_OF_MEMORY;
}
PACL pdacl = (PACL) new unsigned char[size + sizeof(ACL)];
ULONG ldacl = size + sizeof(ACL);
if (pdacl == NULL)
{
delete SecurityDescriptor;
SecurityDescriptor = NULL;
return RPC_S_OUT_OF_MEMORY;
}
ASSERT(RtlValidSid((PSID)&LocalSystem));
ASSERT(RtlValidSid((PSID)&Admin1));
// ASSERT(RtlValidSid((PSID)&Admin2));
// ASSERT(RtlValidSid((PSID)&Admin3));
InitializeSecurityDescriptor(SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
InitializeAcl(pdacl, ldacl, ACL_REVISION);
// this should not fail unless we messed up with the parameters
// somewhere
bRes = AddAccessAllowedAce(pdacl, ACL_REVISION,
FILE_MAP_READ,
(PVOID)&LocalSystem);
ASSERT(bRes);
bRes = AddAccessAllowedAce(pdacl, ACL_REVISION,
FILE_MAP_READ,
(PVOID)&Admin1);
ASSERT(bRes);
/*
bRes = AddAccessAllowedAce(pdacl, ACL_REVISION,
FILE_MAP_READ,
(PVOID)&Admin2);
ASSERT(bRes);
bRes = AddAccessAllowedAce(pdacl, ACL_REVISION,
FILE_MAP_READ,
(PVOID)&Admin3);
ASSERT(bRes);
*/
bRes = SetSecurityDescriptorDacl(SecurityDescriptor, TRUE, pdacl, FALSE);
ASSERT(bRes);
ASSERT(IsValidSecurityDescriptor(SecurityDescriptor));
return RPC_S_OK;
}
void CellHeap::InsertNewPageSegmentInChain(DebugFreeCell *pFirstCell, DebugFreeCell *pLastCell)
{
LIST_ENTRY *EX_Blink;
CellHeapMutex.VerifyOwned();
// chain the cells in the section to the free list
ASSERT(pFirstCell->FreeCellsChain.Blink == NULL);
ASSERT(pLastCell->FreeCellsChain.Flink == NULL);
// create the links from the list to the new segment
EX_Blink = FreeCellsList.Blink;
FreeCellsList.Blink = &(pLastCell->FreeCellsChain);
EX_Blink->Flink = &(pFirstCell->FreeCellsChain);
// create the links from the new segment to the list
pFirstCell->FreeCellsChain.Blink = EX_Blink;
pLastCell->FreeCellsChain.Flink = &FreeCellsList;
}
RPC_STATUS CellHeap::AllocateCellSection(BOOL fFirstSection)
{
RPC_STATUS Status;
CellSection::AllocateCellSection(&Status, fFirstSection, SecurityDescriptor, this);
if (fFirstSection && (Status == RPC_S_OK))
{
pFirstSection = CONTAINING_RECORD(SectionsList.Flink, CellSection, SectionListEntry);
}
return Status;
}
#if DBG
RPC_STATUS CellHeap::OpenSection(OUT HANDLE *pHandle, OUT PVOID *pSection, IN DWORD *pSectionNumbers)
{
return OpenDbgSection(pHandle, pSection, GetCurrentProcessId(), pSectionNumbers);
}
#endif
RPC_STATUS InitializeCellHeap(void)
{
RPC_STATUS Status = RPC_S_OK;
g_pCellHeap = new CellHeap(&Status);
if (g_pCellHeap == NULL)
Status = RPC_S_OUT_OF_MEMORY;
else if (Status != RPC_S_OK)
{
delete g_pCellHeap;
g_pCellHeap = NULL;
}
return Status;
}
C_ASSERT(sizeof(DebugCallInfo) <= 32);
C_ASSERT(sizeof(DebugConnectionInfo) <= 32);
C_ASSERT(sizeof(DebugThreadInfo) <= 32);
C_ASSERT(sizeof(DebugEndpointInfo) <= 32);
C_ASSERT(sizeof(DebugClientCallInfo) <= 32);
C_ASSERT(sizeof(DebugCallTargetInfo) <= 32);
C_ASSERT(sizeof(DebugFreeCell) <= 32);
C_ASSERT(sizeof(DebugCellUnion) <= 32);
// uncomment this for cell heap unit tests
// #define CELL_HEAP_UNIT_TESTS
// cell heap unit tests
#ifdef CELL_HEAP_UNIT_TESTS
typedef struct tagCellTestSectionState
{
int CommitedPages;
int UsedCellsInSection;
} CellTestSectionState;
typedef struct tagCellTestBase
{
int NumberOfSections;
BOOL fCachedSectionPresent;
int LastCommand;
DWORD LastCommandParams[2];
CellTestSectionState sectionsState[1];
} CellTestBase;
class TestCellAllocation
{
public:
int NumberOfCells;
DebugFreeCell **ppCellArray;
CellTag *pTagsArray;
void Free(void);
};
void TestCellAllocation::Free(void)
{
int i;
for (i = 0; i < NumberOfCells; i ++)
{
FreeCell(&(*ppCellArray[i]), &(pTagsArray[i]));
}
delete ppCellArray;
delete pTagsArray;
}
typedef DebugFreeCell *DebugFreeCellPtr;
NEW_SDICT(TestCellAllocation);
class TestState
{
public:
TestCellAllocation_DICT Allocations;
void Free(int Allocation)
{
TestCellAllocation *pAllocation;
pAllocation = Allocations.Find(Allocation);
ASSERT(pAllocation != NULL);
pAllocation->Free();
Allocations.Delete(Allocation);
delete pAllocation;
}
void Allocate(int NumberOfCells)
{
int i;
TestCellAllocation *pTestAllocation;
pTestAllocation = new TestCellAllocation;
ASSERT(pTestAllocation);
pTestAllocation->NumberOfCells = NumberOfCells;
pTestAllocation->ppCellArray = new DebugFreeCellPtr[NumberOfCells];
ASSERT(pTestAllocation->ppCellArray);
pTestAllocation->pTagsArray = new CellTag[NumberOfCells];
ASSERT(pTestAllocation->pTagsArray);
for (i = 0; i < NumberOfCells; i ++)
{
pTestAllocation->ppCellArray[i] = AllocateCell(&pTestAllocation->pTagsArray[i]);
ASSERT(pTestAllocation->ppCellArray[i] != NULL);
}
i = Allocations.Insert(pTestAllocation);
ASSERT(i != -1);
}
};
typedef enum tagCellHeapTestActions
{
chtaFree,
chtaAllocate,
chtaFreeAll
} CellHeapTestActions;
#endif
void RPC_ENTRY I_RpcDoCellUnitTest(IN OUT void *p)
{
#ifdef CELL_HEAP_UNIT_TESTS
const int NumberOfIterations = 383 * 3;
const int NumberOfCellsPerSection = 383;
DebugFreeCell *Cells[NumberOfIterations];
CellTag Tags[NumberOfIterations];
int i, j;
CellTestBase *pTestBase = *(CellTestBase **)p;
static TestState *pTestState = NULL;
DWORD RandomNumbers[2];
int NumberOfItemsToAllocate;
int ItemToFree;
RPC_STATUS RpcStatus;
CellHeapTestActions ActionChosen;
TestCellAllocation *pCurrentAllocation;
int DictCursor;
BOOL fFound;
CellSection *pCellSection;
DWORD LastCommandParams[2];
ULONG Command;
// ServerEnumerationHandle *hServers = (HANDLE *)p;
// StartServerEnumeration(hServers);
CellEnumerationHandle h;
Command = (ULONG)pTestBase;
if ((Command < 0xFFFF) && (Command != 0))
{
RpcStatus = OpenRPCServerDebugInfo(Command, &h);
if (RpcStatus == RPC_S_OK)
{
CloseRPCServerDebugInfo(&h);
}
else
{
ASSERT(0);
}
return;
}
// Cell heap unit tests
// if there are old test results, delete them
if (pTestBase)
delete pTestBase;
if (pTestState == NULL)
{
pTestState = new TestState;
}
RpcStatus = GenerateRandomNumber((unsigned char *)RandomNumbers, 8);
ASSERT(RpcStatus == RPC_S_OK);
// is there something to free?
if (pTestState->Allocations.Size() == 0)
{
ActionChosen = chtaAllocate;
NumberOfItemsToAllocate = RandomNumbers[1] % 5 + 1;
}
else
{
// we can do it both ways - check the random number to figure out which
if ((RandomNumbers[0] % 2777) == 0)
{
// once in a great while, free everything
ActionChosen = chtaFreeAll;
}
else if ((RandomNumbers[0] % 100) > 48)
{
// allocations have a slight edge
ActionChosen = chtaAllocate;
NumberOfItemsToAllocate = RandomNumbers[1] % 5 + 1;
}
else
{
ActionChosen = chtaFree;
ItemToFree = RandomNumbers[1] % pTestState->Allocations.Size();
}
}
switch (ActionChosen)
{
case chtaFreeAll:
pTestState->Allocations.Reset(DictCursor);
while ((pCurrentAllocation = pTestState->Allocations.Next(DictCursor)) != NULL)
{
pTestState->Free(DictCursor - 1);
}
break;
case chtaAllocate:
pTestState->Allocate(NumberOfItemsToAllocate);
LastCommandParams[0] = NumberOfItemsToAllocate;
break;
case chtaFree:
i = 0;
fFound = FALSE;
pTestState->Allocations.Reset(DictCursor);
while ((pCurrentAllocation = pTestState->Allocations.Next(DictCursor)) != NULL)
{
if (ItemToFree == i)
{
LastCommandParams[0] = pCurrentAllocation->NumberOfCells;
LastCommandParams[1] = DictCursor - 1;
pTestState->Free(DictCursor - 1);
fFound = TRUE;
break;
}
i ++;
}
ASSERT(fFound == TRUE);
break;
}
// build the state
pTestBase = (CellTestBase *) new unsigned char [sizeof(CellTestBase)
+ sizeof(CellTestSectionState) * (g_pCellHeap->CellHeapSections.Size() - 1)];
ASSERT(pTestBase);
pTestBase->LastCommand = ActionChosen;
pTestBase->LastCommandParams[0] = LastCommandParams[0];
pTestBase->LastCommandParams[1] = LastCommandParams[1];
pTestBase->NumberOfSections = g_pCellHeap->CellHeapSections.Size();
pTestBase->fCachedSectionPresent = (pCachedCellSection != NULL);
i = 0;
g_pCellHeap->CellHeapSections.Reset(DictCursor);
while ((pCellSection = g_pCellHeap->CellHeapSections.Next(DictCursor)) != NULL)
{
pTestBase->sectionsState[i].CommitedPages = pCellSection->LastCommittedPage;
pTestBase->sectionsState[i].UsedCellsInSection = pCellSection->NumberOfUsedCells;
i ++;
}
*(CellTestBase **)p = pTestBase;
/*
for (j = 0; j < 2; j ++)
{
// do the allocations
for (i = 0; i < NumberOfCellsPerSection; i ++)
{
Cells[i] = AllocateCell(&Tags[i]);
}
for (i = NumberOfCellsPerSection; i < NumberOfCellsPerSection * 2; i ++)
{
Cells[i] = AllocateCell(&Tags[i]);
}
for (i = NumberOfCellsPerSection * 2; i < NumberOfCellsPerSection * 3; i ++)
{
Cells[i] = AllocateCell(&Tags[i]);
}
// do the freeing
for (i = NumberOfCellsPerSection; i < NumberOfCellsPerSection * 2; i ++)
{
FreeCell(Cells[i], &Tags[i]);
}
for (i = 0; i < NumberOfCellsPerSection; i ++)
{
FreeCell(Cells[i], &Tags[i]);
}
for (i = NumberOfCellsPerSection * 2; i < NumberOfCellsPerSection * 3; i ++)
{
FreeCell(Cells[i], &Tags[i]);
}
}
*/
#endif
}