Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1243 lines
29 KiB

/*++
Copyright (C) 2000-2001 Microsoft Corporation
--*/
#include "precomp.h"
#include <wbemcomn.h>
#include "hiecache.h"
#include <creposit.h>
#include <malloc.h>
#include <corex.h>
extern bool g_bShuttingDown;
long CHierarchyCache::s_nCaches = 0;
long CClassRecord::s_nRecords = 0;
//
//
// CClassRecord::CClassRecord
//
////////////////////////////////////////////////////////////////////
CClassRecord::CClassRecord(LPCWSTR wszClassName, LPCWSTR wszHash)
: m_wszClassName(NULL), m_pClassDef(NULL), m_pParent(NULL),
m_eIsKeyed(e_KeynessUnknown), m_bTreeComplete(false),
m_bChildrenComplete(false),
m_lLastChildInvalidationIndex(-1), m_pMoreRecentlyUsed(NULL),
m_pLessRecentlyUsed(NULL), m_lRef(0), m_nStatus(0), m_bSystemClass(false)
{
size_t dwLen = wcslen(wszClassName)+1;
m_wszClassName = new WCHAR[dwLen];
if (m_wszClassName == NULL)
throw CX_MemoryException();
StringCchCopyW(m_wszClassName, dwLen, wszClassName);
StringCchCopyW(m_wszHash, MAX_HASH_LEN+1, wszHash);
m_dwLastUsed = GetTickCount();
s_nRecords++;
}
CClassRecord::~CClassRecord()
{
delete [] m_wszClassName;
if(m_pClassDef)
{
if(m_pClassDef->Release() != 0)
{
s_nRecords++;
s_nRecords--;
}
}
s_nRecords--;
}
HRESULT CClassRecord::EnsureChild(CClassRecord* pChild)
{
for(int i = 0; i < m_apChildren.GetSize(); i++)
{
if(m_apChildren[i] == pChild)
return WBEM_S_FALSE;
}
if(m_apChildren.Add(pChild) < 0)
return WBEM_E_OUT_OF_MEMORY;
return WBEM_S_NO_ERROR;
}
HRESULT CClassRecord::RemoveChild(CClassRecord* pChild)
{
for(int i = 0; i < m_apChildren.GetSize(); i++)
{
if(m_apChildren[i] == pChild)
{
m_apChildren.RemoveAt(i);
return WBEM_S_NO_ERROR;
}
}
return WBEM_E_NOT_FOUND;
}
//
//
// CHierarchyCache::CHierarchyCache
//
/////////////////////////////////////////////////////////////////////
CHierarchyCache::CHierarchyCache(CForestCache* pForest)
: m_pForest(pForest), m_lNextInvalidationIndex(0), m_lRef(0),
m_hresError(S_OK)
{
s_nCaches++;
}
CHierarchyCache::~CHierarchyCache()
{
Clear();
s_nCaches--;
}
void CHierarchyCache::Clear()
{
CInCritSec ics(m_pForest->GetLock());
TIterator it = m_map.begin();
while(it != m_map.end())
{
CClassRecord* pRecord = it->second;
m_pForest->RemoveRecord(pRecord);
it = m_map.erase(it);
pRecord->Release();
}
}
void CHierarchyCache::SetError(HRESULT hresError)
{
m_hresError = hresError;
}
HRESULT CHierarchyCache::GetError()
{
return m_hresError;
}
bool CHierarchyCache::MakeKey(LPCWSTR wszClassName, LPWSTR wszKey)
{
// wbem_wcsupr(wszKey, wszClassName);
return A51Hash(wszClassName, wszKey);
}
INTERNAL CClassRecord* CHierarchyCache::FindClass(LPCWSTR wszClassName)
{
CInCritSec ics(m_pForest->GetLock());
LPWSTR wszKey = (WCHAR*)TempAlloc((MAX_HASH_LEN+2) * sizeof(WCHAR));
if (wszKey == NULL)
return NULL;
CTempFreeMe tfm(wszKey, (MAX_HASH_LEN+2) * sizeof(WCHAR));
if (!MakeKey(wszClassName, wszKey))
return 0;
return FindClassByKey(wszKey);
}
INTERNAL CClassRecord* CHierarchyCache::FindClassByKey(LPCWSTR wszKey)
{
TIterator it = m_map.find(wszKey);
if(it == m_map.end())
return NULL;
return it->second;
}
INTERNAL CClassRecord* CHierarchyCache::EnsureClass(LPCWSTR wszClassName)
{
CInCritSec ics(m_pForest->GetLock());
LPWSTR wszKey = (WCHAR*)TempAlloc((MAX_HASH_LEN+2) * sizeof(WCHAR));
if (wszKey == NULL)
return NULL;
CTempFreeMe tfm(wszKey, (MAX_HASH_LEN+2) * sizeof(WCHAR));
if (!MakeKey(wszClassName, wszKey))
return 0;
TIterator it = m_map.find(wszKey);
if(it == m_map.end())
{
//
// Create a new record with the name
//
try
{
CClassRecord* pRecord = new CClassRecord(wszClassName, wszKey);
if(pRecord == NULL)
return NULL;
pRecord->AddRef(); // one for the map
m_map[pRecord->m_wszHash] = pRecord;
return pRecord;
}
catch (CX_MemoryException)
{
return NULL;
}
}
else
{
return it->second;
}
}
HRESULT CHierarchyCache::AssertClass(_IWmiObject* pClass, LPCWSTR wszClassName,
bool bClone, __int64 nTime, bool bSystemClass)
{
CInCritSec ics(m_pForest->GetLock());
HRESULT hres;
m_pForest->MarkAsserted(this, wszClassName);
//
// If no record is given, find one
//
CClassRecord* pRecord = NULL;
if(wszClassName == NULL)
{
VARIANT v;
VariantInit(&v);
CClearMe cm(&v);
hres = pClass->Get(L"__CLASS", 0, &v, NULL, NULL);
if(FAILED(hres) || V_VT(&v) != VT_BSTR)
return WBEM_E_INVALID_CLASS;
pRecord = EnsureClass(V_BSTR(&v));
}
else
pRecord = EnsureClass(wszClassName);
if(pRecord == NULL)
return WBEM_E_OUT_OF_MEMORY;
//
// Figure out the parent
//
VARIANT v;
VariantInit(&v);
hres = pClass->Get(L"__SUPERCLASS", 0, &v, NULL, NULL);
CClearMe cm(&v);
if(SUCCEEDED(hres))
{
if(V_VT(&v) == VT_BSTR)
pRecord->m_pParent = EnsureClass(V_BSTR(&v));
else
pRecord->m_pParent = EnsureClass(L"");
if(pRecord->m_pParent)
pRecord->m_pParent->EnsureChild(pRecord);
}
else
{
return hres;
}
//
// Check if the class is keyed
//
unsigned __int64 i64Flags = 0;
hres = pClass->QueryObjectFlags(0, WMIOBJECT_GETOBJECT_LOFLAG_KEYED,
&i64Flags);
if(FAILED(hres))
return hres;
if(i64Flags)
{
pRecord->m_eIsKeyed = e_Keyed;
}
else
{
pRecord->m_eIsKeyed = e_NotKeyed;
}
//
// Expell whatever definition is there from the cache
//
m_pForest->RemoveRecord(pRecord);
//
// Figure out how much space this object will take
//
DWORD dwSize;
hres = pClass->GetObjectMemory(NULL, 0, &dwSize);
if(hres != WBEM_E_BUFFER_TOO_SMALL)
{
if(SUCCEEDED(hres))
return WBEM_E_CRITICAL_ERROR;
else
return hres;
}
//
// Good. Make room and add to cache
//
if(m_pForest->MakeRoom(dwSize))
{
if(bClone)
{
IWbemClassObject* pObj = NULL;
hres = pClass->Clone(&pObj);
if(FAILED(hres))
return hres;
if(pObj)
{
pObj->QueryInterface(IID__IWmiObject,
(void**)&pRecord->m_pClassDef);
pObj->Release();
}
}
else
{ pRecord->m_pClassDef = pClass;
pClass->AddRef();
}
if(nTime)
{
pRecord->m_bRead = true;
pRecord->m_nClassDefCachedTime = nTime;
}
else
{
pRecord->m_bRead = false;
pRecord->m_nClassDefCachedTime = g_nCurrentTime++;
}
pRecord->m_dwClassDefSize = dwSize;
pRecord->m_bSystemClass = bSystemClass;
//
// It is most recently used, of course
//
m_pForest->Add(pRecord);
}
return WBEM_S_NO_ERROR;
}
HRESULT CHierarchyCache::InvalidateClass(LPCWSTR wszClassName)
{
CInCritSec ics(m_pForest->GetLock());
HRESULT hres;
//
// Find the record if not given
//
CClassRecord* pRecord = NULL;
pRecord = FindClass(wszClassName);
if(pRecord == NULL)
{
//
// The record is not there --- there is nothing to invalidate. This
// is based on the assumption that if a class record is in the
// cache, then so are all its parents, which is true at the moment
// because in order to construct a class we need to retrieve its
// parents first.
//
return WBEM_S_FALSE;
}
pRecord->AddRef();
CTemplateReleaseMe<CClassRecord> rm1(pRecord);
LONGLONG lThisInvalidationIndex = m_lNextInvalidationIndex++;
hres = InvalidateClassInternal(pRecord);
//
// Clear complete bits in all our parents, since this invalidation
// means that no current enumeration of children can be trusted. At the same
// time untie ourselves from the parent!
//
if(pRecord->m_pParent)
{
pRecord->m_pParent->m_bChildrenComplete = false;
pRecord->m_pParent->m_bTreeComplete = false;
pRecord->m_pParent->m_lLastChildInvalidationIndex =
lThisInvalidationIndex;
pRecord->m_pParent->RemoveChild(pRecord);
CClassRecord* pCurrent = pRecord->m_pParent->m_pParent;
while(pCurrent)
{
pCurrent->m_bTreeComplete = false;
pCurrent = pCurrent->m_pParent;
}
}
return S_OK;
}
HRESULT CHierarchyCache::InvalidateClassInternal(CClassRecord* pRecord)
{
//
// Untie from the usage chain
//
//
// Remove all its children from the cache
//
for(int i = 0; i < pRecord->m_apChildren.GetSize(); i++)
{
InvalidateClassInternal(pRecord->m_apChildren[i]);
}
pRecord->m_apChildren.RemoveAll();
//
// Count ourselves out of the total memory
//
m_pForest->RemoveRecord(pRecord);
//
// Remove ourselves from the cache
//
m_map.erase(pRecord->m_wszHash);
pRecord->Release();
return S_OK;
}
HRESULT CHierarchyCache::DoneWithChildren(LPCWSTR wszClassName, bool bRecursive,
LONGLONG lStartIndex, CClassRecord* pRecord)
{
CInCritSec ics(m_pForest->GetLock());
HRESULT hres;
//
// Find the record if not given
//
if(pRecord == NULL)
{
pRecord = FindClass(wszClassName);
if(pRecord == NULL)
{
// Big time invalidation must have occurred
return WBEM_S_FALSE;
}
}
return DoneWithChildrenByRecord(pRecord, bRecursive, lStartIndex);
}
HRESULT CHierarchyCache::DoneWithChildrenByHash(LPCWSTR wszHash,
bool bRecursive, LONGLONG lStartIndex)
{
CInCritSec ics(m_pForest->GetLock());
HRESULT hres;
//
// Find the record if not given
//
CClassRecord* pRecord = FindClassByKey(wszHash);
if(pRecord == NULL)
{
// Big time invalidation must have occurred
return WBEM_S_FALSE;
}
return DoneWithChildrenByRecord(pRecord, bRecursive, lStartIndex);
}
HRESULT CHierarchyCache::DoneWithChildrenByRecord(CClassRecord* pRecord,
bool bRecursive, LONGLONG lStartIndex)
{
//
// Check if any child invalidations occurred in this node since we started
//
if(lStartIndex < pRecord->m_lLastChildInvalidationIndex)
return WBEM_S_FALSE;
else
pRecord->m_bChildrenComplete = true;
if(bRecursive)
{
//
// We have completed a recursive enumeration --- descend the
// hierarchy and mark as complete all the children that have not been
// modified since the start
//
bool bAllValid = true;
for(int i = 0; i < pRecord->m_apChildren.GetSize(); i++)
{
CClassRecord* pChildRecord = pRecord->m_apChildren[i];
HRESULT hres = DoneWithChildren(pChildRecord->m_wszClassName, true,
lStartIndex, pChildRecord);
if(hres != S_OK)
bAllValid = false;
}
if(bAllValid)
{
//
// There were no invalidations anywhere in the tree, which makes
// this record tree-complete
//
pRecord->m_bTreeComplete = true;
return WBEM_S_NO_ERROR;
}
else
return S_FALSE;
}
else
return WBEM_S_NO_ERROR;
}
RELEASE_ME _IWmiObject* CHierarchyCache::GetClassDef(LPCWSTR wszClassName,
bool bClone, __int64* pnTime,
bool* pbRead)
{
CInCritSec ics(m_pForest->GetLock());
CClassRecord* pRecord = FindClass(wszClassName);
if(pRecord == NULL)
return NULL;
if(pnTime)
*pnTime = pRecord->m_nClassDefCachedTime;
if(pbRead)
*pbRead = pRecord->m_bRead;
return GetClassDefFromRecord(pRecord, bClone);
}
RELEASE_ME _IWmiObject* CHierarchyCache::GetClassDefByHash(LPCWSTR wszHash,
bool bClone, __int64* pnTime,
bool* pbRead, bool *pbSystemClass)
{
CInCritSec ics(m_pForest->GetLock());
CClassRecord* pRecord = FindClassByKey(wszHash);
if(pRecord == NULL)
return NULL;
if(pbRead)
*pbRead = pRecord->m_bRead;
if(pnTime)
*pnTime = pRecord->m_nClassDefCachedTime;
if (pbSystemClass)
*pbSystemClass = pRecord->m_bSystemClass;
return GetClassDefFromRecord(pRecord, bClone);
}
// assumes: in m_cs
RELEASE_ME _IWmiObject* CHierarchyCache::GetClassDefFromRecord(
CClassRecord* pRecord,
bool bClone)
{
//
// Accessing m_pClassDef, so we have to lock the forest
//
CInCritSec ics(m_pForest->GetLock());
if(pRecord->m_pClassDef)
{
m_pForest->MakeMostRecentlyUsed(pRecord);
if(bClone)
{
IWbemClassObject* pObj = NULL;
if(FAILED(pRecord->m_pClassDef->Clone(&pObj)))
return NULL;
else
{
_IWmiObject* pRes = NULL;
pObj->QueryInterface(IID__IWmiObject, (void**)&pRes);
pObj->Release();
return pRes;
}
}
else
{
pRecord->m_pClassDef->AddRef();
return pRecord->m_pClassDef;
}
}
else
return NULL;
}
HRESULT CHierarchyCache::EnumChildren(LPCWSTR wszClassName, bool bRecursive,
CWStringArray& awsChildren)
{
CInCritSec ics(m_pForest->GetLock());
//
// Get the record
//
CClassRecord* pRecord = FindClass(wszClassName);
if(pRecord == NULL)
return WBEM_S_FALSE;
//
// Check if it is complete for this type of enumeration
//
if(!pRecord->m_bChildrenComplete)
return WBEM_S_FALSE;
if(bRecursive && !pRecord->m_bTreeComplete)
return WBEM_S_FALSE;
return EnumChildrenInternal(pRecord, bRecursive, awsChildren);
}
HRESULT CHierarchyCache::EnumChildrenInternal(CClassRecord* pRecord,
bool bRecursive,
CWStringArray& awsChildren)
{
for(int i = 0; i < pRecord->m_apChildren.GetSize(); i++)
{
CClassRecord* pChildRecord = pRecord->m_apChildren[i];
if(awsChildren.Add(pChildRecord->m_wszClassName) < 0)
return WBEM_E_OUT_OF_MEMORY;
if(bRecursive)
{
HRESULT hres = EnumChildrenInternal(pChildRecord, bRecursive,
awsChildren);
if(FAILED(hres))
return hres;
}
}
return WBEM_S_NO_ERROR;
}
HRESULT CHierarchyCache::EnumChildKeysByKey(LPCWSTR wszClassKey,
CWStringArray& awsChildKeys)
{
CInCritSec ics(m_pForest->GetLock());
//
// Get the record
//
CClassRecord* pRecord = FindClassByKey(wszClassKey);
if(pRecord == NULL)
return WBEM_S_FALSE;
//
// Check if it is complete for this type of enumeration
//
if(!pRecord->m_bChildrenComplete)
return WBEM_S_FALSE;
for(int i = 0; i < pRecord->m_apChildren.GetSize(); i++)
{
CClassRecord* pChildRecord = pRecord->m_apChildren[i];
if(awsChildKeys.Add(pChildRecord->m_wszHash) < 0)
return WBEM_E_OUT_OF_MEMORY;
}
return WBEM_S_NO_ERROR;
}
HRESULT CHierarchyCache::GetKeyRoot(LPCWSTR wszClassName,
TEMPFREE_ME LPWSTR* pwszKeyRoot)
{
CInCritSec ics(m_pForest->GetLock());
CClassRecord* pRecord = FindClass(wszClassName);
if(pRecord == NULL)
return WBEM_E_NOT_FOUND;
return GetKeyRootByRecord(pRecord, pwszKeyRoot);
}
// assumes: in cs
HRESULT CHierarchyCache::GetKeyRootByRecord(CClassRecord* pRecord,
TEMPFREE_ME LPWSTR* pwszKeyRoot)
{
*pwszKeyRoot = NULL;
if(pRecord->m_eIsKeyed == e_NotKeyed)
return WBEM_E_CANNOT_BE_ABSTRACT;
//
// Go up until an unkeyed record is found. Keep the previous in pPrev
//
CClassRecord* pPrev = pRecord;
while(pRecord && pRecord->m_eIsKeyed == e_Keyed)
{
pPrev = pRecord;
pRecord = pRecord->m_pParent;
}
if(pRecord && pRecord->m_eIsKeyed == e_NotKeyed)
{
//
// Found unkeyed parent --- pPrev is the root
//
LPCWSTR wszKeyRoot = pPrev->m_wszClassName;
DWORD dwLen = wcslen(wszKeyRoot)+1;
*pwszKeyRoot = (WCHAR*)TempAlloc(dwLen * sizeof(WCHAR));
if (*pwszKeyRoot == NULL)
return WBEM_E_OUT_OF_MEMORY;
StringCchCopyW(*pwszKeyRoot, dwLen, wszKeyRoot);
return S_OK;
}
else
{
//
// No unkeyed parents --- since "" is known to be unkeyed, we had have
// hit a gap in the cache
//
return WBEM_E_NOT_FOUND;
}
}
HRESULT CHierarchyCache::GetKeyRootByKey(LPCWSTR wszKey,
TEMPFREE_ME LPWSTR* pwszKeyRoot)
{
CInCritSec ics(m_pForest->GetLock());
CClassRecord* pRecord = FindClassByKey(wszKey);
if(pRecord == NULL)
return WBEM_E_NOT_FOUND;
return GetKeyRootByRecord(pRecord, pwszKeyRoot);
}
DELETE_ME LPWSTR CHierarchyCache::GetParent(LPCWSTR wszClassName)
{
CInCritSec ics(m_pForest->GetLock());
CClassRecord* pRecord = FindClass(wszClassName);
if(pRecord == NULL)
return NULL;
if(pRecord->m_pParent)
{
LPCWSTR wszParent = pRecord->m_pParent->m_wszClassName;
size_t dwLen = wcslen(wszParent)+1;
LPWSTR wszCopy = new WCHAR[dwLen];
if (wszCopy == NULL)
return NULL;
StringCchCopyW(wszCopy, dwLen, wszParent);
return wszCopy;
}
else
return NULL;
}
//
//
// CForestCache
//
//////////////////////////////////////////////////////////////////////
HRESULT CForestCache::Initialize()
{
CInCritSec ics(&m_cs);
if (m_bInit)
return S_OK;
//
// Read the size limits from the registry
//
HKEY hKey;
long lRes = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\WBEM\\CIMOM",
0, KEY_READ | KEY_WRITE, &hKey);
if(lRes)
return lRes;
CRegCloseMe cm(hKey);
DWORD dwLen = sizeof(DWORD);
DWORD dwMaxSize;
lRes = RegQueryValueExW(hKey, L"Max Class Cache Size", NULL, NULL,
(LPBYTE)&dwMaxSize, &dwLen);
//
// If not there, set to default and write the default into the registry
//
if(lRes != ERROR_SUCCESS)
{
dwMaxSize = 5000000;
lRes = RegSetValueExW(hKey, L"Max Class Cache Size", 0, REG_DWORD,
(LPBYTE)&dwMaxSize, sizeof(DWORD));
}
//
// Read the maximum useful age of an item
//
dwLen = sizeof(DWORD);
DWORD dwMaxAge;
lRes = RegQueryValueExW(hKey, L"Max Class Cache Item Age (ms)", NULL, NULL,
(LPBYTE)&dwMaxAge, &dwLen);
//
// If not there, set to default and write the default into the registry
//
if(lRes != ERROR_SUCCESS)
{
dwMaxAge = 10000;
lRes = RegSetValueExW(hKey, L"Max Class Cache Item Age (ms)", 0,
REG_DWORD, (LPBYTE)&dwMaxAge, sizeof(DWORD));
}
//
// Apply
//
SetMaxMemory(dwMaxSize, dwMaxAge);
//
// Create a timer queue for flushing
//
//m_hTimerQueue = CreateTimerQueue();
//m_hCompletionEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
m_bInit = TRUE;
return WBEM_S_NO_ERROR;
}
bool CForestCache::MakeRoom(DWORD dwSize)
{
CInCritSec ics(&m_cs);
if (!m_bInit)
return false;
if(dwSize > m_dwMaxMemory)
return false; // no hope!
//
// Remove records until satisfied. Also, remove all records older than the
// maximum age
//
DWORD dwNow = GetTickCount();
while(m_pLeastRecentlyUsed &&
(m_dwTotalMemory + dwSize > m_dwMaxMemory ||
dwNow - m_pLeastRecentlyUsed->m_dwLastUsed > m_dwMaxAgeMs)
)
{
RemoveRecord(m_pLeastRecentlyUsed);
}
return true;
}
bool CForestCache::Flush()
{
CInCritSec ics(&m_cs);
if (!m_bInit)
return false;
while(m_pLeastRecentlyUsed)
{
RemoveRecord(m_pLeastRecentlyUsed);
}
return true;
}
//
//
// Test Only Function NOT IN REGULAR CODE
//
///////////////////////////////////////////////////////////////
#ifdef DBG
bool CForestCache::Test()
{
if(m_pMostRecentlyUsed == NULL)
{
if(m_pLeastRecentlyUsed)
DebugBreak();
return true;
}
if(m_pMostRecentlyUsed->m_pMoreRecentlyUsed)
DebugBreak();
CClassRecord* pOne = m_pMostRecentlyUsed;
CClassRecord* pTwo = m_pMostRecentlyUsed->m_pLessRecentlyUsed;
while(pOne && pOne != pTwo)
{
if(pOne->m_pLessRecentlyUsed && pOne->m_pLessRecentlyUsed->m_pMoreRecentlyUsed != pOne)
DebugBreak();
if(pOne->m_pClassDef == NULL)
DebugBreak();
if(pOne->m_pLessRecentlyUsed == NULL && pOne != m_pLeastRecentlyUsed)
DebugBreak();
pOne = pOne->m_pLessRecentlyUsed;
if(pTwo)
pTwo = pTwo->m_pLessRecentlyUsed;
if(pTwo)
pTwo = pTwo->m_pLessRecentlyUsed;
}
if(pOne)
DebugBreak();
return true;
}
#endif
void CForestCache::MakeMostRecentlyUsed(CClassRecord* pRecord)
{
CInCritSec ics(&m_cs);
//Test();
Untie(pRecord);
pRecord->m_pMoreRecentlyUsed = NULL;
pRecord->m_pLessRecentlyUsed = m_pMostRecentlyUsed;
if(m_pMostRecentlyUsed)
m_pMostRecentlyUsed->m_pMoreRecentlyUsed = pRecord;
m_pMostRecentlyUsed = pRecord;
if(m_pLeastRecentlyUsed == NULL)
m_pLeastRecentlyUsed = pRecord;
pRecord->m_dwLastUsed = GetTickCount();
pRecord->m_nStatus = 4;
//Test();
//
// Schedule a timer to clean up, if not already there
//
if(m_hCurrentTimer == NULL)
{
CreateTimerQueueTimer(&m_hCurrentTimer, NULL,
(WAITORTIMERCALLBACK)&staticTimerCallback, this, m_dwMaxAgeMs,
m_dwMaxAgeMs, WT_EXECUTEINTIMERTHREAD);
}
}
void CForestCache::staticTimerCallback(void* pParam, BOOLEAN)
{
#ifdef DBG
if (pParam != &g_Glob.m_ForestCache)
{
char pBuff[128];
StringCchPrintfA(pBuff,128, "_RTLP_GENERIC_TIMER::Context == %p != %p\n",pParam,&g_Glob.m_ForestCache);
OutputDebugStringA(pBuff);
DebugBreak();
}
#endif
g_Glob.m_ForestCache.TimerCallback();
}
void CForestCache::TimerCallback()
{
CInCritSec ics(&m_cs);
// g_Glob.GetForestCache()->m_hCurrentTimer can be NULL at this point
// if there is the CForestCache::Deinitialize function being executed
// but we will delete the timer in that function instead of deleteing it below
if (!m_bInit)
return;
//
// Clean up what's stale
//
MakeRoom(0);
//
// See if we have any more reasons to live
//
if(m_pMostRecentlyUsed == NULL)
{
HANDLE hCurrentTimer;
if (hCurrentTimer = InterlockedCompareExchangePointer(&m_hCurrentTimer,0,m_hCurrentTimer))
{
DeleteTimerQueueTimer( NULL, hCurrentTimer, NULL);
}
}
}
void CForestCache::Add(CClassRecord* pRecord)
{
CInCritSec ics(&m_cs);
if (!m_bInit)
return;
MakeMostRecentlyUsed(pRecord);
m_dwTotalMemory += pRecord->m_dwClassDefSize;
pRecord->m_nStatus = 3;
}
void CForestCache::RemoveRecord(CClassRecord* pRecord)
{
CInCritSec ics(&m_cs);
if (!m_bInit)
return;
if(pRecord->m_pClassDef == NULL)
return;
Untie(pRecord);
m_dwTotalMemory -= pRecord->m_dwClassDefSize;
pRecord->m_pClassDef->Release();
pRecord->m_pClassDef = NULL;
pRecord->m_nStatus = 2;
}
//
//
// helper function, always in m_cs
//
///////////////////////////////////////////////////////
void CForestCache::Untie(CClassRecord* pRecord)
{
CClassRecord* pPrev = pRecord->m_pLessRecentlyUsed;
CClassRecord* pNext = pRecord->m_pMoreRecentlyUsed;
if(pPrev)
pPrev->m_pMoreRecentlyUsed = pNext;
if(pNext)
pNext->m_pLessRecentlyUsed = pPrev;
if(m_pLeastRecentlyUsed == pRecord)
m_pLeastRecentlyUsed = m_pLeastRecentlyUsed->m_pMoreRecentlyUsed;
if(m_pMostRecentlyUsed == pRecord)
m_pMostRecentlyUsed = m_pMostRecentlyUsed->m_pLessRecentlyUsed;
pRecord->m_pMoreRecentlyUsed = pRecord->m_pLessRecentlyUsed = NULL;
}
void CForestCache::SetMaxMemory(DWORD dwMaxMemory, DWORD dwMaxAgeMs)
{
m_dwMaxMemory = dwMaxMemory;
m_dwMaxAgeMs = dwMaxAgeMs;
//
// Make room for 0 bytes --- has the effect of clearing all the records
// above the limit
//
MakeRoom(0);
}
CHierarchyCache* CForestCache::GetNamespaceCache(WString & wszNamespace)
{
CInCritSec ics(&m_cs);
if (!m_bInit)
return NULL;
//
// See if you can find one
//
TIterator it = m_map.find(wszNamespace);
if(it != m_map.end())
{
it->second->AddRef();
return it->second;
}
else
{
//
// Not there --- create one
//
CHierarchyCache* pCache = new CHierarchyCache(this);
if(pCache == NULL)
return NULL;
try
{
pCache->AddRef(); // this refcount is for the cache
m_map[wszNamespace] = pCache;
pCache->AddRef(); // this refcount is for the customers
}
catch (CX_MemoryException)
{
delete pCache; // despite the AddRef
pCache = NULL;
}
return pCache;
}
}
void CForestCache::ReleaseNamespaceCache(WString & wszNamespace,
CHierarchyCache* pCache)
{
CInCritSec ics(&m_cs);
//
// this is a cleanup function, we always want this to be called
//
//if (!m_bInit)
// return;
//
// Find it in the map
//
TIterator it = m_map.find(wszNamespace);
if (it != m_map.end() && (it->second == pCache))
{
//
// Last ref-count --- remove
//
if( 1 == pCache->Release())
{
m_map.erase(it);
pCache->Release(); // this is the last one
}
}
else
{
pCache->Release();
}
}
void CForestCache::BeginTransaction()
{
m_bAssertedInTransaction = false;
}
bool CForestCache::MarkAsserted(CHierarchyCache* pCache, LPCWSTR wszClassName)
{
m_bAssertedInTransaction = true;
return true;
}
void CForestCache::CommitTransaction()
{
m_bAssertedInTransaction = false;
}
void CForestCache::AbortTransaction()
{
if(m_bAssertedInTransaction)
Clear();
m_bAssertedInTransaction = false;
}
void CForestCache::Clear()
{
CInCritSec ics(&m_cs);
if (!m_bInit)
return;
Flush();
TIterator it = m_map.begin();
while(it != m_map.end())
{
it->second->Clear();
it++;
}
}
HRESULT
CForestCache::Deinitialize()
{
HANDLE hCurrentTimer;
if (hCurrentTimer = InterlockedCompareExchangePointer(&m_hCurrentTimer,0,m_hCurrentTimer))
{
DeleteTimerQueueTimer( NULL, hCurrentTimer, NULL);
}
CInCritSec ics(&m_cs);
if (!m_bInit)
return S_OK;
TIterator it = m_map.begin();
while(it != m_map.end())
{
it->second->Clear();
it->second->Release();
it->second = NULL;
it++;
};
m_map.erase(m_map.begin(),m_map.end());
m_bInit = FALSE;
return S_OK;
}
CForestCache::~CForestCache()
{
}