500 lines
13 KiB
C++
500 lines
13 KiB
C++
#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
|
||
|
||
|