windows-nt/Source/XPSP1/NT/inetsrv/iis/svcs/infocomm/cache/alloc.cxx

500 lines
13 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
#include "TsunamiP.Hxx"
#pragma hdrstop
BOOL
TsAllocate(
IN const TSVC_CACHE &TSvcCache,
IN ULONG cbSize,
IN OUT PVOID * ppvNewBlock
)
{
return( TsAllocateEx( TSvcCache,
cbSize,
ppvNewBlock,
NULL ) );
} // TsAllocate
BOOL
TsAllocateEx(
IN const TSVC_CACHE &TSvcCache,
IN ULONG cbSize,
IN OUT PVOID * ppvNewBlock,
OPTIONAL PUSER_FREE_ROUTINE pfnFreeRoutine
)
/*++
Routine Description:
This function allocates a memory block for the calling server.
The returned block is suitable for use as a parameter to
TsCacheDirectoryBlob(). Blocks allocated by this function
must either be cached or freed with TsFree(). Freeing of
cached blocks will be handled by the cache manager.
Arguments:
pServiceInfo - An initialized SERVICE_INFO structure.
cbSize - Number of bytes to allocate. (Must be strictly
greater than zero.)
ppvNewBlock - Address of a pointer to store the new block's
address in.
Return Value:
TRUE - The allocation succeeded, and *ppvNewBlock points to
at least cbSize accessable bytes.
FALSE - The allocation failed.
--*/
{
PBLOB_HEADER pbhNewBlock;
ASSERT( cbSize > 0 );
ASSERT( ppvNewBlock != NULL );
//
// Set pbhNewBlock to NULL so that the exception-cleanup code
// can test against it to see if an allocation occurred before
// the exception.
//
pbhNewBlock = NULL;
__try
{
//
// If asked to allocate zero bytes, we return FALSE and NULL,
// as if allocation failure had occurred.
//
if ( cbSize != 0 )
{
pbhNewBlock = ( PBLOB_HEADER )
ALLOC( cbSize + sizeof( BLOB_HEADER ) );
}
if ( pbhNewBlock != NULL )
{
//
// If the allocation succeeded, we return a pointer to the
// memory just following the BLOB_HEADER.
//
*ppvNewBlock = ( PVOID )( pbhNewBlock + 1 );
//
// Set up the BLOB_HEADER: Normal flags and stored allocation
// size.
//
pbhNewBlock->IsCached = FALSE;
pbhNewBlock->pfnFreeRoutine = pfnFreeRoutine;
InitializeListHead( &pbhNewBlock->PFList );
}
else
{
//
// The allocation failed, and we need to return NULL
//
*ppvNewBlock = NULL;
}
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
//
// An exception transpired. The most likely causes are bogus input
// pointers for pServiceInfo and pbhNewBlock. Whatever the case, we
// free up any memory that may have been allocated and return failure.
//
if ( pbhNewBlock != NULL )
{
FREE( pbhNewBlock );
pbhNewBlock = NULL;
}
}
//
// Return TRUE or FALSE, according to the result of the allocation.
//
if ( pbhNewBlock == NULL )
{
SetLastError( ERROR_NOT_ENOUGH_MEMORY);
return( FALSE );
}
INC_COUNTER( TSvcCache.GetServiceId(),
CurrentObjects );
return( TRUE );
} // TsAllocate
BOOL
TsReallocate(
IN const TSVC_CACHE &TSvcCache,
IN ULONG cbSize,
IN PVOID pvOldBlock,
IN OUT PVOID * ppvNewBlock
)
/*++
Routine Description:
This function will resize a previously allocated memory Blob
for the calling server, possibly moving it in the process.
Arguments:
pServiceInfo - An initialized SERVICE_INFO structure.
cbSize - Number of bytes to resize the block to.
(Must be strictly greater than zero.)
pvOldBlock - Address of a pointer to a previously-allocated
block.
ppvNewBlock - Address of a pointer to store the new block's
address in. If the allocation fails, NULL is
stored here. Note that in many cases,
pvOldBlock will be stored here.
Return Value:
TRUE - The reallocation succeeded, and *ppvNewBlock points to
at least cbSize accessable bytes. pvOldBlock is no
longer a valid pointer, if *ppvNewBlock!=pvOldBlock.
FALSE - The allocation failed. *ppvNewBlock = NULL.
pvOldBlock is still a valid pointer to the block that
we wished to resize.
--*/
{
PBLOB_HEADER pbhNewBlock;
PBLOB_HEADER pbhOldBlock;
ASSERT( pvOldBlock != NULL );
//
// Set pbhNewBlock to NULL so that the exception-cleanup code
// can test against it to see if an allocation occurred before
// the exception.
//
pbhNewBlock = NULL;
__try
{
//
// Adjust the input pointer to refer to the BLOB_HEADER.
//
pbhOldBlock = (( PBLOB_HEADER )pvOldBlock ) - 1;
//
// If the Blob is currently cached, we can't move it
// or change its size. Check for this in the Blob's
// flags, and fail if it occurs.
//
if ( pbhOldBlock->IsCached )
{
DBGPRINTF(( DBG_CONTEXT,
"A service (%d) has attempted to TsRealloc a BLOB that is cached.",
TSvcCache.GetServiceId() ));
BREAKPOINT();
SetLastError( ERROR_INVALID_PARAMETER );
}
else
{
//
// The following assignment probes ppvNewBlock for writeability.
// Hopefully, this ensures that we get an AV from writing to it
// before we call REALLOC and potentially free the old block.
//
*ppvNewBlock = NULL;
pbhNewBlock = ( PBLOB_HEADER )REALLOC( pbhOldBlock, cbSize );
if ( pbhNewBlock != NULL )
{
//
// Store a pointer to the caller-usable part of the new Blob in
// the output parameter.
//
*ppvNewBlock = ( PVOID )( pbhNewBlock + 1 );
}
}
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
//
// An exception occured. If this was caught after a block was
// allocated, we must free the new block. Unfortunately, this
// means that we may end up returning FALSE in a case where
// pbhOldBlock is no longer valid.
//
// if ( pbhOldBlock == pbhNewBlock ), which implies the old
// pointer is still valid, we do not free the new block, but
// hope that the caller will. In any other case, we must
// free the new block to avoid a memory leak, and assume that
// TCPSVCs are going down soon...
//
// ISSUE: It might be best to reflect the exception up to the
// caller, so they can handle it and bail out of the current
// operation.
//
if ( pbhNewBlock != NULL && pbhOldBlock != pbhNewBlock )
{
FREE( pbhNewBlock );
pbhNewBlock = NULL;
SetLastError( ERROR_INVALID_PARAMETER );
}
}
//
// Return TRUE or FALSE, according to the result of the allocation.
//
if ( pbhNewBlock == NULL )
{
return( FALSE );
}
return( TRUE );
} // TsReallocate
BOOL
TsFree(
IN const TSVC_CACHE &TSvcCache,
IN PVOID pvOldBlock
)
/*++
Routine Description:
This function frees a memory block allocated with TsAllocate().
Blocks that are currently cached cannot be freed with this
function.
Arguments:
pServiceInfo - An initialized SERVICE_INFO structure.
pvOldBlock - The address of the block to free. (Must be
non-NULL.)
Return Value:
TRUE - The block was freed. The pointer pvOldBlock is no longer
valid.
FALSE - The block was not freed. Possible reasons include:
- pvOldBlock does not point to a block allocated with
TsAllocate().
- pvOldBlock points to a block that has been cached
with CacheDirectoryBlob().
- pServiceInfo does not point to a valid SERVICE_INFO
structure.
--*/
{
BOOLEAN bSuccess;
PBLOB_HEADER pbhOldBlock;
ASSERT( pvOldBlock != NULL );
__try
{
//
// Adjust the input pointer to refer to the BLOB_HEADER.
//
pbhOldBlock = (( PBLOB_HEADER )pvOldBlock ) - 1;
if (!DisableSPUD) {
EnterCriticalSection( &CacheTable.CriticalSection );
if ( !IsListEmpty( &pbhOldBlock->PFList ) ) {
RemoveEntryList( &pbhOldBlock->PFList );
}
LeaveCriticalSection( &CacheTable.CriticalSection );
}
//
// If the Blob is currently in the cache, we can't free it.
// Check for this in the Blob's flags, and fail if it
// occurs.
//
if ( pbhOldBlock->IsCached )
{
DBGPRINTF(( DBG_CONTEXT,
"A service (%d) has attempted to TsFree a BLOB that it put in the cache.",
TSvcCache.GetServiceId() ));
BREAKPOINT();
bSuccess = FALSE;
}
else
{
if ( pbhOldBlock->pfnFreeRoutine )
{
bSuccess = pbhOldBlock->pfnFreeRoutine( pvOldBlock );
}
else
{
bSuccess = TRUE;
}
if ( bSuccess )
{
//
// Free the memory used by the Blob.
//
bSuccess = !!FREE( pbhOldBlock );
DEC_COUNTER( TSvcCache.GetServiceId(),
CurrentObjects );
}
}
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
//
// Handle exception. Obviously, it's time to return failure.
// It's hardly possible to get here after succefully freeing
// the block, so it's likely that an app will either:
// - not check the return value and leak some memory.
// - check the return value and try again, probably
// only to fail forever.
//
// So, it is not advisable for callers to keep trying this
// call until it succeeds.
//
bSuccess = FALSE;
}
return( bSuccess );
} // TsFree
BOOL
TsCheckInOrFree(
IN PVOID pvOldBlock
)
/*++
Routine Description:
This function checks in a cached memory block or
frees a non-cached memory block allocated with TsAllocate().
Arguments:
pServiceInfo - An initialized SERVICE_INFO structure.
pvOldBlock - The address of the block to free. (Must be
non-NULL.)
Return Value:
TRUE - The block was freed. The pointer pvOldBlock is no longer
valid.
FALSE - The block was not freed. Possible reasons include:
- pvOldBlock does not point to a block allocated with
TsAllocate().
--*/
{
BOOLEAN bSuccess;
PBLOB_HEADER pbhOldBlock;
ASSERT( pvOldBlock != NULL );
__try
{
//
// Adjust the input pointer to refer to the BLOB_HEADER.
//
pbhOldBlock = (( PBLOB_HEADER )pvOldBlock ) - 1;
if (!DisableSPUD) {
EnterCriticalSection( &CacheTable.CriticalSection );
if (!IsListEmpty( &pbhOldBlock->PFList ) ) {
RemoveEntryList( &pbhOldBlock->PFList );
}
LeaveCriticalSection( &CacheTable.CriticalSection );
}
if (BLOB_IS_OR_WAS_CACHED(pvOldBlock)) {
bSuccess = TsCheckInCachedBlob( pvOldBlock );
} else {
if ( pbhOldBlock->pfnFreeRoutine )
{
bSuccess = pbhOldBlock->pfnFreeRoutine( pvOldBlock );
}
else
{
bSuccess = TRUE;
}
if ( bSuccess )
{
//
// Free the memory used by the Blob.
//
bSuccess = !!FREE( pbhOldBlock );
}
}
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
//
// Handle exception. Obviously, it's time to return failure.
// It's hardly possible to get here after succefully freeing
// the block, so it's likely that an app will either:
// - not check the return value and leak some memory.
// - check the return value and try again, probably
// only to fail forever.
//
// So, it is not advisable for callers to keep trying this
// call until it succeeds.
//
ASSERT(FALSE);
bSuccess = FALSE;
}
return( bSuccess );
} // TsCheckInOrFree