|
|
/*****************************************************************************\
* MODULE: cachemgr.cxx * * The module contains routines to implement the caching algorithm for * the printers provider * * Purpose: * * Description: * * The Caching algorithm operates based on a state machine. There are five * states in the cache: * * CACHE_STATE_INIT * CACHE_STATE_ACCESSED_VALID, * CACHE_STATE_DATA_VALID, * CACHE_STATE_ACCESSED_VALID_AGAIN, * CACHE_STATE_NOT_ACCESSED_VALID_AGAIN * * CACHE_STATE_INIT is the initial state. Once the first cache hit * comes in, the cahce manager calles FetchData to fetch the data * and goes into CACHE_STATE_ACCESSED_VALID state. * * In CACHE_STATE_ACCESSED_VALID state, the cache manager waits * for the minimum cache timeout and go to CACHE_STATE_DATA_VALID * * In CACHE_STATE_DATA_VALID state, the cache manager waits for * another cache hit withing half of the last fetch time. * * If there is a hit during this waiting, the cache manager will go * for another fetch and go to CACHE_STATE_ACCESSED_VALID state. * * If there is no hit during the waiting period, the cache manager * waits for another timeout or another cache hit. * * If there is no access during the waiting, the cache manager * invalidates the cache. Otherwise, the cache manager go out and * do anther data fetch and then go to CACHE_STATE_ACCESSED_VALID. * * * * Copyright (C) 1998-1999 Microsoft Corporation * * History: * 10/16/98 weihaic Created * \*****************************************************************************/ #include "precomp.h"
#include "priv.h"
extern BOOL _ppinfo_net_get_info( IN PCINETMONPORT pIniPort, OUT PPRINTER_INFO_2 *ppInfo, OUT LPDWORD lpBufAllocated, IN ALLOCATORFN pAllocator);
extern BOOL ppjob_EnumForCache( IN PCINETMONPORT pIniPort, OUT LPPPJOB_ENUM *ppje);
// cdwMaxCacheValidTime is the maximum expire time for the current cache
// Idealy, we should put a large number to increase the effiency of the cache
// so we choose 30 seconds (30*1000) as the final number. For testing purpose,
// we put 15 seconds to increase the hit of the fetch data code
//
// weihaic 10/23/98
//
const DWORD cdwMaxCacheValidTime = 30*1000; // The cache content will expire after 30 seconds.
const DWORD cdwMinCacheValidTime = 2*1000; // The cache content will be valid for at least 2 seconds
CacheMgr::CacheMgr (): m_dwState (CACHE_STATE_INIT), m_pIniPort (NULL), m_pData (NULL), m_hDataReadyEvent (NULL), m_hHitEvent (NULL), m_hInvalidateCacheEvent (NULL), m_hThread (NULL), m_bCacheStopped (FALSE), m_bAccessed (FALSE), m_bInvalidateFlag (FALSE), m_dwThreadRefCount (0), m_bValid (FALSE) { if ((m_hDataReadyEvent = CreateEvent (NULL, TRUE, FALSE, NULL)) && (m_hHitEvent = CreateEvent (NULL, FALSE, FALSE, NULL)) && (m_hInvalidateCacheEvent = CreateEvent (NULL, FALSE, FALSE, NULL)) )
m_bValid = TRUE; }
CacheMgr::~CacheMgr () { }
VOID CacheMgr::AttachThreadHandle ( HANDLE hThread ) { CacheData.Lock();
m_hThread = hThread; m_dwThreadRefCount = 1;
DBGMSGT (DBG_CACHE_TRACE, ( TEXT ("AttachThreadHandle (hThread = %d)"), hThread));
CacheData.Unlock(); }
HANDLE CacheMgr::GetThreadHandle ( VOID ) { HANDLE hThread;
CacheData.Lock();
hThread = m_hThread;
if (m_hThread) { m_dwThreadRefCount++; }
DBGMSGT (DBG_CACHE_TRACE, ( TEXT ("GetThreadHandle (hThread = %d, Count = %d)"), hThread, m_dwThreadRefCount));
CacheData.Unlock();
return hThread; }
VOID CacheMgr::ReleaseThreadHandle ( VOID ) { HANDLE hThread = NULL;
CacheData.Lock();
if (m_hThread) { m_dwThreadRefCount--;
DBGMSGT (DBG_CACHE_TRACE, ( TEXT ("ReleaseThreadHandle (hThread = %d, Count = %d)"), hThread, m_dwThreadRefCount));
if (m_dwThreadRefCount == 0) { hThread = m_hThread; m_hThread = NULL; } }
CacheData.Unlock();
if (hThread) { CloseHandle (hThread); } }
VOID CacheMgr::Shutdown () { HANDLE hThread;
CacheRead.Lock (); // No more read is possible
m_bCacheStopped = TRUE; if (hThread = GetThreadHandle ()) {
SetEvent (m_hInvalidateCacheEvent);
// Wait for another thread to exit
WaitForSingleObject (hThread, INFINITE);
ReleaseThreadHandle (); }
if (m_hDataReadyEvent) { CloseHandle (m_hDataReadyEvent); }
if (m_hHitEvent) { CloseHandle (m_hHitEvent); }
if (m_hInvalidateCacheEvent) { CloseHandle (m_hInvalidateCacheEvent); }
m_bValid = FALSE;
CacheRead.Unlock ();
delete this; }
BOOL CacheMgr::SetupAsyncFetch ( PCINETMONPORT pIniPort) { BOOL bRet = FALSE;
PTHREADCONTEXT pThreadData = new THREADCONTEXT; HANDLE hThread = NULL;
if (pThreadData) { pThreadData->pIniPort = pIniPort; pThreadData->pCache = this;
pThreadData->pSidToken = new CSid;
if (pThreadData->pSidToken && pThreadData->pSidToken->bValid()) {
if (hThread = CreateThread (NULL, COMMITTED_STACK_SIZE, (LPTHREAD_START_ROUTINE)CacheMgr::WorkingThread, (PVOID) pThreadData, CREATE_SUSPENDED, NULL)) { bRet = TRUE; } }
if (bRet) { AttachThreadHandle (hThread);
//
// The thread is created in the suspended state
//
if (ResumeThread (hThread) == -1) { //
// Resume thread failed, so we need to clean up the thread handle
//
ReleaseThreadHandle (); bRet = FALSE; } }
if (!bRet) {
if (pThreadData->pSidToken) { delete pThreadData->pSidToken; }
delete (pThreadData); } }
return bRet; }
VOID CacheMgr::TransitState ( PCINETMONPORT pIniPort) { PVOID pData = NULL; DWORD dwFetchTime = 0; CACHESTATE dwState, dwOldState; BOOL bNewData; HANDLE hHandles[2];
hHandles [0] = m_hHitEvent; hHandles [1] = m_hInvalidateCacheEvent;
dwState = m_dwState;
do {
DBGMSGT (DBG_CACHE_TRACE, ( TEXT ("TransitState: current state %d"), m_dwState));
dwState = m_dwState; bNewData = FALSE; m_bAccessed = FALSE;
switch (dwState) { case CACHE_STATE_INIT:
// Clean the data ready event
ResetEvent (m_hDataReadyEvent);
if (GetFetchTime (pIniPort, &dwFetchTime, &pData)) { bNewData = TRUE; dwState = CACHE_STATE_ACCESSED_VALID; } else { //Invalid Cache Content
DBGMSGT (DBG_CACHE_ERROR, ( TEXT ("TransitState: FatalError %d"), GetLastError));
// Invalidate the cache content, so that when the next get comes, it will
// get the NULL pointer.
pData = NULL; bNewData = TRUE; dwState = CACHE_STATE_ACCESSED_VALID; }
break;
case CACHE_STATE_ACCESSED_VALID:
WaitForSingleObject (m_hInvalidateCacheEvent, cdwMinCacheValidTime); dwState = CACHE_STATE_DATA_VALID;
break;
case CACHE_STATE_DATA_VALID:
WaitForSingleObject (m_hInvalidateCacheEvent, dwFetchTime / 2);
if (m_bAccessed) { // The cache has been accessed during the waiting time
dwState = CACHE_STATE_ACCESSED_VALID_AGAIN; } else { // The cache has not been accessed during the waiting time
dwState = CACHE_STATE_NOT_ACCESSED_VALID_AGAIN; } break;
case CACHE_STATE_ACCESSED_VALID_AGAIN: if (GetFetchTime (pIniPort, &dwFetchTime, &pData)) { bNewData = TRUE; dwState = CACHE_STATE_ACCESSED_VALID; } else { //Invalid Cache Content
DBGMSGT (DBG_CACHE_ERROR, ( TEXT ("TransitState: FatalError %d"), GetLastError));
// Invalidate the cache content, so that when the next access to cache comes,
// it will get a NULL pointer.
pData = NULL; bNewData = TRUE; dwState = CACHE_STATE_ACCESSED_VALID; }
break;
case CACHE_STATE_NOT_ACCESSED_VALID_AGAIN:
// This has to be a long wait so that the cache will be valid for an access long after
// the last fetch happeen
ResetEvent (m_hHitEvent);
switch (WaitForMultipleObjects (2, hHandles, FALSE, cdwMaxCacheValidTime )) { case WAIT_TIMEOUT: dwState = CACHE_STATE_INIT; break; case WAIT_OBJECT_0: //Accessed
if (GetFetchTime (pIniPort, &dwFetchTime, &pData)) { bNewData = TRUE; dwState = CACHE_STATE_ACCESSED_VALID; } else { //Invalid Cache Content
DBGMSGT (DBG_CACHE_ERROR, ( TEXT ("TransitState: FatalError %d"), GetLastError));
// Invalidate the cache content, so that when the next access to cache comes,
// it will get a NULL pointer.
pData = NULL; bNewData = TRUE; dwState = CACHE_STATE_ACCESSED_VALID; } break;
case WAIT_OBJECT_0 + 1:
dwState = CACHE_STATE_INIT; break;
default: // ERROR
DBGMSGT (DBG_CACHE_ERROR, ( TEXT ("TransitState: WaitForSingleObject FatalError %d"), GetLastError));
// Invalidate the cache
dwState = CACHE_STATE_INIT; break;
} break;
default: DBGMSGT (DBG_CACHE_ERROR, (TEXT ("AsyncFech: wrong state %d"), m_dwState)); // Invalidate the cache
dwState = CACHE_STATE_INIT; }
if (m_bCacheStopped) { // Cache is being stopped. Cleanup everything this thread generates
if (bNewData) { FreeBuffer (pIniPort, pData); } // Since the caching thread is going to abort, so we need to raise
// this flag so that the waiting thread can go on.
SetEvent (m_hDataReadyEvent); break; }
dwOldState = m_dwState; SetState (pIniPort, dwState, bNewData, pData);
if (dwOldState == CACHE_STATE_INIT) { SetEvent (m_hDataReadyEvent); }
if (m_bInvalidateFlag) { // Another thread called invalidate thread and hope get rid of the thread
m_dwState = CACHE_STATE_INIT; }
} while ( m_dwState != CACHE_STATE_INIT );
// Terminate the async fetch thread
ReleaseThreadHandle ();
DBGMSGT (DBG_CACHE_TRACE, (TEXT ("CacheMgr::TransitState: Async thread quit")));
return;
}
VOID CacheMgr::SetState ( PCINETMONPORT pIniPort, CACHESTATE dwState, BOOL bNewData, PVOID pNewData) { PVOID pOldData = NULL;
m_bAccessed = 0; if (bNewData) { CacheData.Lock (); pOldData = m_pData; m_pData = pNewData;
CacheData.Unlock (); }
// This line must be here, since otherwise, the state is updated to the new one
// but the data are not
//
m_dwState = dwState;
if (pOldData) { FreeBuffer (pIniPort, pOldData); }
}
VOID CacheMgr::WorkingThread ( PTHREADCONTEXT pThreadData) { CacheMgr *pThis = pThreadData->pCache; PCINETMONPORT pIniPort = pThreadData->pIniPort;
pThreadData->pSidToken->SetCurrentSid (); delete pThreadData->pSidToken; pThreadData->pSidToken = NULL;
delete pThreadData;
pThis->TransitState (pIniPort); }
BOOL CacheMgr::GetFetchTime ( PCINETMONPORT pIniPort, LPDWORD pdwTime, PVOID *ppData) { BOOL bRet = FALSE; DWORD dwT0, dwT1;
dwT0 = GetTickCount(); if (FetchData (pIniPort, ppData)) { bRet = TRUE; } dwT1 = GetTickCount(); *pdwTime = GetTimeDiff (dwT0, dwT1);
DBGMSGT (DBG_CACHE_TRACE, (TEXT ("GetFetchTime: returns %d, timediff=%d ms"), bRet, GetTimeDiff (dwT0, dwT1)));
return bRet; }
PVOID CacheMgr::BeginReadCache ( PCINETMONPORT pIniPort) { HANDLE hThread = NULL;
if (m_bValid) { CacheRead.Lock ();
CUserData CurUser;
while (TRUE) { if (m_dwState == CACHE_STATE_INIT) {
m_CurUser = CurUser;
hThread = GetThreadHandle ();
if (!hThread) { // If there is no thread running, we need to reset the dataready event
ResetEvent (m_hDataReadyEvent); }
if (hThread // There is already a thread running
|| SetupAsyncFetch (pIniPort)) {
// We must leave the critical section since it might take forever to
// get the information at the first time
CacheRead.Unlock ();
WaitForSingleObject (m_hDataReadyEvent, INFINITE);
CacheRead.Lock ();
}
if (hThread) { ReleaseThreadHandle (); }
break; } else {
if (m_CurUser == CurUser) { if (m_dwState == CACHE_STATE_NOT_ACCESSED_VALID_AGAIN) { SetEvent (m_hHitEvent); } break; } else { // We must call the internal version of InvalidateCache since
// we have to leave the critical section when waiting for the termination
// of the working thread.
//
_InvalidateCache (); // Now, the state becomes CACHE_STATE_INIT
} } }
m_bAccessed = TRUE;
BOOL bRet = CacheData.Lock ();
DBGMSGT (DBG_CACHE_TRACE, (TEXT ("CacheData.Lock() = %d"), bRet));
return m_pData;
} else return NULL; }
VOID CacheMgr::EndReadCache (VOID) {
DBGMSGT (DBG_CACHE_TRACE, (TEXT ("CacheMgr::EndReadCache: Entered")));
if (m_bValid) {
BOOL bRet = CacheData.Unlock ();
DBGMSGT (DBG_CACHE_TRACE, (TEXT ("CacheData.Unlock() = %d"), bRet));
CacheRead.Unlock (); } }
VOID CacheMgr::_InvalidateCache () {
HANDLE hThread = NULL;
if (!m_bInvalidateFlag) {
m_bInvalidateFlag = TRUE;
SetEvent (m_hInvalidateCacheEvent); }
if (hThread = GetThreadHandle ()) {
CacheRead.Unlock (); // We must leave the critical section since we don't know how long it will take for thread to exit
// Wait for another thread to exit
WaitForSingleObject (hThread, INFINITE);
CacheRead.Lock ();
ReleaseThreadHandle (); }
// Clean up the event
ResetEvent (m_hInvalidateCacheEvent); m_bInvalidateFlag = FALSE;
DBGMSGT (DBG_CACHE_TRACE, ( TEXT ("CacheMgr::InvalidateCache dwState=%d"), m_dwState));
}
VOID CacheMgr::InvalidateCache () { CacheRead.Lock ();
_InvalidateCache ();
CacheRead.Unlock (); }
VOID CacheMgr::InvalidateCacheForUser( CLogonUserData *pUser ) /*++
Routine Description: This routine checks to see whether the given user is currently controlling the cache thread. If they are, the thread is terminated.
Arguments: pUser - A pointer to the user.
Return Value: None.
--*/ {
CacheRead.Lock();
if (m_CurUser == *(CUserData *)pUser) // Comparison is valid after caste
_InvalidateCache ();
CacheRead.Unlock(); }
inline DWORD CacheMgr::GetTimeDiff ( DWORD t0, DWORD t1) { if (t1 > t0) { return t1 - t0; } else { return DWORD(-1) - t0 + t1; } }
LPVOID CacheMgr::Allocator( DWORD cb) { return new CHAR[cb]; }
GetPrinterCache::GetPrinterCache( PCINETMONPORT pIniPort): m_pIniPort (pIniPort) {
}
GetPrinterCache::~GetPrinterCache ( VOID) { if (m_pData) { FreeBuffer (m_pIniPort, m_pData); } }
BOOL GetPrinterCache::FetchData ( PCINETMONPORT pIniPort, PVOID *ppData) { BOOL bRet = FALSE; PGETPRINTER_CACHEDATA pCacheData = NULL;
DBGMSGT (DBG_CACHE_TRACE, (TEXT ("GetPrinterCache::FetchData begins"))); #ifdef DEBUG
if (0) { // Simulate the hanging of the current thread
// For debugging purpose.
MessageBox (NULL, TEXT ("GetPrinterCache::FetchData called. Press OK to continue."), TEXT ("ALERT"), MB_OK); } #endif
if (pCacheData = new GETPRINTER_CACHEDATA) {
semEnterCrit ();
pCacheData->bRet = _ppinfo_net_get_info(pIniPort, & (pCacheData->pInfo) , & (pCacheData->cbSize), Allocator); semLeaveCrit ();
pCacheData->dwLastError = GetLastError ();
DBGMSGT (DBG_CACHE_TRACE, (TEXT ("GetPrinterCache::FetchData bRet=%d, err=%d\n"), pCacheData->bRet, pCacheData->dwLastError ));
//
// We must return TRUE, otherwise PPGetPrinter won't get the correct last error
//
bRet = TRUE; //pCacheData->cbSize != 0;
}
if (!bRet) { DBGMSGT (DBG_CACHE_TRACE, (TEXT ("GetPrinterCache::FetchData failed, call FreeBuffer (%x, %x)"), pIniPort, pCacheData)); FreeBuffer (pIniPort, pCacheData); } else { *ppData = pCacheData; }
return bRet; }
BOOL GetPrinterCache::FreeBuffer ( PCINETMONPORT pIniPort, PVOID pData) { PGETPRINTER_CACHEDATA pCacheData = (PGETPRINTER_CACHEDATA) pData;
DBGMSGT (DBG_CACHE_TRACE, (TEXT ("FreeBuffer(%x, %x) called"), pIniPort, pCacheData));
if (pCacheData) { if (pCacheData->pInfo) { delete [] (PCHAR)(pCacheData->pInfo); } delete pCacheData; }
return TRUE; }
BOOL GetPrinterCache::BeginReadCache ( PPRINTER_INFO_2 *ppInfo) { BOOL bRet = FALSE;
if (m_bValid) {
DBGMSGT (DBG_CACHE_TRACE, (TEXT ("GetPrinterCache::BeginReadCache: Entered")));
PGETPRINTER_CACHEDATA pData = (PGETPRINTER_CACHEDATA) CacheMgr::BeginReadCache (m_pIniPort);
if (pData) { if (pData->bRet) { *ppInfo = pData->pInfo; bRet = TRUE; } else { SetLastError (pData->dwLastError); }
}
if (!bRet && GetLastError () == ERROR_SUCCESS) {
SetLastError (ERROR_CAN_NOT_COMPLETE);
}
DBGMSGT (DBG_CACHE_TRACE, (TEXT ("GetPrinterCache::BeginReadCache: pData = %x, return = %d, lasterror = %d"), pData, bRet, GetLastError ())); } else { SetLastError (ERROR_CAN_NOT_COMPLETE); }
return bRet;
}
EnumJobsCache::EnumJobsCache( PCINETMONPORT pIniPort): m_pIniPort (pIniPort) { }
EnumJobsCache::~EnumJobsCache ( VOID) { if (m_pData) { FreeBuffer (m_pIniPort, m_pData); } }
BOOL EnumJobsCache::FetchData ( PCINETMONPORT pIniPort, PVOID *ppData) { BOOL bRet = FALSE; HANDLE hPort = (HANDLE)pIniPort; PENUMJOBS_CACHEDATA pCacheData = NULL;
DBGMSGT (DBG_CACHE_TRACE, (TEXT ("EnumJobsCache::FetchData begins")));
if (pCacheData = new ENUMJOBS_CACHEDATA) {
pCacheData->pje = NULL;
semEnterCrit (); pCacheData->bRet = ppjob_EnumForCache(pIniPort, & (pCacheData->pje)); pCacheData->dwLastError = GetLastError ();
DBGMSGT (DBG_CACHE_TRACE, (TEXT ("EnumJobsCache::FetchData bRet=%d, err=%d\n"), pCacheData->bRet, pCacheData->dwLastError ));
if (pCacheData->bRet) {
if (pCacheData->pje) { pCacheData->cbSize = pCacheData->pje->cbSize; } else { pCacheData->cbSize = 0; } }
semLeaveCrit ();
bRet = TRUE; }
if (bRet) { *ppData = pCacheData; }
return bRet; }
BOOL EnumJobsCache::FreeBuffer ( PCINETMONPORT pIniPort, PVOID pData) { PENUMJOBS_CACHEDATA pCacheData = (PENUMJOBS_CACHEDATA) pData;
if (pCacheData) { if (pCacheData->pje) {
// memFree has access to the global link list, so it is neccesary
// to run it under critical secion.
//
semEnterCrit (); memFree(pCacheData->pje, memGetSize(pCacheData->pje)); semLeaveCrit (); } delete pCacheData; }
return TRUE; }
BOOL EnumJobsCache::BeginReadCache ( LPPPJOB_ENUM *ppje) { BOOL bRet = FALSE;
if (m_bValid) {
DBGMSGT (DBG_CACHE_TRACE, (TEXT ("EnumJobsCache::BeginReadCache: Entered")));
PENUMJOBS_CACHEDATA pData = (PENUMJOBS_CACHEDATA) CacheMgr::BeginReadCache (m_pIniPort);
if (pData) { if (pData->bRet) { *ppje = pData->pje; bRet = TRUE; } else { SetLastError (pData->dwLastError); } }
if (!bRet && GetLastError () == ERROR_SUCCESS) {
SetLastError (ERROR_CAN_NOT_COMPLETE);
}
DBGMSGT (DBG_CACHE_TRACE, (TEXT ("EnumJobsCache::BeginReadCache: pData = %x, return = %d, lasterror = %d"), pData, bRet, GetLastError ())); } else { SetLastError (ERROR_CAN_NOT_COMPLETE); }
return bRet;
}
VOID EnumJobsCache::EndReadCache ( VOID) { DBGMSGT (DBG_CACHE_TRACE, (TEXT ("EnumJobsCache::EndReadCache: Entered")));
CacheMgr::EndReadCache (); }
/*********************************************************************************
** End of File (cachemgr.cxx) *********************************************************************************/
|