home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / winbase / winnt / mpheap / mpheap.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-04-12  |  18.7 KB  |  643 lines

  1. /*++
  2.  
  3. Copyright (c) 1995  Microsoft Corporation
  4.  
  5. Module Name:
  6.  
  7.     mpheap.c
  8.  
  9. Abstract:
  10.  
  11.     This DLL is a wrapper that sits on top of the Win32 Heap* api.  It
  12.     provides multiple heaps and handles all the serialization itself.
  13.  
  14.     Many multithreaded applications that use the standard memory allocation
  15.     routines (malloc/free, LocalAlloc/LocalFree, HeapAlloc/HeapFree) suffer
  16.     a significant a significant performance penalty when running on a
  17.     multi-processor machine.  This is due to the serialization used by the
  18.     default heap package.  On a multiprocessor machine, more than one
  19.     thread may simultaneously try to allocate memory.  One thread will
  20.     block on the critical section guarding the heap.  The other thread must
  21.     then signal the critical section when it is finished to unblock the
  22.     waiting thread.  The additional codepath of blocking and signalling adds
  23.     significant overhead to the frequent memory allocation path.
  24.  
  25.     By providing multiple heaps, this DLL allows simultaneous operations on
  26.     each heap.  A thread on processor 0 can allocate memory from one heap
  27.     at the same time that a thread on processor 1 is allocating from a
  28.     different heap.  The additional overhead in this DLL is compensated by
  29.     drastically reducing the number of times a thread must wait for heap
  30.     access.
  31.  
  32.     The basic scheme is to attempt to lock each heap in turn with the new
  33.     TryEnterCriticalSection API.  This will enter the critical section if
  34.     it is unowned.  If the critical section is owned by a different thread,
  35.     TryEnterCriticalSection returns failure instead of blocking until the
  36.     other thread leaves the critical section.
  37.  
  38.     Another trick to increase performance is the use of a lookaside list to
  39.     satisfy frequent allocations.  By using InterlockedExchange to remove
  40.     lookaside list entries and InterlockedCompareExchange to add lookaside
  41.     list entries, allocations and frees can be completed without needing a
  42.     critical section lock.
  43.  
  44.     The final trick is the use of delayed frees.  If a chunk of memory is
  45.     being freed, and the required lock is already held by a different
  46.     thread, the free block is simply added to a delayed free list and the
  47.     API completes immediately.  The next thread to acquire the heap lock
  48.     will free everything on the list.
  49.  
  50.     Every application uses memory allocation routines in different ways.
  51.     In order to allow better tuning of this package, MpHeapGetStatistics
  52.     allows an application to monitor the amount of contention it is
  53.     getting.  Increasing the number of heaps increases the potential
  54.     concurrency, but also increases memory overhead.  Some experimentation
  55.     is recommended to determine the optimal settings for a given number of
  56.     processors.
  57.  
  58.     Some applications can benefit from additional techniques.  For example,
  59.     per-thread lookaside lists for common allocation sizes can be very
  60.     effective.  No locking is required for a per-thread structure, since no
  61.     other thread will ever be accessing it.  Since each thread reuses the
  62.     same memory, per-thread structures also improve locality of reference.
  63.  
  64. --*/
  65. #include <windows.h>
  66. #include "mpheap.h"
  67.  
  68. #define MPHEAP_VALID_OPTIONS  (MPHEAP_GROWABLE                 | \
  69.                                MPHEAP_REALLOC_IN_PLACE_ONLY    | \
  70.                                MPHEAP_TAIL_CHECKING_ENABLED    | \
  71.                                MPHEAP_FREE_CHECKING_ENABLED    | \
  72.                                MPHEAP_DISABLE_COALESCE_ON_FREE | \
  73.                                MPHEAP_ZERO_MEMORY              | \
  74.                                MPHEAP_COLLECT_STATS)
  75.  
  76. //
  77. // Flags that are not passed on to the Win32 heap package
  78. //
  79. #define MPHEAP_PRIVATE_FLAGS (MPHEAP_COLLECT_STATS | MPHEAP_ZERO_MEMORY);
  80.  
  81. //
  82. // Define the heap header that gets tacked on the front of
  83. // every allocation. Eight bytes is a lot, but we can't make
  84. // it any smaller or else the allocation will not be properly
  85. // aligned for 64-bit quantities.
  86. //
  87. typedef struct _MP_HEADER {
  88.     union {
  89.         struct _MP_HEAP_ENTRY *HeapEntry;
  90.         PSINGLE_LIST_ENTRY Next;
  91.     };
  92.     ULONG LookasideIndex;
  93. } MP_HEADER, *PMP_HEADER;
  94. //
  95. // Definitions and structures for lookaside list
  96. //
  97. #define LIST_ENTRIES 128
  98.  
  99. typedef struct _MP_HEAP_LOOKASIDE {
  100.     PMP_HEADER Entry;
  101. } MP_HEAP_LOOKASIDE, *PMP_HEAP_LOOKASIDE;
  102.  
  103. #define NO_LOOKASIDE 0xffffffff
  104. #define MaxLookasideSize (8*LIST_ENTRIES-7)
  105. #define LookasideIndexFromSize(s) ((s < MaxLookasideSize) ? ((s) >> 3) : NO_LOOKASIDE)
  106.  
  107. //
  108. // Define the structure that describes the entire MP heap.
  109. //
  110. // There is one MP_HEAP_ENTRY structure for each Win32 heap
  111. // and a MP_HEAP structure that contains them all.
  112. //
  113. // Each MP_HEAP structure contains a lookaside list for quick
  114. // lock-free alloc/free of various size blocks.
  115. //
  116.  
  117. typedef struct _MP_HEAP_ENTRY {
  118.     HANDLE Heap;
  119.     PSINGLE_LIST_ENTRY DelayedFreeList;
  120.     CRITICAL_SECTION Lock;
  121.     DWORD Allocations;
  122.     DWORD Frees;
  123.     DWORD LookasideAllocations;
  124.     DWORD LookasideFrees;
  125.     DWORD DelayedFrees;
  126.     MP_HEAP_LOOKASIDE Lookaside[LIST_ENTRIES];
  127. } MP_HEAP_ENTRY, *PMP_HEAP_ENTRY;
  128.  
  129.  
  130. typedef struct _MP_HEAP {
  131.     DWORD HeapCount;
  132.     DWORD Flags;
  133.     DWORD Hint;
  134.     DWORD PadTo32Bytes;
  135.     MP_HEAP_ENTRY Entry[1];     // variable size
  136. } MP_HEAP, *PMP_HEAP;
  137.  
  138. VOID
  139. ProcessDelayedFreeList(
  140.     IN PMP_HEAP_ENTRY HeapEntry
  141.     );
  142.  
  143. //
  144. // HeapHint is a per-thread variable that offers a hint as to which heap to
  145. // check first.  By giving each thread affinity towards a different heap,
  146. // it is more likely that the first heap a thread picks for its allocation
  147. // will be available.  It also improves a thread's locality of reference,
  148. // which is very important for good MP performance
  149. //
  150. __declspec(thread) DWORD HeapHint;
  151.  
  152. HANDLE
  153. WINAPI
  154. MpHeapCreate(
  155.     DWORD flOptions,
  156.     DWORD dwInitialSize,
  157.     DWORD dwParallelism
  158.     )
  159. /*++
  160.  
  161. Routine Description:
  162.  
  163.     This routine creates an MP-enhanced heap. An MP heap consists of a
  164.     collection of standard Win32 heaps whose serialization is controlled
  165.     by the routines in this module to allow multiple simultaneous allocations.
  166.  
  167. Arguments:
  168.  
  169.     flOptions - Supplies the options for this heap.
  170.  
  171.         Currently valid flags are:
  172.  
  173.             MPHEAP_GROWABLE
  174.             MPHEAP_REALLOC_IN_PLACE_ONLY
  175.             MPHEAP_TAIL_CHECKING_ENABLED
  176.             MPHEAP_FREE_CHECKING_ENABLED
  177.             MPHEAP_DISABLE_COALESCE_ON_FREE
  178.             MPHEAP_ZERO_MEMORY
  179.             MPHEAP_COLLECT_STATS
  180.  
  181.     dwInitialSize - Supplies the initial size of the combined heaps.
  182.  
  183.     dwParallelism - Supplies the number of Win32 heaps that will make up the
  184.         MP heap. A value of zero defaults to three + # of processors.
  185.  
  186. Return Value:
  187.  
  188.     HANDLE - Returns a handle to the MP heap that can be passed to the
  189.              other routines in this package.
  190.  
  191.     NULL - Failure, GetLastError() specifies the exact error code.
  192.  
  193. --*/
  194. {
  195.     DWORD Error;
  196.     DWORD i;
  197.     HANDLE Heap;
  198.     PMP_HEAP MpHeap;
  199.     DWORD HeapSize;
  200.     DWORD PrivateFlags;
  201.  
  202.     if (flOptions & ~MPHEAP_VALID_OPTIONS) {
  203.         SetLastError(ERROR_INVALID_PARAMETER);
  204.         return(NULL);
  205.     }
  206.  
  207.     flOptions |= HEAP_NO_SERIALIZE;
  208.  
  209.     PrivateFlags = flOptions & MPHEAP_PRIVATE_FLAGS;
  210.  
  211.     flOptions &= ~MPHEAP_PRIVATE_FLAGS;
  212.  
  213.     if (dwParallelism == 0) {
  214.         SYSTEM_INFO SystemInfo;
  215.  
  216.         GetSystemInfo(&SystemInfo);
  217.         dwParallelism = 3 + SystemInfo.dwNumberOfProcessors;
  218.     }
  219.  
  220.     HeapSize = dwInitialSize / dwParallelism;
  221.  
  222.     //
  223.     // The first heap is special, since the MP_HEAP structure itself
  224.     // is allocated from there.
  225.     //
  226.     Heap = HeapCreate(flOptions,HeapSize,0);
  227.     if (Heap == NULL) {
  228.         //
  229.         // HeapCreate has already set last error appropriately.
  230.         //
  231.         return(NULL);
  232.     }
  233.  
  234.     MpHeap = HeapAlloc(Heap,0,sizeof(MP_HEAP) +
  235.                               (dwParallelism-1)*sizeof(MP_HEAP_ENTRY));
  236.     if (MpHeap==NULL) {
  237.         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  238.         HeapDestroy(Heap);
  239.         return(NULL);
  240.     }
  241.  
  242.     //
  243.     // Initialize the MP heap structure
  244.     //
  245.     MpHeap->HeapCount = 1;
  246.     MpHeap->Flags = PrivateFlags;
  247.     MpHeap->Hint = 0;
  248.  
  249.     //
  250.     // Initialize the first heap
  251.     //
  252.     MpHeap->Entry[0].Heap = Heap;
  253.     InitializeCriticalSection(&MpHeap->Entry[0].Lock);
  254.     MpHeap->Entry[0].DelayedFreeList = NULL;
  255.     ZeroMemory(MpHeap->Entry[0].Lookaside, sizeof(MpHeap->Entry[0].Lookaside));
  256.  
  257.     //
  258.     // Initialize the remaining heaps. Note that the heap has been
  259.     // sufficiently initialized to use MpHeapDestroy for cleanup
  260.     // if something bad happens.
  261.     //
  262.     for (i=1; i<dwParallelism; i++) {
  263.         MpHeap->Entry[i].Heap = HeapCreate(flOptions, HeapSize, 0);
  264.         if (MpHeap->Entry[i].Heap == NULL) {
  265.             Error = GetLastError();
  266.             MpHeapDestroy((HANDLE)MpHeap);
  267.             SetLastError(Error);
  268.             return(NULL);
  269.         }
  270.         InitializeCriticalSection(&MpHeap->Entry[i].Lock);
  271.         MpHeap->Entry[i].DelayedFreeList = NULL;
  272.         ZeroMemory(MpHeap->Entry[i].Lookaside, sizeof(MpHeap->Entry[i].Lookaside));
  273.         ++MpHeap->HeapCount;
  274.     }
  275.  
  276.     return((HANDLE)MpHeap);
  277. }
  278.  
  279. BOOL
  280. WINAPI
  281. MpHeapDestroy(
  282.     HANDLE hMpHeap
  283.     )
  284. {
  285.     DWORD i;
  286.     DWORD HeapCount;
  287.     PMP_HEAP MpHeap;
  288.     BOOL Success = TRUE;
  289.  
  290.     MpHeap = (PMP_HEAP)hMpHeap;
  291.     HeapCount = MpHeap->HeapCount;
  292.  
  293.     //
  294.     // Lock down all the heaps so we don't end up hosing people
  295.     // who may be allocating things while we are deleting the heaps.
  296.     // By setting MpHeap->HeapCount = 0 we also attempt to prevent
  297.     // people from getting hosed as soon as we delete the critical
  298.     // sections and heaps.
  299.     //
  300.     MpHeap->HeapCount = 0;
  301.     for (i=0; i<HeapCount; i++) {
  302.         EnterCriticalSection(&MpHeap->Entry[i].Lock);
  303.     }
  304.  
  305.     //
  306.     // Delete the heaps and their associated critical sections.
  307.     // Note that the order is important here. Since the MpHeap
  308.     // structure was allocated from MpHeap->Heap[0] we must
  309.     // delete that last.
  310.     //
  311.     for (i=HeapCount-1; i>0; i--) {
  312.         DeleteCriticalSection(&MpHeap->Entry[i].Lock);
  313.         if (!HeapDestroy(MpHeap->Entry[i].Heap)) {
  314.             Success = FALSE;
  315.         }
  316.     }
  317.     return(Success);
  318. }
  319.  
  320. BOOL
  321. WINAPI
  322. MpHeapValidate(
  323.     HANDLE hMpHeap,
  324.     LPVOID lpMem
  325.     )
  326. {
  327.     PMP_HEAP MpHeap;
  328.     DWORD i;
  329.     BOOL Success;
  330.     PMP_HEADER Header;
  331.     PMP_HEAP_ENTRY Entry;
  332.  
  333.     MpHeap = (PMP_HEAP)hMpHeap;
  334.  
  335.     if (lpMem == NULL) {
  336.  
  337.         //
  338.         // Lock and validate each heap in turn.
  339.         //
  340.         for (i=0; i < MpHeap->HeapCount; i++) {
  341.             Entry = &MpHeap->Entry[i];
  342.             try {
  343.                 EnterCriticalSection(&Entry->Lock);
  344.                 Success = HeapValidate(Entry->Heap, 0, NULL);
  345.                 LeaveCriticalSection(&Entry->Lock);
  346.             } except (EXCEPTION_EXECUTE_HANDLER) {
  347.                 return(FALSE);
  348.             }
  349.  
  350.             if (!Success) {
  351.                 return(FALSE);
  352.             }
  353.         }
  354.         return(TRUE);
  355.     } else {
  356.  
  357.         //
  358.         // Lock and validate the given heap entry
  359.         //
  360.         Header = ((PMP_HEADER)lpMem) - 1;
  361.         try {
  362.             EnterCriticalSection(&Header->HeapEntry->Lock);
  363.             Success = HeapValidate(Header->HeapEntry->Heap, 0, Header);
  364.             LeaveCriticalSection(&Header->HeapEntry->Lock);
  365.         } except (EXCEPTION_EXECUTE_HANDLER) {
  366.             return(FALSE);
  367.         }
  368.         return(Success);
  369.     }
  370. }
  371.  
  372. UINT
  373. WINAPI
  374. MpHeapCompact(
  375.     HANDLE hMpHeap
  376.     )
  377. {
  378.     PMP_HEAP MpHeap;
  379.     DWORD i;
  380.     DWORD LargestFreeSize=0;
  381.     DWORD FreeSize;
  382.     PMP_HEAP_ENTRY Entry;
  383.  
  384.     MpHeap = (PMP_HEAP)hMpHeap;
  385.  
  386.     //
  387.     // Lock and compact each heap in turn.
  388.     //
  389.     for (i=0; i < MpHeap->HeapCount; i++) {
  390.         Entry = &MpHeap->Entry[i];
  391.         EnterCriticalSection(&Entry->Lock);
  392.         FreeSize = HeapCompact(Entry->Heap, 0);
  393.         LeaveCriticalSection(&Entry->Lock);
  394.  
  395.         if (FreeSize > LargestFreeSize) {
  396.             LargestFreeSize = FreeSize;
  397.         }
  398.     }
  399.  
  400.     return(LargestFreeSize);
  401.  
  402. }
  403.  
  404.  
  405. LPVOID
  406. WINAPI
  407. MpHeapAlloc(
  408.     HANDLE hMpHeap,
  409.     DWORD flOptions,
  410.     DWORD dwBytes
  411.     )
  412. {
  413.     PMP_HEADER Header;
  414.     PMP_HEAP MpHeap;
  415.     DWORD i;
  416.     PMP_HEAP_ENTRY Entry;
  417.     DWORD Index;
  418.     DWORD Size;
  419.  
  420.     MpHeap = (PMP_HEAP)hMpHeap;
  421.  
  422.     flOptions |= MpHeap->Flags;
  423.  
  424.     Size = ((dwBytes + 7) & (ULONG)~7) + sizeof(MP_HEADER);
  425.     Index=LookasideIndexFromSize(Size);
  426.  
  427.     //
  428.     // Iterate through the heap locks looking for one
  429.     // that is not owned.
  430.     //
  431.     i=HeapHint;
  432.     if (i>=MpHeap->HeapCount) {
  433.         i=0;
  434.         HeapHint=0;
  435.     }
  436.     Entry = &MpHeap->Entry[i];
  437.     do {
  438.         //
  439.         // Check the lookaside list for a suitable allocation.
  440.         //
  441.         if ((Index != NO_LOOKASIDE) &&
  442.             (Entry->Lookaside[Index].Entry != NULL)) {
  443.             if ((Header = (PMP_HEADER)InterlockedExchange((PLONG)&Entry->Lookaside[Index].Entry,
  444.                                                           (LONG)NULL)) != NULL) {
  445.                 //
  446.                 // We have a lookaside hit, return it immediately.
  447.                 //
  448.                 ++Entry->LookasideAllocations;
  449.                 if (flOptions & MPHEAP_ZERO_MEMORY) {
  450.                     ZeroMemory(Header + 1, dwBytes);
  451.                 }
  452.                 HeapHint=i;
  453.                 return(Header + 1);
  454.             }
  455.         }
  456.  
  457.         //
  458.         // Attempt to lock this heap without blocking.
  459.         //
  460.         if (TryEnterCriticalSection(&Entry->Lock)) {
  461.             //
  462.             // success, go allocate immediately
  463.             //
  464.             goto LockAcquired;
  465.         }
  466.  
  467.         //
  468.         // This heap is owned by another thread, try
  469.         // the next one.
  470.         //
  471.         i++;
  472.         Entry++;
  473.         if (i==MpHeap->HeapCount) {
  474.             i=0;
  475.             Entry=&MpHeap->Entry[0];
  476.         }
  477.     } while ( i != HeapHint );
  478.  
  479.     //
  480.     // All of the critical sections were owned by someone else,
  481.     // so we have no choice but to wait for a critical section.
  482.     //
  483.     EnterCriticalSection(&Entry->Lock);
  484.  
  485. LockAcquired:
  486.     ++Entry->Allocations;
  487.     if (Entry->DelayedFreeList != NULL) {
  488.         ProcessDelayedFreeList(Entry);
  489.     }
  490.     Header = HeapAlloc(Entry->Heap, 0, Size);
  491.     LeaveCriticalSection(&Entry->Lock);
  492.     if (Header != NULL) {
  493.         Header->HeapEntry = Entry;
  494.         Header->LookasideIndex = Index;
  495.         if (flOptions & MPHEAP_ZERO_MEMORY) {
  496.             ZeroMemory(Header + 1, dwBytes);
  497.         }
  498.         HeapHint = i;
  499.         return(Header + 1);
  500.     } else {
  501.         return(NULL);
  502.     }
  503. }
  504.  
  505. LPVOID
  506. WINAPI
  507. MpHeapReAlloc(
  508.     HANDLE hMpHeap,
  509.     LPVOID lpMem,
  510.     DWORD dwBytes
  511.     )
  512. {
  513.     PMP_HEADER Header;
  514.     PCRITICAL_SECTION Lock;
  515.  
  516.     Header = ((PMP_HEADER)lpMem) - 1;
  517.     Lock = &Header->HeapEntry->Lock;
  518.     dwBytes = ((dwBytes + 7) & (ULONG)~7) + sizeof(MP_HEADER);
  519.  
  520.     EnterCriticalSection(Lock);
  521.     Header = HeapReAlloc(Header->HeapEntry->Heap, 0, Header, dwBytes);
  522.     LeaveCriticalSection(Lock);
  523.  
  524.     if (Header != NULL) {
  525.         Header->LookasideIndex = LookasideIndexFromSize(dwBytes);
  526.         return(Header + 1);
  527.     } else {
  528.         return(NULL);
  529.     }
  530. }
  531.  
  532. BOOL
  533. WINAPI
  534. MpHeapFree(
  535.     HANDLE hMpHeap,
  536.     LPVOID lpMem
  537.     )
  538. {
  539.     PMP_HEADER Header;
  540.     PCRITICAL_SECTION Lock;
  541.     BOOL Success;
  542.     PMP_HEAP_ENTRY HeapEntry;
  543.     PSINGLE_LIST_ENTRY Next;
  544.     PMP_HEAP MpHeap;
  545.  
  546.     Header = ((PMP_HEADER)lpMem) - 1;
  547.     HeapEntry = Header->HeapEntry;
  548.     MpHeap = (PMP_HEAP)hMpHeap;
  549.  
  550.     HeapHint = HeapEntry - &MpHeap->Entry[0];
  551.  
  552.     if (Header->LookasideIndex != NO_LOOKASIDE) {
  553.         //
  554.         // Try and put this back on the lookaside list
  555.         //
  556.         if (InterlockedCompareExchange((PVOID *)&HeapEntry->Lookaside[Header->LookasideIndex],
  557.                                        (PVOID)Header,
  558.                                        NULL) == NULL) {
  559.             //
  560.             // Successfully freed to lookaside list.
  561.             //
  562.             ++HeapEntry->LookasideFrees;
  563.             return(TRUE);
  564.         }
  565.     }
  566.     Lock = &HeapEntry->Lock;
  567.  
  568.     if (TryEnterCriticalSection(Lock)) {
  569.         ++HeapEntry->Frees;
  570.         Success = HeapFree(HeapEntry->Heap, 0, Header);
  571.         LeaveCriticalSection(Lock);
  572.         return(Success);
  573.     }
  574.     //
  575.     // The necessary heap critical section could not be immediately
  576.     // acquired. Post this free onto the Delayed free list and let
  577.     // whoever has the lock process it.
  578.     //
  579.     do {
  580.         Next = HeapEntry->DelayedFreeList;
  581.         Header->Next = Next;
  582.     } while ( InterlockedCompareExchange(&HeapEntry->DelayedFreeList,
  583.                                          &Header->Next,
  584.                                          Next) != Next);
  585.     return(TRUE);
  586. }
  587.  
  588. VOID
  589. ProcessDelayedFreeList(
  590.     IN PMP_HEAP_ENTRY HeapEntry
  591.     )
  592. {
  593.     PSINGLE_LIST_ENTRY FreeList;
  594.     PSINGLE_LIST_ENTRY Next;
  595.     PMP_HEADER Header;
  596.  
  597.     //
  598.     // Capture the entire delayed free list with a single interlocked exchange.
  599.     // Once we have removed the entire list, free each entry in turn.
  600.     //
  601.     FreeList = (PSINGLE_LIST_ENTRY)InterlockedExchange((LPLONG)&HeapEntry->DelayedFreeList, (LONG)NULL);
  602.     while (FreeList != NULL) {
  603.         Next = FreeList->Next;
  604.         Header = CONTAINING_RECORD(FreeList, MP_HEADER, Next);
  605.         ++HeapEntry->DelayedFrees;
  606.         HeapFree(HeapEntry->Heap, 0, Header);
  607.         FreeList = Next;
  608.     }
  609. }
  610.  
  611. DWORD
  612. MpHeapGetStatistics(
  613.     HANDLE hMpHeap,
  614.     LPDWORD lpdwSize,
  615.     MPHEAP_STATISTICS Stats[]
  616.     )
  617. {
  618.     PMP_HEAP MpHeap;
  619.     PMP_HEAP_ENTRY Entry;
  620.     DWORD i;
  621.     DWORD RequiredSize;
  622.  
  623.     MpHeap = (PMP_HEAP)hMpHeap;
  624.     RequiredSize = MpHeap->HeapCount * sizeof(MPHEAP_STATISTICS);
  625.     if (*lpdwSize < RequiredSize) {
  626.         *lpdwSize = RequiredSize;
  627.         return(ERROR_MORE_DATA);
  628.     }
  629.     ZeroMemory(Stats, MpHeap->HeapCount * sizeof(MPHEAP_STATISTICS));
  630.     for (i=0; i < MpHeap->HeapCount; i++) {
  631.         Entry = &MpHeap->Entry[i];
  632.  
  633.         Stats[i].Contention = Entry->Lock.DebugInfo->ContentionCount;
  634.         Stats[i].TotalAllocates = (Entry->Allocations + Entry->LookasideAllocations);
  635.         Stats[i].TotalFrees = (Entry->Frees + Entry->LookasideFrees + Entry->DelayedFrees);
  636.         Stats[i].LookasideAllocates = Entry->LookasideAllocations;
  637.         Stats[i].LookasideFrees = Entry->LookasideFrees;
  638.         Stats[i].DelayedFrees = Entry->DelayedFrees;
  639.     }
  640.     *lpdwSize = RequiredSize;
  641.     return(ERROR_SUCCESS);
  642. }
  643.