mirror of https://github.com/tongzx/nt5src
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.
634 lines
14 KiB
634 lines
14 KiB
/***************************************************************************
|
|
|
|
File Name: Alloc.cpp
|
|
|
|
COPYRIGHT© 2001 Microsoft Corporation and Executive Software International, Inc.
|
|
|
|
*/
|
|
|
|
#include "stdafx.h"
|
|
#ifdef BOOTIME
|
|
extern "C"{
|
|
#include <stdio.h>
|
|
}
|
|
#include "Offline.h"
|
|
#else
|
|
#ifndef NOWINDOWSH
|
|
#include <windows.h>
|
|
#endif
|
|
#endif
|
|
|
|
|
|
#include "ErrMacro.h"
|
|
#include "alloc.h"
|
|
|
|
/****************************************************************************
|
|
|
|
COPYRIGHT© 2001 Microsoft Corporation and Executive Software International, Inc.
|
|
|
|
ROUTINE DESCRIPTION:
|
|
Allocate or reallocate a block of memory and optionally lock it.
|
|
If ppMemory != NULL, then lock the memory.
|
|
|
|
GLOBALS:
|
|
ErrorTable is defined in errors.h and corresponds the error numbers
|
|
to the text define for them.
|
|
|
|
INPUT:
|
|
SizeInBytes - How much memory to allocate or reallocate
|
|
phMemory - receives the handle to the memory
|
|
ppMemory - Optionally receives the pointer to the locked memory
|
|
|
|
RETURN:
|
|
TRUE = Success
|
|
FALSE = Failure
|
|
*/
|
|
|
|
BOOL
|
|
AllocateMemory(
|
|
IN DWORD SizeInBytes,
|
|
IN OUT PHANDLE phMemory,
|
|
IN OUT PVOID* ppMemory
|
|
)
|
|
{
|
|
BOOL bStatus = FALSE;
|
|
|
|
HGLOBAL hTemp = NULL;
|
|
|
|
// Check for zero size to allocate.
|
|
if (SizeInBytes == 0){
|
|
EH(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
__try {
|
|
|
|
// Check to see if the handle is NULL. This means no memory yet - so allocate some.
|
|
if(*phMemory == NULL) {
|
|
|
|
// Allocate the requested memory.
|
|
*phMemory = GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT, SizeInBytes);
|
|
if (*phMemory == NULL){
|
|
EH(FALSE);
|
|
bStatus = FALSE;
|
|
}
|
|
else {
|
|
bStatus = TRUE;
|
|
}
|
|
__leave;
|
|
}
|
|
// We already have some memory so we want to reallocate.
|
|
// First check if the size of the memory being requested
|
|
// is the same as that already allocated.
|
|
if(GlobalSize(*phMemory) == SizeInBytes) {
|
|
bStatus = TRUE;
|
|
__leave;
|
|
}
|
|
|
|
// Check to see if we have a pointer to locked memory.
|
|
if((ppMemory != NULL) && (*ppMemory != NULL)) {
|
|
|
|
// Unlock it so that we can reallocate.
|
|
GlobalUnlock(*phMemory);
|
|
}
|
|
|
|
// Reallocate the memory. Store the return value in a temporary handle
|
|
// so that we still have a pointer to (clean-up) the original memory
|
|
// block if GlobalReAlloc fails. (RAID 516728)
|
|
hTemp = GlobalReAlloc(*phMemory, SizeInBytes, GMEM_MOVEABLE|GMEM_ZEROINIT);
|
|
if (hTemp == NULL){
|
|
EH(FALSE);
|
|
bStatus = FALSE;
|
|
__leave;
|
|
}
|
|
else {
|
|
*phMemory = hTemp;
|
|
}
|
|
|
|
// Success.
|
|
bStatus = TRUE;
|
|
}
|
|
__finally {
|
|
|
|
// If there was an error then cleanup.
|
|
if (bStatus == FALSE) {
|
|
|
|
// Check to see if we have a pointer.
|
|
if((ppMemory != NULL) && (*ppMemory != NULL) && (*phMemory != NULL)) {
|
|
|
|
// So unlock it the memory.
|
|
EH_ASSERT(GlobalUnlock(*phMemory) == FALSE);
|
|
*ppMemory = NULL;
|
|
}
|
|
// Now Free the memory.
|
|
if(*phMemory != NULL) {
|
|
|
|
EH_ASSERT(GlobalFree(*phMemory) == NULL);
|
|
*phMemory = NULL;
|
|
}
|
|
} else {
|
|
// Check to see if we have a pointer to a pointer to locked memory.
|
|
if((ppMemory != NULL) && (*phMemory != NULL)) {
|
|
|
|
// Lock the memory and return the pointer..
|
|
*ppMemory = GlobalLock(*phMemory);
|
|
if (*ppMemory == NULL){
|
|
bStatus = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return bStatus;
|
|
}
|
|
|
|
|
|
/*
|
|
VOID
|
|
SapDumpSlabList(
|
|
IN CONST PSA_CONTEXT pSaContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Utility function to dump out the slabs for a given Context
|
|
|
|
Arguments:
|
|
pSaContext - The context for which the slabs are to be dumped
|
|
|
|
Return Value:
|
|
None
|
|
|
|
//--/
|
|
{
|
|
PSLAB_HEADER pSlab = pSaContext->pAllocatedSlabs;
|
|
DWORD dwCount = 0;
|
|
|
|
//
|
|
// Slabs in use
|
|
//
|
|
printf("\n** Dumping allocated slabs: ");
|
|
while (pSlab) {
|
|
|
|
++dwCount;
|
|
printf("%p(%lu) ", pSlab, pSlab->dwFreeCount);
|
|
pSlab = pSlab->pNext;
|
|
}
|
|
printf("%p <<%lu>>\n", pSlab, dwCount);
|
|
|
|
//
|
|
// Free slab cache
|
|
//
|
|
dwCount = 0;
|
|
pSlab = pSaContext->pFreeSlabs;
|
|
printf("** Dumping free slabs: ");
|
|
while (pSlab) {
|
|
|
|
++dwCount;
|
|
printf("%p ", pSlab);
|
|
pSlab = pSlab->pNext;
|
|
}
|
|
printf("%p (%lu)\n\n", pSlab, dwCount);
|
|
|
|
}
|
|
*/
|
|
|
|
|
|
inline
|
|
VOID
|
|
SapUnlinkSlab(
|
|
IN CONST PSLAB_HEADER pSlab
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Utility function to remove a slab from the linked list. Note that the
|
|
pNext and pPrev for this slab are left untouched. It is the caller's
|
|
responsibility to ensure that he doesn't use those pointers!
|
|
|
|
Arguments:
|
|
pSlab - Slab to be unlinked.
|
|
|
|
Return Value:
|
|
None
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Unlink from the chain
|
|
//
|
|
if (pSlab->pNext) {
|
|
pSlab->pNext->pPrev = pSlab->pPrev;
|
|
}
|
|
|
|
if (pSlab->pPrev) {
|
|
pSlab->pPrev->pNext = pSlab->pNext;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
PSLAB_HEADER
|
|
SapGarbageCollect(
|
|
IN OUT PSA_CONTEXT pSaContext,
|
|
IN OUT PSLAB_HEADER pSlab
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Routine to deal with slabs that are freed. The first couple of freed
|
|
slabs will generally end up in our free list (which is used as a cache
|
|
for future allocations). If our free list already has two empty slabs,
|
|
we return this slab to the system.
|
|
|
|
Arguments:
|
|
pSaContext - The slab-allocator context
|
|
|
|
pSlab - Slab that was freed. This may be NULL, in which case this
|
|
routine will just return NULL.
|
|
|
|
Return Value:
|
|
pSlab->pNext
|
|
|
|
--*/
|
|
{
|
|
PSLAB_HEADER pFree = pSaContext->pFreeSlabs;
|
|
PSLAB_HEADER pNext = NULL;
|
|
|
|
if (NULL == pSlab) {
|
|
return NULL;
|
|
}
|
|
|
|
pNext = pSlab->pNext;
|
|
//
|
|
// If this is the first slab being freed, move the slabHead pointer to
|
|
// the next slab
|
|
//
|
|
if (pSlab == pSaContext->pAllocatedSlabs) {
|
|
pSaContext->pAllocatedSlabs = pNext;
|
|
}
|
|
|
|
//
|
|
// Make sure we unlink this slab from our chain of allocated slabs
|
|
//
|
|
SapUnlinkSlab(pSlab);
|
|
|
|
//
|
|
// Check what we should do with this slab--add it to our free list,
|
|
// or return to the system
|
|
//
|
|
if ((pFree) && (pFree->pNext)) {
|
|
//
|
|
// We already have two free slabs, return this to the system
|
|
//
|
|
HeapFree(GetProcessHeap(), 0L, pSlab);
|
|
}
|
|
else {
|
|
//
|
|
// We have less than two entries in our free slab list. Add this
|
|
// slab to the head of the free list
|
|
//
|
|
pSlab->pPrev = NULL;
|
|
pSlab->pNext = pFree;
|
|
|
|
if (pFree) {
|
|
pFree->pPrev = pSlab;
|
|
}
|
|
|
|
pSaContext->pFreeSlabs = pSlab;
|
|
}
|
|
|
|
return pNext;
|
|
}
|
|
|
|
|
|
PVOID
|
|
SaAllocatePacket(
|
|
IN OUT PSA_CONTEXT pSaContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Returns a packet of size SIZE_OF_PACKET bytes. May return NULL if the
|
|
system is out of free memory.
|
|
|
|
SaInitialiseContext must have been called prior to this routine
|
|
being called.
|
|
|
|
Arguments:
|
|
pSaContext - The slab-allocator context
|
|
|
|
Return Value:
|
|
Pointer to a packet, or NULL if the system is out of memory.
|
|
|
|
Packets returned should be freed using SaFreePacket (or SaFreeAllPackets).
|
|
|
|
--*/
|
|
{
|
|
PSLAB_HEADER pSlab = NULL;
|
|
PPACKET_HEADER pReturn = NULL;
|
|
|
|
//
|
|
// Check input parameters
|
|
//
|
|
if (NULL == pSaContext) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Find the first slab with unallocated packets
|
|
//
|
|
pSlab = pSaContext->pAllocatedSlabs;
|
|
while ((NULL != pSlab) && (0 == pSlab->dwFreeCount)) {
|
|
//
|
|
// This slab is fully allocated, try the next one (till we run out
|
|
// of slabs)
|
|
//
|
|
pSlab = pSlab->pNext;
|
|
}
|
|
|
|
if (NULL == pSlab) {
|
|
//
|
|
// All slabs are full, allocate a new slab. Check if our free
|
|
// slab list has any slabs we can use.
|
|
//
|
|
if (pSaContext->pFreeSlabs != NULL) {
|
|
|
|
pSlab = pSaContext->pFreeSlabs;
|
|
// pSlab->pNext and pPrev will be initialised below
|
|
|
|
|
|
pSaContext->pFreeSlabs = pSaContext->pFreeSlabs->pNext;
|
|
if (pSaContext->pFreeSlabs) {
|
|
pSaContext->pFreeSlabs->pPrev = NULL;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// Our free list is empty. Allocate a new slab from ProcessHeap.
|
|
//
|
|
pSlab = (PSLAB_HEADER) HeapAlloc(GetProcessHeap(),
|
|
0L,
|
|
pSaContext->dwSlabSize);
|
|
|
|
if (NULL == pSlab) {
|
|
//
|
|
// System is out of memory. Sigh.
|
|
//
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Initialise the new slab
|
|
//
|
|
pSlab->pNext = pSaContext->pAllocatedSlabs;
|
|
pSlab->pPrev = NULL;
|
|
pSlab->pFree = NULL;
|
|
pSlab->dwFreeCount = pSaContext->dwPacketsPerSlab;
|
|
|
|
if (pSaContext->pAllocatedSlabs) {
|
|
//
|
|
// Add it to the head of the chain
|
|
//
|
|
pSaContext->pAllocatedSlabs->pPrev = pSlab;
|
|
}
|
|
pSaContext->pAllocatedSlabs = pSlab;
|
|
}
|
|
|
|
//
|
|
// Get the packet to return;
|
|
//
|
|
if (NULL == pSlab->pFree) {
|
|
pReturn = (PPACKET_HEADER) ((LPBYTE)pSlab + sizeof(SLAB_HEADER) +
|
|
((pSaContext->dwPacketsPerSlab - (pSlab->dwFreeCount)) *
|
|
pSaContext->dwPacketSize));
|
|
}
|
|
else {
|
|
pReturn = pSlab->pFree;
|
|
pSlab->pFree = pSlab->pFree->pNext;
|
|
}
|
|
|
|
//
|
|
// And decrement our free count
|
|
//
|
|
--(pSlab->dwFreeCount);
|
|
|
|
//
|
|
// Write the slab header at the start of the packet, and return the rest
|
|
// of the packet to the caller
|
|
//
|
|
pReturn->pSlab = pSlab;
|
|
return (PVOID) ((LPBYTE)pReturn + sizeof(PACKET_HEADER));
|
|
}
|
|
|
|
|
|
VOID
|
|
SaFreePacket(
|
|
IN PSA_CONTEXT pSaContext,
|
|
IN PVOID pMemory
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Frees a packet allocated by SaAllocatePacket.
|
|
|
|
Arguments:
|
|
pSaContext - The slab-allocator context
|
|
pMemory - The memory to be freed.
|
|
|
|
Return Value:
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PSLAB_HEADER pSlab = NULL;
|
|
PPACKET_HEADER pPacket = NULL;
|
|
|
|
if ((NULL == pSaContext) || (NULL == pMemory)) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// The packet starts one pointer-length before the memory we returned
|
|
// to the caller
|
|
//
|
|
pPacket = (PPACKET_HEADER)((LPBYTE)pMemory - sizeof(PACKET_HEADER));
|
|
|
|
//
|
|
// The slab start address is written at the start of our packet
|
|
//
|
|
pSlab = pPacket->pSlab;
|
|
|
|
//
|
|
// Add this packet to the (head of the) slab's free packet list
|
|
//
|
|
pPacket->pNext = pSlab->pFree;
|
|
pSlab->pFree = pPacket;
|
|
|
|
//
|
|
// Increment the FreeCount for the slab
|
|
//
|
|
++(pSlab->dwFreeCount);
|
|
|
|
//
|
|
// Check if the entire slab is free
|
|
//
|
|
if (pSaContext->dwPacketsPerSlab == pSlab->dwFreeCount) {
|
|
SapGarbageCollect(pSaContext, pSlab);
|
|
}
|
|
else {
|
|
//
|
|
// If this has more than twice as many free packets as the slab at
|
|
// the list head, move this up
|
|
//
|
|
if ((pSlab->dwFreeCount > 4) &&
|
|
(pSlab->dwFreeCount > ((pSaContext->pAllocatedSlabs->dwFreeCount) * 2))
|
|
) {
|
|
|
|
SapUnlinkSlab(pSlab);
|
|
|
|
pSlab->pNext = pSaContext->pAllocatedSlabs;
|
|
pSlab->pPrev = NULL;
|
|
|
|
if (pSaContext->pAllocatedSlabs) {
|
|
pSaContext->pAllocatedSlabs->pPrev = pSlab;
|
|
}
|
|
|
|
pSaContext->pAllocatedSlabs = pSlab;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
SaFreeAllPackets(
|
|
IN OUT PSA_CONTEXT pSaContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Frees all packets that have been allocated for a given context
|
|
|
|
Arguments:
|
|
pSaContext - The slab-allocator context that is to be freed
|
|
|
|
Return Value:
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PSLAB_HEADER pTemp = NULL,
|
|
pSlab = pSaContext->pAllocatedSlabs;
|
|
|
|
if (NULL == pSaContext) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Free all allocated slabs. No need to free invidiual packets
|
|
//
|
|
while (SapGarbageCollect(pSaContext, pSaContext->pAllocatedSlabs))
|
|
;
|
|
}
|
|
|
|
|
|
BOOL
|
|
SaInitialiseContext(
|
|
IN OUT PSA_CONTEXT pSaContext,
|
|
IN CONST DWORD dwPacketSize,
|
|
IN CONST DWORD dwSlabSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Initialisation routine to set up a slab allocator context. This routine
|
|
must be called before any of the Sa* routines above.
|
|
|
|
Arguments:
|
|
pSaContext - The slab-allocator context to set up
|
|
|
|
dwPacketSize - The size of a packet for this slab, in bytes
|
|
|
|
dwSlabSize - The size of a slab. Generally close to a PAGE_SIZE.
|
|
|
|
Return Value:
|
|
TRUE - Initialization completed successfully
|
|
|
|
FALSE - The slab wasn't initialized. (Generally because the parameters
|
|
are invalid)
|
|
|
|
--*/
|
|
{
|
|
if ((NULL == pSaContext) || (0 == dwPacketSize) || (0 == dwSlabSize)) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
pSaContext->pAllocatedSlabs = NULL;
|
|
pSaContext->pFreeSlabs = NULL;
|
|
|
|
pSaContext->dwPacketSize = dwPacketSize
|
|
+ sizeof(struct _PACKET_HEADER) // allow for our packet header
|
|
+ (sizeof(PVOID) * 4); // allow for the AVL tree over-head
|
|
|
|
pSaContext->dwSlabSize = dwSlabSize;
|
|
|
|
pSaContext->dwPacketsPerSlab =
|
|
(pSaContext->dwSlabSize - sizeof(struct _SLAB_HEADER)) / (pSaContext->dwPacketSize);
|
|
|
|
|
|
if (pSaContext->dwPacketsPerSlab < 1) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
SaFreeContext(
|
|
IN OUT PSA_CONTEXT pSaContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Clean up routine to free all memory associated with a slab allocator
|
|
context.
|
|
|
|
Arguments:
|
|
pSaContext - The slab-allocator context to be cleaned up
|
|
|
|
Return Value:
|
|
None
|
|
|
|
--*/
|
|
{
|
|
if (!pSaContext) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Free all allocated packets
|
|
//
|
|
SaFreeAllPackets(pSaContext);
|
|
|
|
//
|
|
// Return the free slabs to the system
|
|
//
|
|
if (pSaContext->pFreeSlabs) {
|
|
|
|
if (pSaContext->pFreeSlabs->pNext) {
|
|
HeapFree(GetProcessHeap(), 0L, pSaContext->pFreeSlabs->pNext);
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(), 0L, pSaContext->pFreeSlabs);
|
|
pSaContext->pFreeSlabs = NULL;
|
|
}
|
|
|
|
//
|
|
// And zero out the struct
|
|
//
|
|
ZeroMemory(pSaContext, sizeof(SA_CONTEXT));
|
|
}
|