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.

869 lines
23 KiB

  1. /*++
  2. Copyright (c) 1995-1996 Microsoft Corporation
  3. Module Name :
  4. acache.cxx
  5. Abstract:
  6. This module implements the Allocation cache handler and associated
  7. objects.
  8. Author:
  9. Murali R. Krishnan ( MuraliK ) 12-Sept-1996
  10. Environment:
  11. Win32 - User Mode
  12. Project:
  13. Internet Server DLL
  14. Functions Exported:
  15. Revision History:
  16. --*/
  17. // TODO:
  18. // * (Debug only) Add guard blocks before and after each allocation to detect
  19. // under- and overruns.
  20. // * (Debug only) Change the order of the freelist to FIFO (instead of
  21. // LIFO) to help catch cases of a block being free'd while something
  22. // else still points to it and then getting reused.
  23. /************************************************************
  24. * Include Headers
  25. ************************************************************/
  26. #include "precomp.hxx"
  27. #include <acache.hxx>
  28. #include <irtlmisc.h>
  29. #define PRIVATE_HEAP
  30. //
  31. // # of CPUs in machine (for allocation threshold scaling)
  32. //
  33. DWORD g_cCPU = 1;
  34. //
  35. // specifies the registry location to use for getting the ATQ Configuration
  36. // (Global overrides)
  37. //
  38. CHAR g_PSZ_ACACHE_CONFIG_PARAMS_REG_KEY[] = ACACHE_REG_PARAMS_REG_KEY;
  39. /************************************************************
  40. * Inlined Documentation on Alloc-Cache
  41. *
  42. * Allocation Cache:
  43. * This module is to cache the commonly allocated objects
  44. * to serve following goals
  45. * 1) we can have maximum reuse of blocks
  46. * 2) avoid traffic to the process heap manager
  47. * 3) gather statistics for understanding of usage
  48. *
  49. * Details on Allocation Cache:
  50. * There is one ALLOC_CACHE_HANDLER (shortly ACH) object per
  51. * object that we decide to cache. The ACH is initialized by
  52. * the configuration supplied during its construction. ACH serves
  53. * as the main object for allocation/free of the objects it is created
  54. * to cache. ACH gathers statistics of various operations and provides
  55. * hooks to export the gathered statistics. There is a periodic cleanup
  56. * scavenger that frees up long unused blocks thus reducing the working
  57. * set of the system.
  58. *
  59. * All ACH objects created are chained and maintained in the global
  60. * list of allocation cache handler objects. This global list is used
  61. * for enumeration, debugging, and statistics dumps
  62. *
  63. * Allocation cache Configuration:
  64. *
  65. * Each ACH object is created with the ALLOC_CACHE_CONFIGURATION that
  66. * specifies the (concurrency factor, threshold, size) desired.
  67. * The concurrency factor ensures that we support the specified level
  68. * of concurrency in allocations. The threshold specifies the number
  69. * of objects that we will maintain (max) in the free-list. When the
  70. * threshold is exceeded the freed objects are pushed to the process
  71. * pool until the currently active objects fall below the threshold.
  72. * In addition, each ACH object also retains a read-only name for the
  73. * object allocated - for friendly tracking purposes.
  74. *
  75. * There is also a global configuration parameter that specifies the
  76. * Lookaside cleanup interval.
  77. *
  78. * Allocation and Free:
  79. * Allocation allocates one free object from the free-list if any exist.
  80. * Otherwise the allocation will result in fetching a new object from
  81. * the process heap manager.
  82. * A free adds the freed object to the free-list if the # free objects
  83. * is less than the threshold specified. Otherwise the object is freed
  84. * to the process heap manager.
  85. * Statistics are gathered during both allocation and free operations.
  86. *
  87. * Statistics:
  88. * Statistics are gathered during the alloc/free operations throughout
  89. * the life-time of the ACH. These statistics are reported via the
  90. * DumpStatsToHtml() exported function. The statistics can also be
  91. * gathered by the NTSD helper function.
  92. *
  93. * Scheduled List cleanup:
  94. * There is a scheduled work item for the lookaside cleanup interval.
  95. * The callback function walks through the list of ACH items on global
  96. * list and takes snapshot of the # allocation calls. On a subsequent
  97. * walk-through, if the # allocation calls remains the same (which will
  98. * be the case if there is no allocation activity), then, the entire
  99. * list of alloced objects is pruned. This pruning reduces the working
  100. * set of the process.
  101. ************************************************************/
  102. /************************************************************
  103. * Static Functions of ALLOC_CACHE_HANDLER
  104. ************************************************************/
  105. CRITICAL_SECTION ALLOC_CACHE_HANDLER::sm_csItems;
  106. LIST_ENTRY ALLOC_CACHE_HANDLER::sm_lItemsHead;
  107. DWORD ALLOC_CACHE_HANDLER::sm_dwScheduleCookie = 0;
  108. LONG ALLOC_CACHE_HANDLER::sm_nFillPattern = 0xACA50000 ;
  109. // This class is used to implement the free list. We cast the free'd
  110. // memory block to a CFreeList*. The signature is used to guard against
  111. // double deletion. We also fill memory with a pattern.
  112. class CFreeList
  113. {
  114. public:
  115. SINGLE_LIST_ENTRY Next;
  116. DWORD dwSig;
  117. enum {
  118. FREESIG = (('A') | ('C' << 8) | ('a' << 16) | (('$' << 24) | 0x80)),
  119. };
  120. };
  121. /* class static */
  122. BOOL
  123. ALLOC_CACHE_HANDLER::Initialize(VOID)
  124. {
  125. // get the number of processors for this machine
  126. // do it only for NT Server only (don't scale workstation)
  127. if ( TsIsNtServer() ) {
  128. SYSTEM_INFO si;
  129. GetSystemInfo( &si );
  130. g_cCPU = si.dwNumberOfProcessors;
  131. } else {
  132. g_cCPU = 1;
  133. }
  134. // initialize the class statics
  135. InitializeListHead( &sm_lItemsHead);
  136. INITIALIZE_CRITICAL_SECTION( &sm_csItems);
  137. return ( TRUE);
  138. } // ALLOC_CACHE_HANDLER::Initialize()
  139. /* class static */
  140. BOOL
  141. ALLOC_CACHE_HANDLER::Cleanup(VOID)
  142. {
  143. DBG_ASSERT( sm_dwScheduleCookie == 0);
  144. DBG_ASSERT( IsListEmpty(&sm_lItemsHead));
  145. DeleteCriticalSection( &sm_csItems);
  146. return ( TRUE);
  147. } // ALLOC_CACHE_HANDLER::Cleanup()
  148. /* class static */
  149. VOID
  150. ALLOC_CACHE_HANDLER::InsertNewItem( IN ALLOC_CACHE_HANDLER * pach)
  151. {
  152. EnterCriticalSection( &sm_csItems);
  153. InsertTailList( &sm_lItemsHead, &pach->m_lItemsEntry);
  154. LeaveCriticalSection( &sm_csItems);
  155. return;
  156. } // ALLOC_CACHE_HANDLER::InsertNewItem()
  157. /* class static */
  158. VOID
  159. ALLOC_CACHE_HANDLER::RemoveItem( IN ALLOC_CACHE_HANDLER * pach)
  160. {
  161. EnterCriticalSection( &sm_csItems);
  162. RemoveEntryList( &pach->m_lItemsEntry);
  163. LeaveCriticalSection( &sm_csItems);
  164. return;
  165. } // ALLOC_CACHE_HANDLER::RemoveItem()
  166. /* class static */
  167. BOOL
  168. ALLOC_CACHE_HANDLER::DumpStatsToHtml(
  169. OUT CHAR * pchBuffer,
  170. IN OUT LPDWORD lpcchBuffer )
  171. /*++
  172. Description:
  173. This function dumps the stats on all allocation cached objects
  174. to HTML format for diagnostics
  175. Arguments:
  176. pchBuffer - pointer to buffer that will contain the html results
  177. lpcchBuffer - pointer to DWORD containing the size of buffer on entry
  178. On return this contains the # of bytes written out to buffer
  179. Return:
  180. TRUE for success and FALSE for failure
  181. Look at GetLastError() for the error code.
  182. --*/
  183. {
  184. LIST_ENTRY * pEntry;
  185. DWORD iCount, cch;
  186. DWORD cbTotalMem = 0;
  187. BOOL fRet = TRUE;
  188. if ( (lpcchBuffer == NULL) ) {
  189. SetLastError( ERROR_INVALID_PARAMETER);
  190. return ( FALSE);
  191. }
  192. EnterCriticalSection( &sm_csItems);
  193. if ( 300 < *lpcchBuffer ) {
  194. // Print the header blob
  195. cch = wsprintf( pchBuffer,
  196. "\r\nAllocCacheTable Data <br>\r\n"
  197. "<TABLE BORDER> <TR> "
  198. "<TH> Item Name </TH> "
  199. "<TH> Config(concurr, threshold, size) </TH> "
  200. "<TH> # Total Items </TH> "
  201. "<TH> # Alloc Calls </TH> "
  202. "<TH> # Free Calls </TH> "
  203. "<TH> # Free Entries </TH> "
  204. "<TH> # Total Size (bytes) </TH> "
  205. "<TH> Fill Pattern </TH> "
  206. "<TH> Heap </TH> "
  207. " </TR>\r\n"
  208. );
  209. } else {
  210. cch = 300;
  211. }
  212. for ( pEntry = sm_lItemsHead.Flink, iCount = 0;
  213. pEntry != &sm_lItemsHead;
  214. pEntry = pEntry->Flink, iCount++
  215. ) {
  216. ALLOC_CACHE_HANDLER * pach =
  217. CONTAINING_RECORD( pEntry,
  218. ALLOC_CACHE_HANDLER,
  219. m_lItemsEntry
  220. );
  221. cbTotalMem += pach->m_acConfig.cbSize * pach->m_nTotal;
  222. if ( (cch + 160 + strlen( pach->m_pszName)) < *lpcchBuffer) {
  223. cch += wsprintf( pchBuffer + cch,
  224. " <TR> <TD> [%d] %s </TD>"
  225. " <TD> (%d, %d, %d) </TD>"
  226. " <TD> %4d </TD>"
  227. " <TD> %4d </TD>"
  228. " <TD> %4d </TD>"
  229. " <TD> %4d </TD>"
  230. " <TD> %4d </TD>"
  231. " <TD> 0x%08lX </TD>"
  232. " <TD> %p </TD>"
  233. " </TR>\r\n"
  234. ,
  235. iCount, pach->m_pszName,
  236. pach->m_acConfig.nConcurrency,
  237. pach->m_acConfig.nThreshold,
  238. pach->m_acConfig.cbSize,
  239. pach->m_nTotal,
  240. pach->m_nAllocCalls,
  241. pach->m_nFreeCalls,
  242. pach->m_nFreeEntries,
  243. pach->m_acConfig.cbSize * pach->m_nTotal,
  244. pach->m_nFillPattern,
  245. pach->m_hHeap
  246. );
  247. } else {
  248. cch += 160 + strlen( pach->m_pszName);
  249. }
  250. } // for
  251. LeaveCriticalSection( &sm_csItems);
  252. //
  253. // dump the final summary
  254. //
  255. if ( (cch + 100 ) < *lpcchBuffer) {
  256. cch += wsprintf( pchBuffer + cch,
  257. " <b>"
  258. " <TR> </TR>"
  259. " <TR> <TD> Total </TD> <TD> </TD>"
  260. " <TD> </TD>"
  261. " <TD> </TD>"
  262. " <TD> </TD>"
  263. " <TD> </TD>"
  264. " <TD> %4d </TD>"
  265. " </TR>"
  266. "</b>\r\n"
  267. " </TABLE>\r\n\r\n"
  268. ,
  269. cbTotalMem
  270. );
  271. } else {
  272. cch += 100;
  273. }
  274. if ( *lpcchBuffer < cch ) {
  275. SetLastError( ERROR_INSUFFICIENT_BUFFER);
  276. fRet = FALSE;
  277. }
  278. *lpcchBuffer = cch;
  279. return (fRet);
  280. } // ALLOC_CACHE_HANDLER::DumpStatsToHtml()
  281. extern "C"
  282. BOOL AllocCacheDumpStatsToHtml( OUT CHAR * pch,
  283. IN OUT LPDWORD lpcchBuff)
  284. {
  285. return ( ALLOC_CACHE_HANDLER::DumpStatsToHtml( pch, lpcchBuff));
  286. }
  287. /* class static */
  288. BOOL
  289. ALLOC_CACHE_HANDLER::SetLookasideCleanupInterval( VOID )
  290. {
  291. DWORD dwError;
  292. DWORD dwVal = 0;
  293. HKEY hkey;
  294. dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  295. g_PSZ_ACACHE_CONFIG_PARAMS_REG_KEY,
  296. 0,
  297. KEY_READ,
  298. &hkey);
  299. if ( dwError == NO_ERROR ) {
  300. //
  301. // get the lookaside list cleanup period
  302. //
  303. dwVal = I_AtqReadRegDword( hkey,
  304. ACACHE_REG_LOOKASIDE_CLEANUP_INTERVAL,
  305. ACACHE_REG_DEFAULT_CLEANUP_INTERVAL );
  306. DBG_REQUIRE( !RegCloseKey( hkey ) );
  307. }
  308. if ( dwVal != 0 )
  309. {
  310. sm_dwScheduleCookie =
  311. ScheduleWorkItem( ALLOC_CACHE_HANDLER::CleanupAllLookasides,
  312. NULL,
  313. dwVal * 1000,
  314. TRUE );
  315. if ( sm_dwScheduleCookie == 0 )
  316. {
  317. return FALSE;
  318. }
  319. }
  320. return TRUE;
  321. } // ALLOC_CACHE_HANDLER::SetLookasideCleanupInterval()
  322. /* class static */
  323. BOOL
  324. ALLOC_CACHE_HANDLER::ResetLookasideCleanupInterval( VOID )
  325. {
  326. BOOL fReturn = TRUE;
  327. if ( sm_dwScheduleCookie )
  328. {
  329. fReturn = RemoveWorkItem( sm_dwScheduleCookie );
  330. if (fReturn) {
  331. sm_dwScheduleCookie = 0;
  332. }
  333. }
  334. return ( fReturn);
  335. } // ALLOC_CACHE_HANDLER::ResetLookasideCleanupInterval()
  336. /* class static */
  337. VOID
  338. WINAPI
  339. ALLOC_CACHE_HANDLER::CleanupAllLookasides(
  340. IN PVOID /* pvContext */
  341. )
  342. {
  343. LIST_ENTRY * pEntry;
  344. EnterCriticalSection( &sm_csItems);
  345. for ( pEntry = sm_lItemsHead.Flink;
  346. pEntry != &sm_lItemsHead;
  347. pEntry = pEntry->Flink )
  348. {
  349. ALLOC_CACHE_HANDLER * pach =
  350. CONTAINING_RECORD( pEntry,
  351. ALLOC_CACHE_HANDLER,
  352. m_lItemsEntry
  353. );
  354. IF_DEBUG( ALLOC_CACHE) {
  355. DBGPRINTF(( DBG_CONTEXT,
  356. "Cleaning lookaside list for '%s' handler\n",
  357. pach->m_pszName ));
  358. }
  359. pach->CleanupLookaside( FALSE );
  360. }
  361. LeaveCriticalSection( &sm_csItems);
  362. } // ALLOC_CACHE_HANDLER::CleanupAllLookasides()
  363. /************************************************************
  364. * Member Functions of ALLOC_CACHE_HANDLER
  365. ************************************************************/
  366. ALLOC_CACHE_HANDLER::ALLOC_CACHE_HANDLER(
  367. IN LPCSTR pszName,
  368. IN const ALLOC_CACHE_CONFIGURATION * pacConfig,
  369. IN BOOL fEnableCleanupAsserts /* = TRUE */
  370. )
  371. : m_fValid ( FALSE),
  372. m_nTotal (0),
  373. m_nAllocCalls (0),
  374. m_nFreeCalls (0),
  375. m_nFreeEntries (0),
  376. m_pszName (pszName),
  377. m_nLastAllocCount(0),
  378. m_hHeap (NULL),
  379. m_fCleanupAssertsEnabled(fEnableCleanupAsserts)
  380. {
  381. DBG_ASSERT( NULL != pacConfig );
  382. m_acConfig = *pacConfig;
  383. if ( pacConfig->nThreshold == INFINITE) {
  384. // this will be compared against a signed value. So be careful.
  385. m_acConfig.nThreshold = 0x7FFFFFFF;
  386. } else {
  387. // scale by the number of processors on MP machines
  388. m_acConfig.nThreshold *= g_cCPU;
  389. }
  390. // make sure the block is big enough to hold a CFreeList
  391. m_acConfig.cbSize = max(m_acConfig.cbSize, sizeof(CFreeList));
  392. // round up the block size to a multiple of the size of a LONG (for
  393. // the fill pattern in Free()).
  394. m_acConfig.cbSize =
  395. (m_acConfig.cbSize + sizeof(LONG) - 1) & ~(sizeof(LONG) - 1);
  396. INITIALIZE_CRITICAL_SECTION( & m_csLock);
  397. m_lHead.Next = NULL;
  398. m_nFillPattern = InterlockedIncrement(&sm_nFillPattern);
  399. //
  400. // Create private heap
  401. //
  402. #ifdef PRIVATE_HEAP
  403. if (TsIsNtServer())
  404. m_hHeap = HeapCreate( 0, 0, 0 );
  405. else
  406. m_hHeap = IisHeap();
  407. if( m_hHeap == NULL )
  408. {
  409. return;
  410. }
  411. #endif
  412. ALLOC_CACHE_HANDLER::InsertNewItem( this);
  413. m_fValid = TRUE;
  414. return;
  415. } // ALLOC_CACHE_HANDLER::ALLOC_CACHE_HANDLER()
  416. ALLOC_CACHE_HANDLER::~ALLOC_CACHE_HANDLER(VOID)
  417. {
  418. if ( m_fValid) {
  419. CleanupLookaside( TRUE );
  420. DeleteCriticalSection( & m_csLock);
  421. ALLOC_CACHE_HANDLER::RemoveItem( this);
  422. #ifdef PRIVATE_HEAP
  423. if ( m_hHeap )
  424. {
  425. if (TsIsNtServer())
  426. DBG_REQUIRE( HeapDestroy( m_hHeap ) );
  427. m_hHeap = NULL;
  428. }
  429. #endif
  430. }
  431. if (m_fCleanupAssertsEnabled) {
  432. DBG_ASSERT( 0 == m_nTotal );
  433. DBG_ASSERT( m_lHead.Next == NULL);
  434. }
  435. return;
  436. } // ALLOC_CACHE_HANDLER::~ALLOC_CACHE_HANDLER()
  437. VOID
  438. ALLOC_CACHE_HANDLER::CleanupLookaside(
  439. IN BOOL fForceCleanup
  440. )
  441. /*++
  442. Description:
  443. This function cleans up the lookaside list by removing excess storage space
  444. used by the objects allocated by this instance. This function is
  445. used by the periodic scavenging operation as well as for final cleanup.
  446. Arguments:
  447. fForceCleanup - forces a cleanup operation always.
  448. Returns:
  449. None
  450. --*/
  451. {
  452. if ( !fForceCleanup )
  453. {
  454. //
  455. // We are called for the regular scavenging operation
  456. // Take a snapshot of the # allocation calls so that
  457. // we may cleanup space when services are idle.
  458. //
  459. IF_DEBUG( ALLOC_CACHE) {
  460. DBGPRINTF(( DBG_CONTEXT,
  461. "AllocCalls = %ld, LastAllocCount = %ld\n",
  462. m_nAllocCalls,
  463. m_nLastAllocCount ));
  464. }
  465. if ( m_nAllocCalls != m_nLastAllocCount )
  466. {
  467. InterlockedExchange( &m_nLastAllocCount,
  468. m_nAllocCalls );
  469. return;
  470. }
  471. }
  472. SINGLE_LIST_ENTRY listHeadCopy;
  473. //
  474. // make a copy of the first element in the list inside the lock
  475. // Free the entire chain outside the locked section.
  476. // Otherwise on a busy system the threads will be waiting for
  477. // this thread to complete
  478. //
  479. Lock();
  480. listHeadCopy.Next = m_lHead.Next;
  481. //
  482. // we are about to cleanup all entries -> so set state back properly.
  483. //
  484. m_lHead.Next = NULL;
  485. m_nFreeEntries = 0; // no more free-entries available
  486. Unlock();
  487. //
  488. // free up all the entries in the list
  489. //
  490. PSINGLE_LIST_ENTRY pl;
  491. pl = PopEntryList( &listHeadCopy);
  492. while ( pl != NULL) {
  493. InterlockedDecrement( &m_nTotal);
  494. #ifdef PRIVATE_HEAP
  495. HeapFree( m_hHeap, 0, pl );
  496. #else
  497. ::LocalFree(pl);
  498. #endif
  499. pl = PopEntryList( &listHeadCopy);
  500. } // for
  501. DBG_ASSERT( listHeadCopy.Next == NULL);
  502. return;
  503. } // ALLOC_CACHE_HANDLER::CleanupLookaside()
  504. LPVOID
  505. ALLOC_CACHE_HANDLER::Alloc( VOID )
  506. {
  507. LPVOID pv = NULL;
  508. if ( m_nFreeEntries > 0) {
  509. //
  510. // There are free entries available - allocate from the free pool
  511. //
  512. // Only acquire the lock if there's potentially something to grab
  513. Lock();
  514. // Check again if the free entry is available.
  515. if ( m_nFreeEntries > 0) {
  516. pv = (LPVOID) PopEntryList( & m_lHead); // get the real object
  517. m_nFreeEntries--;
  518. }
  519. Unlock();
  520. if ( NULL != pv ) {
  521. CFreeList* pfl = (CFreeList*) pv;
  522. // If the signature is wrong then somebody's been scribbling
  523. // on memory that they've freed
  524. DBG_ASSERT(pfl->dwSig == CFreeList::FREESIG);
  525. pfl->dwSig = 0; // clear; just in case caller never overwrites
  526. }
  527. }
  528. if ( NULL == pv) {
  529. //
  530. // No free entry. Need to alloc a new object.
  531. //
  532. #ifdef PRIVATE_HEAP
  533. DBG_ASSERT( m_hHeap != NULL );
  534. pv = (LPVOID) HeapAlloc( m_hHeap,
  535. HEAP_ZERO_MEMORY,
  536. m_acConfig.cbSize );
  537. #else
  538. pv = (LPVOID) LocalAlloc( LPTR, m_acConfig.cbSize );
  539. #endif
  540. if ( NULL != pv) {
  541. // update counters
  542. InterlockedIncrement( &m_nTotal);
  543. }
  544. }
  545. if ( NULL != pv ) {
  546. InterlockedIncrement( &m_nAllocCalls);
  547. }
  548. return ( pv);
  549. } // ALLOC_CACHE_HANDLER::Alloc()
  550. BOOL
  551. ALLOC_CACHE_HANDLER::Free( LPVOID pv)
  552. {
  553. // Assume that this is allocated using the Alloc() function
  554. DBG_ASSERT( NULL != pv);
  555. // use a signature to check against double deletions
  556. CFreeList* pfl = (CFreeList*) pv;
  557. DBG_ASSERT(pfl->dwSig != CFreeList::FREESIG);
  558. #ifdef _DEBUG
  559. // Fill the memory with an improbable pattern that is unique
  560. // to this allocator (for identification in the debugger)
  561. RtlFillMemoryUlong(pv, m_acConfig.cbSize, m_nFillPattern);
  562. #else // !_DEBUG
  563. // Start filling the space beyond the portion overlaid by the initial
  564. // CFreeList. Fill at most 6 DWORDS.
  565. LONG* pl = (LONG*) (pfl+1);
  566. for (LONG cb = (LONG)min(6 * sizeof(LONG),m_acConfig.cbSize) - sizeof(CFreeList);
  567. cb > 0;
  568. cb -= sizeof(LONG))
  569. {
  570. *pl++ = m_nFillPattern;
  571. }
  572. #endif // !_DEBUG
  573. // Now, set the signature
  574. pfl->dwSig = CFreeList::FREESIG;
  575. // store the items in the alloc cache.
  576. if ( m_nFreeEntries >= m_acConfig.nThreshold) {
  577. //
  578. // threshold for free entries is exceeded. free the object to
  579. // process pool
  580. //
  581. #ifdef PRIVATE_HEAP
  582. HeapFree( m_hHeap, 0, pv );
  583. #else
  584. ::LocalFree(pv);
  585. #endif
  586. InterlockedDecrement( &m_nTotal);
  587. } else {
  588. //
  589. // Store the given pointer in the single linear list
  590. //
  591. Lock();
  592. PushEntryList( &m_lHead, &pfl->Next);
  593. m_nFreeEntries++;
  594. Unlock();
  595. }
  596. InterlockedIncrement( &m_nFreeCalls);
  597. return ( TRUE);
  598. } // ALLOC_CACHE_HANDLER::Free()
  599. VOID
  600. ALLOC_CACHE_HANDLER::Print( VOID)
  601. {
  602. CHAR rgchBuffer[8192];
  603. DWORD cchBuffer = sizeof(rgchBuffer);
  604. DBG_REQUIRE( IpPrint( rgchBuffer, &cchBuffer));
  605. DBGDUMP(( DBG_CONTEXT, rgchBuffer));
  606. return;
  607. } // ALLOC_CACHE_HANDLER::Print()
  608. BOOL
  609. ALLOC_CACHE_HANDLER::IpPrint( OUT CHAR * pchBuffer, IN OUT LPDWORD pcchSize)
  610. {
  611. DWORD cchUsed;
  612. cchUsed = wsprintfA( pchBuffer,
  613. "[%d]ALLOC_CACHE_HANDLER[%08p]. Config: "
  614. " ObjSize = %d. Concurrency=%d. Thres=%d.\n"
  615. " TotalObjs = %d. Calls: Alloc(%d), Free(%d)."
  616. " FreeEntries = %d. FillPattern = 0x%08lX.\n"
  617. ,
  618. GetCurrentThreadId(),
  619. this,
  620. m_acConfig.cbSize,
  621. m_acConfig.nConcurrency,
  622. m_acConfig.nThreshold,
  623. m_nTotal, m_nAllocCalls, m_nFreeCalls,
  624. m_nFreeEntries, m_nFillPattern
  625. );
  626. Lock();
  627. // NYI: Print the list of individual pointers
  628. Unlock();
  629. DBG_ASSERT( *pcchSize > cchUsed);
  630. *pcchSize = cchUsed;
  631. return (TRUE);
  632. } // ALLOC_CACHE_HANDLER::IpPrint()
  633. VOID
  634. ALLOC_CACHE_HANDLER::QueryStats( IN ALLOC_CACHE_STATISTICS * pacStats )
  635. {
  636. DBG_ASSERT( pacStats != NULL );
  637. pacStats->acConfig = m_acConfig;
  638. pacStats->nTotal = m_nTotal;
  639. pacStats->nAllocCalls = m_nAllocCalls;
  640. pacStats->nFreeCalls = m_nFreeCalls;
  641. pacStats->nFreeEntries = m_nFreeEntries;
  642. return;
  643. } // ALLOC_CACHE_HANDLER::QueryStats()
  644. //
  645. // Global functions
  646. //
  647. DWORD
  648. I_AtqReadRegDword(
  649. IN HKEY hkey,
  650. IN LPCSTR pszValueName,
  651. IN DWORD dwDefaultValue )
  652. /*++
  653. NAME: I_AtqReadRegDword
  654. SYNOPSIS: Reads a DWORD value from the registry.
  655. ENTRY: hkey - Openned registry key to read
  656. pszValueName - The name of the value.
  657. dwDefaultValue - The default value to use if the
  658. value cannot be read.
  659. RETURNS DWORD - The value from the registry, or dwDefaultValue.
  660. --*/
  661. {
  662. DWORD err;
  663. DWORD dwBuffer;
  664. DWORD cbBuffer = sizeof(dwBuffer);
  665. DWORD dwType;
  666. if( hkey != NULL ) {
  667. err = RegQueryValueExA( hkey,
  668. pszValueName,
  669. NULL,
  670. &dwType,
  671. (LPBYTE)&dwBuffer,
  672. &cbBuffer );
  673. if( ( err == NO_ERROR ) && ( dwType == REG_DWORD ) ) {
  674. dwDefaultValue = dwBuffer;
  675. }
  676. }
  677. return dwDefaultValue;
  678. } // I_AtqReadRegDword()
  679. /************************ End of File ***********************/