windows-nt/Source/XPSP1/NT/inetsrv/iis/svcs/infocomm/cache2/tlcach.cxx
2020-09-26 16:20:57 +08:00

759 lines
16 KiB
C++

/**********************************************************************/
/** Microsoft Windows NT **/
/** Copyright(c) Microsoft Corp., 1998 **/
/**********************************************************************/
/*
tlcach.cxx
This module implements the private interface to the two-level cache. This
cache is used to either cache the contents of a file or to copy the
file and cache the handle to it
FILE HISTORY:
BAlam 10-31-1998 Created
*/
#include "TsunamiP.Hxx"
#pragma hdrstop
// #define LOCAL_ALLOC 1
#define PRIVATE_HEAP 1
// #define VIRTUAL_ALLOC 1
// #define LOOKASIDE 1
#include "issched.hxx"
#include "tlcach.h"
//
// Globals
//
// Memory Cache size statistics
DWORDLONG g_cbMaxMemCacheSize;
DWORDLONG g_cbMemCacheSize;
DWORDLONG g_cbMaxMemCacheUsed;
DWORD g_cMemCacheElements;
DWORD g_cMaxMemCacheElementsUsed;
// Cache utility
CRITICAL_SECTION g_csMemCache;
DWORD g_dwMemCacheSizeCookie;
DWORD g_cmsecAdjustmentTime = DEFAULT_ADJUSTMENT_TIME;
CHAR g_achTempPath[ MAX_PATH + 1 ];
#if defined(LOCAL_ALLOC)
const char g_szTsunamiAllocator[] = "LocalAlloc";
#elif defined(PRIVATE_HEAP)
const char g_szTsunamiAllocator[] = "PrivateHeap";
HANDLE g_hMemCacheHeap = NULL;
#elif defined(VIRTUAL_ALLOC)
const char g_szTsunamiAllocator[] = "VirtualAlloc";
#elif defined(LOOKASIDE)
const char g_szTsunamiAllocator[] = "LookAside";
enum {
ENORMOUS = 300,
MANY = 200,
LOTS = 100,
SOME = 50,
FEW = 20,
MINIMAL = 4,
KB = 1024,
};
ALLOC_CACHE_CONFIGURATION g_aacc[] = {
{ 1, SOME, 128},
{ 1, SOME, 256},
{ 1, SOME, 512},
{ 1, LOTS, 768},
{ 1, LOTS, 1 * KB},
{ 1, ENORMOUS, 2 * KB},
{ 1, ENORMOUS, 3 * KB},
{ 1, ENORMOUS, 4 * KB},
{ 1, MANY, 5 * KB},
{ 1, MANY, 6 * KB},
{ 1, MANY, 7 * KB},
{ 1, MANY, 8 * KB},
{ 1, LOTS, 9 * KB},
{ 1, LOTS, 10 * KB},
{ 1, LOTS, 11 * KB},
{ 1, LOTS, 12 * KB},
{ 1, LOTS, 14 * KB},
{ 1, SOME, 16 * KB},
{ 1, SOME, 20 * KB},
{ 1, SOME, 24 * KB},
{ 1, SOME, 28 * KB},
{ 1, SOME, 32 * KB},
{ 1, FEW, 36 * KB},
{ 1, FEW, 40 * KB},
{ 1, FEW, 44 * KB},
{ 1, FEW, 48 * KB},
{ 1, FEW, 56 * KB},
{ 1, MINIMAL, 64 * KB},
{ 1, MINIMAL, 80 * KB},
{ 1, MINIMAL, 96 * KB},
{ 1, MINIMAL, 128 * KB},
{ 1, MINIMAL, 160 * KB},
{ 1, MINIMAL, 192 * KB},
{ 1, MINIMAL, 256 * KB},
};
CLookAside* g_laMemCache = NULL;
// paacc must be sorted in increasing sizes, it is sorted right now from
// where it is called
CLookAside::CLookAside(
ALLOC_CACHE_CONFIGURATION* paacc,
SIZE_T caacc)
: m_dwSignature(SIGNATURE),
m_apach(NULL),
m_aacc(NULL),
m_cach(0),
m_nMinSize(0),
m_nMaxSize(0)
{
ALLOC_CACHE_HANDLER** apach = new ALLOC_CACHE_HANDLER* [caacc];
if (apach == NULL)
return;
ALLOC_CACHE_CONFIGURATION* paacc2 = new ALLOC_CACHE_CONFIGURATION [caacc];
if (paacc2 == NULL)
return;
for (SIZE_T i = 0; i < caacc; ++i)
{
ALLOC_CACHE_CONFIGURATION acc = paacc[i];
acc.cbSize -= HEAP_PREFIX + HEAP_SUFFIX + ACACHE_OVERHEAD;
paacc2[i] = acc;
if (i == 0)
m_nMinSize = acc.cbSize;
else if (i == caacc-1)
m_nMaxSize = acc.cbSize;
char szName[40];
sprintf(szName, "TsLookAside-%d", acc.cbSize);
apach[i] = new ALLOC_CACHE_HANDLER(szName, &acc);
bool fInOrder = (i == 0 || paacc2[i].cbSize > paacc2[i-1].cbSize);
if (!fInOrder)
{
DBGPRINTF((DBG_CONTEXT, "CLookAside: config array out of order\n"));
}
if (apach[i] == NULL || !fInOrder)
{
for (SIZE_T j = i; j-- > 0; )
{
delete apach[j];
}
m_nMinSize = m_nMaxSize = 0;
return;
}
}
m_apach = apach;
m_aacc = paacc2;
m_cach = caacc;
}
CLookAside::~CLookAside()
{
for (SIZE_T j = m_cach; j-- > 0; )
{
delete m_apach[j];
}
delete [] m_apach;
delete [] m_aacc;
m_dwSignature = SIGNATURE_X;
}
int
CLookAside::_FindAllocator(
IN DWORD cbSize)
{
if (cbSize > m_nMaxSize)
return -1; // too big to cache
else if (cbSize <= m_nMinSize)
return 0;
int l = 0, h = m_cach-1;
do
{
DBG_ASSERT(m_aacc[l].cbSize < cbSize && cbSize <= m_aacc[h].cbSize);
unsigned m = (unsigned) (l + h) >> 1;
DBG_ASSERT(m > 0);
if (m_aacc[m-1].cbSize < cbSize && cbSize <= m_aacc[m].cbSize)
return m;
else if (m_aacc[m].cbSize < cbSize)
l = m+1;
else
h = m-1;
} while (l <= h);
DBG_ASSERT(FALSE);
return -1;
}
LPVOID
CLookAside::Alloc(
IN DWORD cbSize)
{
LPVOID pv = NULL;
int iAllocator = _FindAllocator(cbSize);
if (iAllocator < 0)
{
pv = VirtualAlloc(NULL, cbSize, MEM_COMMIT, PAGE_READWRITE);
}
else
{
DBG_ASSERT(iAllocator < m_cach && cbSize <= m_aacc[iAllocator].cbSize);
pv = m_apach[iAllocator]->Alloc();
}
return pv;
}
BOOL
CLookAside::Free(
IN LPVOID pv,
IN DWORD cbSize)
{
int iAllocator = _FindAllocator(cbSize);
if (iAllocator < 0)
{
VirtualFree(pv, 0, MEM_RELEASE);
}
else
{
DBG_ASSERT(iAllocator < m_cach && cbSize <= m_aacc[iAllocator].cbSize);
m_apach[iAllocator]->Free(pv);
}
return TRUE;
}
#endif // LOCAL_ALLOC
//
// Defines
//
#define MemCacheLock() ( EnterCriticalSection( &g_csMemCache ) )
#define MemCacheUnlock() ( LeaveCriticalSection( &g_csMemCache ) )
//
// Private declarations
//
VOID
WINAPI
I_MemoryCacheSizeAdjustor(
PVOID pContext
);
//
// Global functions
//
DWORD
InitializeTwoLevelCache(
IN DWORDLONG cbMemoryCacheSize
)
/*++
Routine Description:
Initialize memory cache
Arguments:
cbMemoryCacheSize - Size of memory cache (in bytes).
Return Value:
ERROR_SUCCESS if successful, else Win32 Error
--*/
{
DWORD dwError = ERROR_SUCCESS;
if (DisableTsunamiCaching)
return dwError;
INITIALIZE_CRITICAL_SECTION( &g_csMemCache );
if ( cbMemoryCacheSize == (DWORDLONG)-1 )
{
MEMORYSTATUSEX MemoryStatus;
MemoryStatus.dwLength = sizeof MemoryStatus;
//
// Get our own estimate of size of cache
//
GlobalMemoryStatusEx( &MemoryStatus );
g_cbMaxMemCacheSize = min( MemoryStatus.ullAvailPhys,
MemoryStatus.ullTotalVirtual ) / 2;
//
// Schedule a max cache size adjustor
//
g_dwMemCacheSizeCookie = ScheduleWorkItem( I_MemoryCacheSizeAdjustor,
NULL,
g_cmsecAdjustmentTime,
TRUE );
if ( !g_dwMemCacheSizeCookie )
{
dwError = GetLastError();
}
}
else
{
g_cbMaxMemCacheSize = cbMemoryCacheSize;
}
if ( dwError == ERROR_SUCCESS )
{
#if defined(LOCAL_ALLOC)
// no initialization needed
#elif defined(PRIVATE_HEAP)
g_hMemCacheHeap = HeapCreate( 0, 0, 0 );
if (g_hMemCacheHeap == NULL)
dwError = ERROR_NOT_ENOUGH_MEMORY;
#elif defined(VIRTUAL_ALLOC)
// no initialization needed
#elif defined(LOOKASIDE)
g_laMemCache = new CLookAside(g_aacc,
sizeof(g_aacc)/sizeof(g_aacc[0]));
if (g_laMemCache == NULL)
dwError = ERROR_NOT_ENOUGH_MEMORY;
#endif // LOCAL_ALLOC
}
if ( dwError != ERROR_SUCCESS )
{
TerminateTwoLevelCache();
}
return dwError;
}
DWORD
ReadFileIntoMemoryCache(
IN HANDLE hFile,
IN DWORD cbFile,
OUT DWORD * pcbRequired,
OUT VOID ** ppvBuffer
)
/*++
Routine Description:
Read contents of file into a buffer
Arguments:
hFile - Handle to valid file
cbFile - Size of file ( ==> size of buffer )
pcbRequired - Filled in with number of bytes required to be removed from
cache to fit element
ppvBuffer - Filled in with pointer to buffer with file contents. Set
to NULL on failure
Return Value:
ERROR_SUCCESS if successful, else Win32 Error
--*/
{
BOOL bRet;
VOID * pvBuffer = NULL;
DWORD cbRead;
OVERLAPPED Overlapped;
DWORD dwError = ERROR_SUCCESS;
DBG_ASSERT( hFile && ( hFile != INVALID_HANDLE_VALUE ) );
DBG_ASSERT( pcbRequired != NULL );
DBG_ASSERT( ppvBuffer != NULL );
*pcbRequired = 0;
//
// First check whether there will be room in cache for the blob
//
MemCacheLock();
if ( ( g_cbMemCacheSize + cbFile ) > g_cbMaxMemCacheSize )
{
//
// Not enough room for cache
//
MemCacheUnlock();
*pcbRequired = DIFF(( g_cbMemCacheSize + cbFile ) - g_cbMaxMemCacheSize);
dwError = ERROR_NOT_ENOUGH_MEMORY;
goto Finished;
}
g_cbMemCacheSize += cbFile;
g_cbMaxMemCacheUsed = max( g_cbMaxMemCacheUsed, g_cbMemCacheSize );
g_cMemCacheElements++;
g_cMaxMemCacheElementsUsed = max( g_cMaxMemCacheElementsUsed, g_cMemCacheElements );
MemCacheUnlock();
*pcbRequired = 0;
//
// Allocate blob for file
//
#if defined(LOCAL_ALLOC)
pvBuffer = LocalAlloc( LMEM_FIXED, cbFile );
#elif defined(PRIVATE_HEAP)
DBG_ASSERT(g_hMemCacheHeap != NULL);
pvBuffer = HeapAlloc( g_hMemCacheHeap, 0, cbFile );
#elif defined(VIRTUAL_ALLOC)
pvBuffer = VirtualAlloc(NULL, cbFile, MEM_COMMIT, PAGE_READWRITE);
#elif defined(LOOKASIDE)
pvBuffer = g_laMemCache->Alloc(cbFile);
#endif // LOCAL_ALLOC
if ( pvBuffer == NULL )
{
MemCacheLock();
g_cbMemCacheSize -= cbFile;
MemCacheUnlock();
dwError = ERROR_NOT_ENOUGH_MEMORY;
goto Finished;
}
//
// Read file into blob
//
Overlapped.Offset = 0;
Overlapped.OffsetHigh = 0;
Overlapped.hEvent = NULL;
bRet = ReadFile( hFile,
pvBuffer,
cbFile,
&cbRead,
&Overlapped );
if ( !bRet )
{
dwError = GetLastError();
if ( dwError != ERROR_IO_PENDING )
{
//
// Something bad happened
//
goto Finished;
}
else
{
//
// Reset the error lest we confuse ourselves later on cleanup
//
dwError = ERROR_SUCCESS;
//
// Wait for async read to complete
//
bRet = GetOverlappedResult( hFile,
&Overlapped,
&cbRead,
TRUE );
if ( !bRet )
{
//
// Something bad happened
//
dwError = GetLastError();
goto Finished;
}
}
}
//
// Ensure that we read the number of bytes we expected to
//
if ( cbRead != cbFile )
{
dwError = ERROR_INVALID_DATA;
}
Finished:
if ( dwError != ERROR_SUCCESS )
{
if ( pvBuffer != NULL )
{
#if defined(LOCAL_ALLOC)
LocalFree( pvBuffer );
#elif defined(PRIVATE_HEAP)
HeapFree( g_hMemCacheHeap, 0, pvBuffer );
#elif defined(VIRTUAL_ALLOC)
VirtualFree( pvBuffer, 0, MEM_RELEASE );
#elif defined(LOOKASIDE)
g_laMemCache->Free(pvBuffer, cbFile);
#endif // LOCAL_ALLOC
pvBuffer = NULL;
}
}
*ppvBuffer = pvBuffer;
return dwError;
}
DWORD
ReleaseFromMemoryCache(
IN VOID * pvBuffer,
IN DWORD cbBuffer
)
/*++
Routine Description:
Release file content blob from cache
Arguments:
pvBuffer - Buffer to release
cbBuffer - Size of buffer
Return Value:
ERROR_SUCCESS if successful, else Win32 Error
--*/
{
DBG_ASSERT( pvBuffer );
#if defined(LOCAL_ALLOC)
LocalFree( pvBuffer );
#elif defined(PRIVATE_HEAP)
DBG_ASSERT(g_hMemCacheHeap != NULL);
HeapFree( g_hMemCacheHeap, 0, pvBuffer );
#elif defined(VIRTUAL_ALLOC)
VirtualFree( pvBuffer, 0, MEM_RELEASE );
#elif defined(LOOKASIDE)
g_laMemCache->Free(pvBuffer, cbBuffer);
#endif // LOCAL_ALLOC
MemCacheLock();
g_cbMemCacheSize -= cbBuffer;
g_cMemCacheElements--;
MemCacheUnlock();
return ERROR_SUCCESS;
}
DWORD
TerminateTwoLevelCache(
VOID
)
/*++
Routine Description:
Terminate the memory cache
Arguments:
None
Return Value:
ERROR_SUCCESS if successful
ERROR_INVALID_DATA if still elements in cache
--*/
{
if (DisableTsunamiCaching)
return ERROR_SUCCESS;
#if defined(LOCAL_ALLOC)
// no cleanup
#elif defined(PRIVATE_HEAP)
if (g_hMemCacheHeap != NULL)
{
DBG_REQUIRE( HeapDestroy( g_hMemCacheHeap ) );
g_hMemCacheHeap = NULL;
}
#elif defined(VIRTUAL_ALLOC)
// no cleanup
#elif defined(LOOKASIDE)
delete g_laMemCache;
g_laMemCache = NULL;
#endif // LOCAL_ALLOC
if ( g_dwMemCacheSizeCookie != 0 )
{
RemoveWorkItem( g_dwMemCacheSizeCookie );
g_dwMemCacheSizeCookie = 0;
}
DeleteCriticalSection( &g_csMemCache );
return ( g_cbMemCacheSize ) ?
ERROR_INVALID_DATA : ERROR_SUCCESS;
}
VOID
WINAPI
I_MemoryCacheSizeAdjustor(
IN PVOID pContext
)
/*++
Routine Description:
Called to adjust the maximum size of the memory cache
Arguments:
pContext - Context (set to NULL)
Return value:
None
--*/
{
MEMORYSTATUSEX MemoryStatus;
MemoryStatus.dwLength = sizeof MemoryStatus;
GlobalMemoryStatusEx( &MemoryStatus );
MemCacheLock();
g_cbMaxMemCacheSize = min( MemoryStatus.ullAvailPhys + g_cbMemCacheSize,
MemoryStatus.ullTotalVirtual ) / 2;
MemCacheUnlock();
}
DWORD
DumpMemoryCacheToHtml(
IN CHAR * pszBuffer,
IN OUT DWORD * pcbBuffer
)
/*++
Routine Description:
Dump memory cache stats to buffer
Arguments:
pszBuffer - buffer to fill
pcbBuffer - size of buffer
Return value:
ERROR_SUCCESS if successful, else Win32 Error
--*/
{
*pcbBuffer = wsprintf( pszBuffer,
"<table>"
"<tr><td>Current memory cache size</td><td align=right>%I64d</td></tr>"
"<tr><td>Current memory cache limit</td><td align=right>%I64d</td></tr>"
"<tr><td>Number of items in memory cache</td><td align=right>%d</td></tr>"
"<tr><td>Peak memory cache size</td><td align=right>%I64d</td></tr>"
"<tr><td>Peak memory cache element count</td><td align=right>%d</td></tr>"
"</table>",
g_cbMemCacheSize,
g_cbMaxMemCacheSize,
g_cMemCacheElements,
g_cbMaxMemCacheUsed,
g_cMaxMemCacheElementsUsed
);
return TRUE;
}
VOID
QueryMemoryCacheStatistics(
IN INETA_CACHE_STATISTICS * pCacheCtrs,
IN BOOL fClearAll
)
/*++
Routine Description:
Query memory cache perfmon counters
Arguments:
pCacheCtrs - Relevant members of stat structure are filled in
fClearAll - Clear the counters
Return value:
ERROR_SUCCESS if successful, else Win32 Error
--*/
{
DBG_ASSERT( pCacheCtrs );
if ( fClearAll )
{
pCacheCtrs->CurrentFileCacheSize = 0;
pCacheCtrs->MaximumFileCacheSize = 0;
}
else
{
pCacheCtrs->CurrentFileCacheSize = g_cbMemCacheSize;
pCacheCtrs->MaximumFileCacheSize = g_cbMaxMemCacheUsed;
}
}