Source code of Windows XP (NT5)
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.

1084 lines
24 KiB

  1. /*++
  2. Copyright (C) 2000-2001 Microsoft Corporation
  3. --*/
  4. #include <wbemcomn.h>
  5. #include "hiecache.h"
  6. #include <malloc.h>
  7. #include <corex.h>
  8. int g_nRecords = 0;
  9. int g_nCaches = 0;
  10. CClassRecord::CClassRecord(LPCWSTR wszClassName, LPCWSTR wszHash)
  11. : m_wszClassName(NULL), m_pClassDef(NULL), m_pParent(NULL),
  12. m_bIsKeyed(false), m_bTreeComplete(false), m_bChildrenComplete(false),
  13. m_lLastChildInvalidationIndex(-1), m_pMoreRecentlyUsed(NULL),
  14. m_pLessRecentlyUsed(NULL), m_lRef(0), m_nStatus(0)
  15. {
  16. m_wszClassName = new WCHAR[wcslen(wszClassName)+1];
  17. if (m_wszClassName == NULL)
  18. throw CX_MemoryException();
  19. wcscpy(m_wszClassName, wszClassName);
  20. wcscpy(m_wszHash, wszHash);
  21. m_dwLastUsed = GetTickCount();
  22. g_nRecords++;
  23. }
  24. CClassRecord::~CClassRecord()
  25. {
  26. delete [] m_wszClassName;
  27. if(m_pClassDef)
  28. {
  29. if(m_pClassDef->Release() != 0)
  30. {
  31. g_nRecords++;
  32. g_nRecords--;
  33. }
  34. }
  35. g_nRecords--;
  36. }
  37. HRESULT CClassRecord::EnsureChild(CClassRecord* pChild)
  38. {
  39. A51TRACE(("Make %S child of %S\n", pChild->m_wszClassName,
  40. m_wszClassName));
  41. for(int i = 0; i < m_apChildren.GetSize(); i++)
  42. {
  43. if(m_apChildren[i] == pChild)
  44. return WBEM_S_FALSE;
  45. }
  46. if(m_apChildren.Add(pChild) < 0)
  47. return WBEM_E_OUT_OF_MEMORY;
  48. return WBEM_S_NO_ERROR;
  49. }
  50. HRESULT CClassRecord::RemoveChild(CClassRecord* pChild)
  51. {
  52. A51TRACE(("Make %S NOT child of %S\n",
  53. pChild->m_wszClassName, m_wszClassName));
  54. for(int i = 0; i < m_apChildren.GetSize(); i++)
  55. {
  56. if(m_apChildren[i] == pChild)
  57. {
  58. m_apChildren.RemoveAt(i);
  59. return WBEM_S_NO_ERROR;
  60. }
  61. }
  62. return WBEM_E_NOT_FOUND;
  63. }
  64. CHierarchyCache::CHierarchyCache(CForestCache* pForest)
  65. : m_pForest(pForest), m_lNextInvalidationIndex(0), m_lRef(0),
  66. m_hresError(S_OK)
  67. {
  68. g_nCaches++;
  69. }
  70. CHierarchyCache::~CHierarchyCache()
  71. {
  72. TIterator it = m_map.begin();
  73. while(it != m_map.end())
  74. {
  75. CClassRecord* pRecord = it->second;
  76. m_pForest->RemoveRecord(pRecord);
  77. it = m_map.erase(it);
  78. pRecord->Release();
  79. }
  80. g_nCaches--;
  81. }
  82. void CHierarchyCache::SetError(HRESULT hresError)
  83. {
  84. m_hresError = hresError;
  85. }
  86. HRESULT CHierarchyCache::GetError()
  87. {
  88. return m_hresError;
  89. }
  90. void CHierarchyCache::MakeKey(LPCWSTR wszClassName, LPWSTR wszKey)
  91. {
  92. // wbem_wcsupr(wszKey, wszClassName);
  93. A51Hash(wszClassName, wszKey);
  94. }
  95. INTERNAL CClassRecord* CHierarchyCache::FindClass(LPCWSTR wszClassName)
  96. {
  97. CInCritSec ics(&m_cs);
  98. LPWSTR wszKey = (WCHAR*)_alloca(MAX_HASH_LEN*2+2);
  99. MakeKey(wszClassName, wszKey);
  100. return FindClassByKey(wszKey);
  101. }
  102. INTERNAL CClassRecord* CHierarchyCache::FindClassByKey(LPCWSTR wszKey)
  103. {
  104. TIterator it = m_map.find(wszKey);
  105. if(it == m_map.end())
  106. return NULL;
  107. return it->second;
  108. }
  109. INTERNAL CClassRecord* CHierarchyCache::EnsureClass(LPCWSTR wszClassName)
  110. {
  111. CInCritSec ics(&m_cs);
  112. LPWSTR wszKey = (WCHAR*)_alloca(MAX_HASH_LEN*2+2);
  113. MakeKey(wszClassName, wszKey);
  114. TIterator it = m_map.find(wszKey);
  115. if(it == m_map.end())
  116. {
  117. //
  118. // Create a new record with the name
  119. //
  120. try
  121. {
  122. CClassRecord* pRecord = new CClassRecord(wszClassName, wszKey);
  123. if(pRecord == NULL)
  124. return NULL;
  125. pRecord->AddRef();
  126. m_map[pRecord->m_wszHash] = pRecord;
  127. return pRecord;
  128. }
  129. catch (CX_MemoryException)
  130. {
  131. return NULL;
  132. }
  133. }
  134. else
  135. {
  136. return it->second;
  137. }
  138. }
  139. HRESULT CHierarchyCache::AssertClass(_IWmiObject* pClass, LPCWSTR wszClassName,
  140. bool bClone, __int64 nTime)
  141. {
  142. CInCritSec ics(&m_cs);
  143. HRESULT hres;
  144. //
  145. // If no record is given, find one
  146. //
  147. CClassRecord* pRecord = NULL;
  148. if(wszClassName == NULL)
  149. {
  150. VARIANT v;
  151. VariantInit(&v);
  152. CClearMe cm(&v);
  153. hres = pClass->Get(L"__CLASS", 0, &v, NULL, NULL);
  154. if(FAILED(hres) || V_VT(&v) != VT_BSTR)
  155. return WBEM_E_INVALID_CLASS;
  156. pRecord = EnsureClass(V_BSTR(&v));
  157. }
  158. else
  159. pRecord = EnsureClass(wszClassName);
  160. if(pRecord == NULL)
  161. return WBEM_E_OUT_OF_MEMORY;
  162. //
  163. // Figure out the parent
  164. //
  165. A51TRACE(("%p: Asserting %S on %I64d with %I64d\n",
  166. this, pRecord->m_wszClassName, g_nCurrentTime, nTime));
  167. VARIANT v;
  168. VariantInit(&v);
  169. hres = pClass->Get(L"__SUPERCLASS", 0, &v, NULL, NULL);
  170. CClearMe cm(&v);
  171. if(SUCCEEDED(hres))
  172. {
  173. if(V_VT(&v) == VT_BSTR)
  174. pRecord->m_pParent = EnsureClass(V_BSTR(&v));
  175. else
  176. pRecord->m_pParent = EnsureClass(L"");
  177. if(pRecord->m_pParent)
  178. pRecord->m_pParent->EnsureChild(pRecord);
  179. }
  180. else
  181. {
  182. return hres;
  183. }
  184. //
  185. // Check if the class is keyed
  186. //
  187. unsigned __int64 i64Flags = 0;
  188. hres = pClass->QueryObjectFlags(0, WMIOBJECT_GETOBJECT_LOFLAG_KEYED,
  189. &i64Flags);
  190. if(FAILED(hres))
  191. return hres;
  192. if(i64Flags)
  193. {
  194. pRecord->m_bIsKeyed = true;
  195. }
  196. else
  197. {
  198. pRecord->m_bIsKeyed = false;
  199. }
  200. //
  201. // Expell whatever definition is there from the cache
  202. //
  203. m_pForest->RemoveRecord(pRecord);
  204. //
  205. // Figure out how much space this object will take
  206. //
  207. DWORD dwSize;
  208. hres = pClass->GetObjectMemory(NULL, 0, &dwSize);
  209. if(hres != WBEM_E_BUFFER_TOO_SMALL)
  210. {
  211. if(SUCCEEDED(hres))
  212. return WBEM_E_CRITICAL_ERROR;
  213. else
  214. return hres;
  215. }
  216. //
  217. // Good. Make room and add to cache
  218. //
  219. if(m_pForest->MakeRoom(dwSize))
  220. {
  221. if(bClone)
  222. {
  223. IWbemClassObject* pObj = NULL;
  224. hres = pClass->Clone(&pObj);
  225. if(FAILED(hres))
  226. return hres;
  227. if(pObj)
  228. {
  229. pObj->QueryInterface(IID__IWmiObject,
  230. (void**)&pRecord->m_pClassDef);
  231. pObj->Release();
  232. }
  233. }
  234. else
  235. { pRecord->m_pClassDef = pClass;
  236. pClass->AddRef();
  237. }
  238. if(nTime)
  239. {
  240. pRecord->m_bRead = true;
  241. pRecord->m_nClassDefCachedTime = nTime;
  242. }
  243. else
  244. {
  245. pRecord->m_bRead = false;
  246. pRecord->m_nClassDefCachedTime = g_nCurrentTime++;
  247. }
  248. pRecord->m_dwClassDefSize = dwSize;
  249. //
  250. // It is most recently used, of course
  251. //
  252. m_pForest->Add(pRecord);
  253. }
  254. return WBEM_S_NO_ERROR;
  255. }
  256. HRESULT CHierarchyCache::InvalidateClass(LPCWSTR wszClassName)
  257. {
  258. CInCritSec ics(&m_cs);
  259. HRESULT hres;
  260. //
  261. // Find the record if not given
  262. //
  263. CClassRecord* pRecord = NULL;
  264. pRecord = FindClass(wszClassName);
  265. if(pRecord == NULL)
  266. {
  267. //
  268. // The record is not there --- there is nothing to invalidate. This
  269. // is based on the assumption that if a class record is in the
  270. // cache, then so are all its parents, which is true at the moment
  271. // because in order to construct a class we need to retrieve its
  272. // parents first.
  273. //
  274. return WBEM_S_FALSE;
  275. }
  276. pRecord->AddRef();
  277. CTemplateReleaseMe<CClassRecord> rm1(pRecord);
  278. LONGLONG lThisInvalidationIndex = m_lNextInvalidationIndex++;
  279. hres = InvalidateClassInternal(pRecord);
  280. //
  281. // Clear complete bits in all our parents, since this invalidation
  282. // means that no current enumeration of children can be trusted. At the same
  283. // time untie ourselves from the parent!
  284. //
  285. if(pRecord->m_pParent)
  286. {
  287. pRecord->m_pParent->m_bChildrenComplete = false;
  288. pRecord->m_pParent->m_bTreeComplete = false;
  289. pRecord->m_pParent->m_lLastChildInvalidationIndex =
  290. lThisInvalidationIndex;
  291. pRecord->m_pParent->RemoveChild(pRecord);
  292. CClassRecord* pCurrent = pRecord->m_pParent->m_pParent;
  293. while(pCurrent)
  294. {
  295. pCurrent->m_bTreeComplete = false;
  296. pCurrent = pCurrent->m_pParent;
  297. }
  298. }
  299. return S_OK;
  300. }
  301. HRESULT CHierarchyCache::InvalidateClassInternal(CClassRecord* pRecord)
  302. {
  303. //
  304. // Untie from the usage chain
  305. //
  306. A51TRACE(("%p: Invalidating %S on %I64d with %d children\n",
  307. this, pRecord->m_wszClassName, g_nCurrentTime,
  308. pRecord->m_apChildren.GetSize()));
  309. //
  310. // Remove all its children from the cache
  311. //
  312. for(int i = 0; i < pRecord->m_apChildren.GetSize(); i++)
  313. {
  314. InvalidateClassInternal(pRecord->m_apChildren[i]);
  315. }
  316. pRecord->m_apChildren.RemoveAll();
  317. //
  318. // Count ourselves out of the total memory
  319. //
  320. m_pForest->RemoveRecord(pRecord);
  321. //
  322. // Remove ourselves from the cache
  323. //
  324. m_map.erase(pRecord->m_wszHash);
  325. pRecord->Release();
  326. return S_OK;
  327. }
  328. HRESULT CHierarchyCache::DoneWithChildren(LPCWSTR wszClassName, bool bRecursive,
  329. LONGLONG lStartIndex, CClassRecord* pRecord)
  330. {
  331. CInCritSec ics(&m_cs);
  332. HRESULT hres;
  333. //
  334. // Find the record if not given
  335. //
  336. if(pRecord == NULL)
  337. {
  338. pRecord = FindClass(wszClassName);
  339. if(pRecord == NULL)
  340. {
  341. // Big time invalidation must have occurred
  342. return WBEM_S_FALSE;
  343. }
  344. }
  345. return DoneWithChildrenByRecord(pRecord, bRecursive, lStartIndex);
  346. }
  347. HRESULT CHierarchyCache::DoneWithChildrenByHash(LPCWSTR wszHash,
  348. bool bRecursive, LONGLONG lStartIndex)
  349. {
  350. CInCritSec ics(&m_cs);
  351. HRESULT hres;
  352. //
  353. // Find the record if not given
  354. //
  355. CClassRecord* pRecord = FindClassByKey(wszHash);
  356. if(pRecord == NULL)
  357. {
  358. // Big time invalidation must have occurred
  359. return WBEM_S_FALSE;
  360. }
  361. return DoneWithChildrenByRecord(pRecord, bRecursive, lStartIndex);
  362. }
  363. HRESULT CHierarchyCache::DoneWithChildrenByRecord(CClassRecord* pRecord,
  364. bool bRecursive, LONGLONG lStartIndex)
  365. {
  366. //
  367. // Check if any child invalidations occurred in this node since we started
  368. //
  369. if(lStartIndex < pRecord->m_lLastChildInvalidationIndex)
  370. return WBEM_S_FALSE;
  371. else
  372. pRecord->m_bChildrenComplete = true;
  373. if(bRecursive)
  374. {
  375. //
  376. // We have completed a recursive enumeration --- descend the
  377. // hierarchy and mark as complete all the children that have not been
  378. // modified since the start
  379. //
  380. bool bAllValid = true;
  381. for(int i = 0; i < pRecord->m_apChildren.GetSize(); i++)
  382. {
  383. CClassRecord* pChildRecord = pRecord->m_apChildren[i];
  384. HRESULT hres = DoneWithChildren(pChildRecord->m_wszClassName, true,
  385. lStartIndex, pChildRecord);
  386. if(hres != S_OK)
  387. bAllValid = false;
  388. }
  389. if(bAllValid)
  390. {
  391. //
  392. // There were no invalidations anywhere in the tree, which makes
  393. // this record tree-complete
  394. //
  395. pRecord->m_bTreeComplete = true;
  396. return WBEM_S_NO_ERROR;
  397. }
  398. else
  399. return S_FALSE;
  400. }
  401. else
  402. return WBEM_S_NO_ERROR;
  403. }
  404. RELEASE_ME _IWmiObject* CHierarchyCache::GetClassDef(LPCWSTR wszClassName,
  405. bool bClone, __int64* pnTime,
  406. bool* pbRead)
  407. {
  408. CInCritSec ics(&m_cs);
  409. CClassRecord* pRecord = FindClass(wszClassName);
  410. if(pRecord == NULL)
  411. return NULL;
  412. if(pnTime)
  413. *pnTime = pRecord->m_nClassDefCachedTime;
  414. if(pbRead)
  415. *pbRead = pRecord->m_bRead;
  416. return GetClassDefFromRecord(pRecord, bClone);
  417. }
  418. RELEASE_ME _IWmiObject* CHierarchyCache::GetClassDefByHash(LPCWSTR wszHash,
  419. bool bClone, __int64* pnTime,
  420. bool* pbRead)
  421. {
  422. CInCritSec ics(&m_cs);
  423. CClassRecord* pRecord = FindClassByKey(wszHash);
  424. if(pRecord == NULL)
  425. return NULL;
  426. if(pbRead)
  427. *pbRead = pRecord->m_bRead;
  428. if(pnTime)
  429. *pnTime = pRecord->m_nClassDefCachedTime;
  430. return GetClassDefFromRecord(pRecord, bClone);
  431. }
  432. // assumes: in m_cs
  433. RELEASE_ME _IWmiObject* CHierarchyCache::GetClassDefFromRecord(
  434. CClassRecord* pRecord,
  435. bool bClone)
  436. {
  437. //
  438. // Accessing m_pClassDef, so we have to lock the forest
  439. //
  440. CInCritSec ics(m_pForest->GetLock());
  441. if(pRecord->m_pClassDef)
  442. {
  443. m_pForest->MakeMostRecentlyUsed(pRecord);
  444. if(bClone)
  445. {
  446. IWbemClassObject* pObj = NULL;
  447. if(FAILED(pRecord->m_pClassDef->Clone(&pObj)))
  448. return NULL;
  449. else
  450. {
  451. _IWmiObject* pRes = NULL;
  452. pObj->QueryInterface(IID__IWmiObject, (void**)&pRes);
  453. pObj->Release();
  454. return pRes;
  455. }
  456. }
  457. else
  458. {
  459. pRecord->m_pClassDef->AddRef();
  460. return pRecord->m_pClassDef;
  461. }
  462. }
  463. else
  464. return NULL;
  465. }
  466. HRESULT CHierarchyCache::EnumChildren(LPCWSTR wszClassName, bool bRecursive,
  467. CWStringArray& awsChildren)
  468. {
  469. CInCritSec ics(&m_cs);
  470. //
  471. // Get the record
  472. //
  473. CClassRecord* pRecord = FindClass(wszClassName);
  474. if(pRecord == NULL)
  475. return WBEM_S_FALSE;
  476. //
  477. // Check if it is complete for this type of enumeration
  478. //
  479. if(!pRecord->m_bChildrenComplete)
  480. return WBEM_S_FALSE;
  481. if(bRecursive && !pRecord->m_bTreeComplete)
  482. return WBEM_S_FALSE;
  483. return EnumChildrenInternal(pRecord, bRecursive, awsChildren);
  484. }
  485. HRESULT CHierarchyCache::EnumChildrenInternal(CClassRecord* pRecord,
  486. bool bRecursive,
  487. CWStringArray& awsChildren)
  488. {
  489. for(int i = 0; i < pRecord->m_apChildren.GetSize(); i++)
  490. {
  491. CClassRecord* pChildRecord = pRecord->m_apChildren[i];
  492. if(awsChildren.Add(pChildRecord->m_wszClassName) < 0)
  493. return WBEM_E_OUT_OF_MEMORY;
  494. if(bRecursive)
  495. {
  496. HRESULT hres = EnumChildrenInternal(pChildRecord, bRecursive,
  497. awsChildren);
  498. if(FAILED(hres))
  499. return hres;
  500. }
  501. }
  502. return WBEM_S_NO_ERROR;
  503. }
  504. HRESULT CHierarchyCache::EnumChildKeysByKey(LPCWSTR wszClassKey,
  505. CWStringArray& awsChildKeys)
  506. {
  507. CInCritSec ics(&m_cs);
  508. //
  509. // Get the record
  510. //
  511. CClassRecord* pRecord = FindClassByKey(wszClassKey);
  512. if(pRecord == NULL)
  513. return WBEM_S_FALSE;
  514. //
  515. // Check if it is complete for this type of enumeration
  516. //
  517. if(!pRecord->m_bChildrenComplete)
  518. return WBEM_S_FALSE;
  519. for(int i = 0; i < pRecord->m_apChildren.GetSize(); i++)
  520. {
  521. CClassRecord* pChildRecord = pRecord->m_apChildren[i];
  522. if(awsChildKeys.Add(pChildRecord->m_wszHash) < 0)
  523. return WBEM_E_OUT_OF_MEMORY;
  524. }
  525. return WBEM_S_NO_ERROR;
  526. }
  527. HRESULT CHierarchyCache::GetKeyRoot(LPCWSTR wszClassName,
  528. TEMPFREE_ME LPWSTR* pwszKeyRoot)
  529. {
  530. CInCritSec ics(&m_cs);
  531. CClassRecord* pRecord = FindClass(wszClassName);
  532. if(pRecord == NULL)
  533. return WBEM_E_NOT_FOUND;
  534. return GetKeyRootByRecord(pRecord, pwszKeyRoot);
  535. }
  536. // assumes: in cs
  537. HRESULT CHierarchyCache::GetKeyRootByRecord(CClassRecord* pRecord,
  538. TEMPFREE_ME LPWSTR* pwszKeyRoot)
  539. {
  540. *pwszKeyRoot = NULL;
  541. if(!pRecord->m_bIsKeyed)
  542. return WBEM_E_CANNOT_BE_ABSTRACT;
  543. //
  544. // Go up until an unkeyed record is found. Keep the previous in pPrev
  545. //
  546. CClassRecord* pPrev = pRecord;
  547. while(pRecord && pRecord->m_bIsKeyed)
  548. {
  549. pPrev = pRecord;
  550. pRecord = pRecord->m_pParent;
  551. }
  552. if(pRecord)
  553. {
  554. //
  555. // Found unkeyed parent --- pPrev is the root
  556. //
  557. LPCWSTR wszKeyRoot = pPrev->m_wszClassName;
  558. DWORD dwLen = (wcslen(wszKeyRoot)+1) * sizeof(WCHAR);
  559. *pwszKeyRoot = (WCHAR*)TempAlloc(dwLen);
  560. if (*pwszKeyRoot == NULL)
  561. return WBEM_E_OUT_OF_MEMORY;
  562. wcscpy(*pwszKeyRoot, wszKeyRoot);
  563. return S_OK;
  564. }
  565. else
  566. {
  567. //
  568. // No unkeyed parents --- since "" is known to be unkeyed, we had have
  569. // hit a gap in the cache
  570. //
  571. return WBEM_E_NOT_FOUND;
  572. }
  573. }
  574. HRESULT CHierarchyCache::GetKeyRootByKey(LPCWSTR wszKey,
  575. TEMPFREE_ME LPWSTR* pwszKeyRoot)
  576. {
  577. CInCritSec ics(&m_cs);
  578. CClassRecord* pRecord = FindClassByKey(wszKey);
  579. if(pRecord == NULL)
  580. return WBEM_E_NOT_FOUND;
  581. return GetKeyRootByRecord(pRecord, pwszKeyRoot);
  582. }
  583. DELETE_ME LPWSTR CHierarchyCache::GetParent(LPCWSTR wszClassName)
  584. {
  585. CInCritSec ics(&m_cs);
  586. CClassRecord* pRecord = FindClass(wszClassName);
  587. if(pRecord == NULL)
  588. return NULL;
  589. if(pRecord->m_pParent)
  590. {
  591. LPCWSTR wszParent = pRecord->m_pParent->m_wszClassName;
  592. LPWSTR wszCopy = new WCHAR[wcslen(wszParent)+1];
  593. if (wszCopy == NULL)
  594. return NULL;
  595. wcscpy(wszCopy, wszParent);
  596. return wszCopy;
  597. }
  598. else
  599. return NULL;
  600. }
  601. HRESULT CForestCache::Initialize()
  602. {
  603. //
  604. // Read the size limits from the registry
  605. //
  606. HKEY hKey;
  607. long lRes = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
  608. L"SOFTWARE\\Microsoft\\WBEM\\CIMOM",
  609. 0, KEY_READ | KEY_WRITE, &hKey);
  610. if(lRes)
  611. return lRes;
  612. CRegCloseMe cm(hKey);
  613. DWORD dwLen = sizeof(DWORD);
  614. DWORD dwMaxSize;
  615. lRes = RegQueryValueExW(hKey, L"Max Class Cache Size", NULL, NULL,
  616. (LPBYTE)&dwMaxSize, &dwLen);
  617. //
  618. // If not there, set to default and write the default into the registry
  619. //
  620. if(lRes != ERROR_SUCCESS)
  621. {
  622. dwMaxSize = 5000000;
  623. lRes = RegSetValueExW(hKey, L"Max Class Cache Size", 0, REG_DWORD,
  624. (LPBYTE)&dwMaxSize, sizeof(DWORD));
  625. }
  626. //
  627. // Read the maximum useful age of an item
  628. //
  629. dwLen = sizeof(DWORD);
  630. DWORD dwMaxAge;
  631. lRes = RegQueryValueExW(hKey, L"Max Class Cache Item Age (ms)", NULL, NULL,
  632. (LPBYTE)&dwMaxAge, &dwLen);
  633. //
  634. // If not there, set to default and write the default into the registry
  635. //
  636. if(lRes != ERROR_SUCCESS)
  637. {
  638. dwMaxAge = 10000;
  639. lRes = RegSetValueExW(hKey, L"Max Class Cache Item Age (ms)", 0,
  640. REG_DWORD, (LPBYTE)&dwMaxAge, sizeof(DWORD));
  641. }
  642. //
  643. // Apply
  644. //
  645. SetMaxMemory(dwMaxSize, dwMaxAge);
  646. //
  647. // Create a timer queue for flushing
  648. //
  649. m_hTimerQueue = CreateTimerQueue();
  650. m_hCompletionEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  651. return WBEM_S_NO_ERROR;
  652. }
  653. bool CForestCache::MakeRoom(DWORD dwSize)
  654. {
  655. CInCritSec ics(&m_cs);
  656. if(dwSize > m_dwMaxMemory)
  657. return false; // no hope!
  658. //
  659. // Remove records until satisfied. Also, remove all records older than the
  660. // maximum age
  661. //
  662. DWORD dwNow = GetTickCount();
  663. while(m_pLeastRecentlyUsed &&
  664. (m_dwTotalMemory + dwSize > m_dwMaxMemory ||
  665. dwNow - m_pLeastRecentlyUsed->m_dwLastUsed > m_dwMaxAgeMs)
  666. )
  667. {
  668. RemoveRecord(m_pLeastRecentlyUsed);
  669. }
  670. return true;
  671. }
  672. bool CForestCache::Flush()
  673. {
  674. CInCritSec ics(&m_cs);
  675. while(m_pLeastRecentlyUsed)
  676. {
  677. RemoveRecord(m_pLeastRecentlyUsed);
  678. }
  679. return true;
  680. }
  681. bool CForestCache::Test()
  682. {
  683. if(m_pMostRecentlyUsed == NULL)
  684. {
  685. if(m_pLeastRecentlyUsed)
  686. DebugBreak();
  687. return true;
  688. }
  689. if(m_pMostRecentlyUsed->m_pMoreRecentlyUsed)
  690. DebugBreak();
  691. CClassRecord* pOne = m_pMostRecentlyUsed;
  692. CClassRecord* pTwo = m_pMostRecentlyUsed->m_pLessRecentlyUsed;
  693. while(pOne && pOne != pTwo)
  694. {
  695. if(pOne->m_pLessRecentlyUsed && pOne->m_pLessRecentlyUsed->m_pMoreRecentlyUsed != pOne)
  696. DebugBreak();
  697. if(pOne->m_pClassDef == NULL)
  698. DebugBreak();
  699. if(pOne->m_pLessRecentlyUsed == NULL && pOne != m_pLeastRecentlyUsed)
  700. DebugBreak();
  701. pOne = pOne->m_pLessRecentlyUsed;
  702. if(pTwo)
  703. pTwo = pTwo->m_pLessRecentlyUsed;
  704. if(pTwo)
  705. pTwo = pTwo->m_pLessRecentlyUsed;
  706. }
  707. if(pOne)
  708. DebugBreak();
  709. return true;
  710. }
  711. void CForestCache::MakeMostRecentlyUsed(CClassRecord* pRecord)
  712. {
  713. CInCritSec ics(&m_cs);
  714. //Test();
  715. Untie(pRecord);
  716. pRecord->m_pMoreRecentlyUsed = NULL;
  717. pRecord->m_pLessRecentlyUsed = m_pMostRecentlyUsed;
  718. if(m_pMostRecentlyUsed)
  719. m_pMostRecentlyUsed->m_pMoreRecentlyUsed = pRecord;
  720. m_pMostRecentlyUsed = pRecord;
  721. if(m_pLeastRecentlyUsed == NULL)
  722. m_pLeastRecentlyUsed = pRecord;
  723. pRecord->m_dwLastUsed = GetTickCount();
  724. pRecord->m_nStatus = 4;
  725. //Test();
  726. //
  727. // Schedule a timer to clean up, if not already there
  728. //
  729. if(m_hCurrentTimer == NULL)
  730. {
  731. CreateTimerQueueTimer(&m_hCurrentTimer, m_hTimerQueue,
  732. (WAITORTIMERCALLBACK)&staticTimerCallback, this, m_dwMaxAgeMs,
  733. m_dwMaxAgeMs, WT_EXECUTEINTIMERTHREAD);
  734. }
  735. }
  736. void CForestCache::staticTimerCallback(void* pParam, BOOLEAN)
  737. {
  738. ((CForestCache*)pParam)->TimerCallback();
  739. }
  740. void CForestCache::TimerCallback()
  741. {
  742. CInCritSec ics(&m_cs);
  743. //
  744. // Clean up what's stale
  745. //
  746. MakeRoom(0);
  747. //
  748. // See if we have any more reasons to live
  749. //
  750. if(m_pMostRecentlyUsed == NULL)
  751. {
  752. DeleteTimerQueueTimer(m_hTimerQueue, m_hCurrentTimer,
  753. m_hCompletionEvent);
  754. m_hCurrentTimer = NULL;
  755. }
  756. }
  757. void CForestCache::Add(CClassRecord* pRecord)
  758. {
  759. CInCritSec ics(&m_cs);
  760. MakeMostRecentlyUsed(pRecord);
  761. m_dwTotalMemory += pRecord->m_dwClassDefSize;
  762. pRecord->m_nStatus = 3;
  763. }
  764. void CForestCache::RemoveRecord(CClassRecord* pRecord)
  765. {
  766. CInCritSec ics(&m_cs);
  767. if(pRecord->m_pClassDef == NULL)
  768. return;
  769. Untie(pRecord);
  770. m_dwTotalMemory -= pRecord->m_dwClassDefSize;
  771. pRecord->m_pClassDef->Release();
  772. pRecord->m_pClassDef = NULL;
  773. pRecord->m_nStatus = 2;
  774. }
  775. void CForestCache::Untie(CClassRecord* pRecord)
  776. {
  777. //Test();
  778. CClassRecord* pPrev = pRecord->m_pLessRecentlyUsed;
  779. CClassRecord* pNext = pRecord->m_pMoreRecentlyUsed;
  780. if(pPrev)
  781. pPrev->m_pMoreRecentlyUsed = pNext;
  782. if(pNext)
  783. pNext->m_pLessRecentlyUsed = pPrev;
  784. if(m_pLeastRecentlyUsed == pRecord)
  785. m_pLeastRecentlyUsed = m_pLeastRecentlyUsed->m_pMoreRecentlyUsed;
  786. if(m_pMostRecentlyUsed == pRecord)
  787. m_pMostRecentlyUsed = m_pMostRecentlyUsed->m_pLessRecentlyUsed;
  788. pRecord->m_pMoreRecentlyUsed = pRecord->m_pLessRecentlyUsed = NULL;
  789. //Test();
  790. }
  791. void CForestCache::SetMaxMemory(DWORD dwMaxMemory, DWORD dwMaxAgeMs)
  792. {
  793. m_dwMaxMemory = dwMaxMemory;
  794. m_dwMaxAgeMs = dwMaxAgeMs;
  795. //
  796. // Make room for 0 bytes --- has the effect of clearing all the records
  797. // above the limit
  798. //
  799. MakeRoom(0);
  800. }
  801. CHierarchyCache* CForestCache::GetNamespaceCache(LPCWSTR wszNamespace)
  802. {
  803. CInCritSec ics(&m_cs);
  804. //
  805. // See if you can find one
  806. //
  807. TIterator it = m_map.find(wszNamespace);
  808. if(it != m_map.end())
  809. {
  810. it->second->AddRef();
  811. return it->second;
  812. }
  813. else
  814. {
  815. //
  816. // Not there --- create one
  817. //
  818. CHierarchyCache* pCache = new CHierarchyCache(this);
  819. if(pCache == NULL)
  820. return NULL;
  821. pCache->AddRef();
  822. m_map[wszNamespace] = pCache;
  823. return pCache;
  824. }
  825. }
  826. void CForestCache::ReleaseNamespaceCache(LPCWSTR wszNamespace,
  827. CHierarchyCache* pCache)
  828. {
  829. CInCritSec ics(&m_cs);
  830. //
  831. // Find it in the map
  832. //
  833. TIterator it = m_map.find(wszNamespace);
  834. _ASSERT(it != m_map.end(), L"Releasing a non-existing namespace cache");
  835. _ASSERT(it->second == pCache, L"Releasing a non-matching namespace cache");
  836. if(pCache->Release() == 0)
  837. {
  838. //
  839. // Last ref-count --- remove
  840. //
  841. delete it->second;
  842. m_map.erase(it);
  843. }
  844. }
  845. CForestCache::~CForestCache()
  846. {
  847. TIterator it = m_map.begin();
  848. while(it != m_map.end())
  849. {
  850. delete it->second;
  851. it++;
  852. }
  853. if(m_hCompletionEvent)
  854. CloseHandle(m_hCompletionEvent);
  855. if(m_hTimerQueue)
  856. DeleteTimerQueueEx(m_hTimerQueue, INVALID_HANDLE_VALUE);
  857. }