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

687 lines
20 KiB

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