/**********************************************************************/ /** 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, "" "" "" "" "" "" "
Current memory cache size%I64d
Current memory cache limit%I64d
Number of items in memory cache%d
Peak memory cache size%I64d
Peak memory cache element count%d
", 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; } }