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
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()
|
|
{
|
|
}
|