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.

3617 lines
89 KiB

  1. /*
  2. Copyright (c) 2000 Microsoft Corporation
  3. File name:
  4. heaplowf.c
  5. Author:
  6. adrmarin Thu Nov 30 2000
  7. Low Fragmentation Heap implementation
  8. The file implements a bucket oriented heap. This approach provides in general
  9. a significant bounded low fragmentation for large heap usages.
  10. Generally, applications tend to use only a few sizes for allocations. LFH contains a
  11. number of 128 buckets to allocate blocks up to 16K. The allocation granularity grows with
  12. the block size, keeping though a reasonable internal fragmentation (~6% for the worst case).
  13. The size and the granularity within each range is shown in the table below:
  14. Size Range Granularity Buckets
  15. 0 256 8 32
  16. 257 512 16 16
  17. 513 1024 32 16
  18. 1025 2048 64 16
  19. 2049 4096 128 16
  20. 4097 8192 256 16
  21. 8193 16384 512 16
  22. Regardless how randomly the allocation pattern is, the LFH will handle only
  23. 128 different sizes, choosing the smallest block large enough to complete the request.
  24. Each bucket places individual allocations into bigger blocks (sub-segments),
  25. which contain several other blocks with the same size. The allocations and free
  26. operations within sub-segments are lock free, the algorithm being similar with allocations
  27. from lookasides (interlocked S-Lists). This is the fastest path of the heap allocator,
  28. and provides performance similar with lookasides; it also keeps all these blocks together,
  29. avoiding fragmentation. Depending upon the heap usage, each bucket can have several sub-segments
  30. allocated to satisfy all requests, but only one of these is currently in use
  31. for allocations (it is active). When the active sub-segments has no sub-blocks available,
  32. another sub-segment will become active to satisfy the allocation requests. If the bucket does
  33. not have any available sub-segments, it will allocate a new one from the NT heap.
  34. Also, if a sub-segment does not contain any busy sub-block, the whole amount of memory
  35. will be returned to the NT heap. Unlike the allocations, which are done from the
  36. active sub-segments, the free operations can be done to every sub-segments, either active or passive.
  37. There are no constrains regarding the number of blocks within a sub-segment.
  38. LFH is more concerned with the sub-segments sizes that are allocated from the NT heap.
  39. Since the best-fit policy is good if we keep a relatively small number of sizes and blocks,
  40. the LFH will allocate sub-segments in size power of two. In practice, only about 9 different
  41. sizes will be requested from the NT heap (from 1K to 512K). In this way, depending upon the size
  42. in the current bucket will result a number of blocks. When the sub-segment is destroyed,
  43. that big chunk is returned to the NT heap, making it possible to be reused later for other buckets.
  44. Note that for some app scenarios, with low heap usage, random distributed, LFH is not the best choice.
  45. To achieve a good SMP scalability, all operations here are non-blocking. The only situation where
  46. we aquire a critical section is when wi allocate an array of sub-segment descriptors.
  47. This is a very rare case, even for an intensive MP usage.
  48. */
  49. #include "ntrtlp.h"
  50. #include "heap.h"
  51. #include "heappriv.h"
  52. //#define _HEAP_DEBUG
  53. #define PrintMsg DbgPrint
  54. #define HeapAlloc RtlAllocateHeap
  55. #define HeapFree RtlFreeHeap
  56. //
  57. // The conversion code needs the Lock/Unlock APIs
  58. //
  59. #define HeapLock RtlLockHeap
  60. #define HeapUnlock RtlUnlockHeap
  61. #define HeapSize RtlSizeHeap
  62. #ifdef _HEAP_DEBUG
  63. #define HeapValidate RtlValidateHeap
  64. #endif //_HEAP_DEBUG
  65. PSINGLE_LIST_ENTRY
  66. FASTCALL
  67. RtlpInterlockedPopEntrySList (
  68. IN PSLIST_HEADER ListHead
  69. );
  70. PSINGLE_LIST_ENTRY
  71. FASTCALL
  72. RtlpInterlockedPushEntrySList (
  73. IN PSLIST_HEADER ListHead,
  74. IN PSINGLE_LIST_ENTRY ListEntry
  75. );
  76. #define RtlpSubSegmentPop RtlpInterlockedPopEntrySList
  77. #define RtlpSubSegmentPush(SList,Block) \
  78. RtlpInterlockedPushEntrySList((SList),(PSINGLE_LIST_ENTRY)(Block))
  79. #define TEBDesiredAffinity (NtCurrentTeb()->HeapVirtualAffinity)
  80. //
  81. // On x86 is not available the interlockCompareExchange64. We need to implement it localy.
  82. // Also the macros below will take care of the inconsistency between definition
  83. // of this function on X86 and 64-bit platforms
  84. //
  85. #if !defined(_WIN64)
  86. LONGLONG
  87. FASTCALL
  88. RtlInterlockedCompareExchange64 (
  89. IN OUT PLONGLONG Destination,
  90. IN PLONGLONG Exchange,
  91. IN PLONGLONG Comperand
  92. );
  93. #define LOCKCOMP64(l,n,c) \
  94. (RtlInterlockedCompareExchange64((PLONGLONG)(l), (PLONGLONG)(&n), (PLONGLONG)(&c)) == (*((PLONGLONG)(&c))))
  95. #else //#if defined(_WIN64)
  96. //
  97. // 64 bit specific definitions
  98. //
  99. #define LOCKCOMP64(l,n,c) \
  100. (_InterlockedCompareExchange64((PLONGLONG)(l), *((PLONGLONG)(&n)), *((PLONGLONG)(&c))) == (*((PLONGLONG)(&c))))
  101. #endif // #if defined(_WIN64)
  102. ULONG
  103. FORCEINLINE
  104. RtlpGetFirstBitSet64(
  105. LONGLONG Mask
  106. )
  107. {
  108. if ((ULONG)Mask) {
  109. return RtlFindFirstSetRightMember((ULONG)Mask);
  110. }
  111. return 32 + RtlFindFirstSetRightMember((ULONG)(Mask >> 32));
  112. }
  113. #define HEAP_AFFINITY_LIMIT 64 // N.B. This cannot be larger than 64
  114. // (the number of bits in LONGLONG data type)
  115. typedef struct _AFFINITY_STATE{
  116. LONGLONG FreeEntries;
  117. LONGLONG UsedEntries;
  118. ULONG Limit;
  119. LONG CrtLimit;
  120. ULONG_PTR OwnerTID[ HEAP_AFFINITY_LIMIT ];
  121. //
  122. // The counters below are not absolutely necessary for the affinity manager.
  123. // But these help understanding the frequence of affinity changes. In general
  124. // accessing of these fields should be rare, even for many threads (like hundreds)
  125. // Benchmarks didn't show any visible difference with all these removed
  126. //
  127. ULONG AffinitySwaps;
  128. ULONG AffinityResets;
  129. ULONG AffinityLoops;
  130. ULONG AffinityAllocs;
  131. } AFFINITY_STATE, *PAFFINITY_STATE;
  132. #define GetCrtThreadId() ((ULONG_PTR)NtCurrentTeb()->ClientId.UniqueThread)
  133. AFFINITY_STATE RtlpAffinityState;
  134. VOID
  135. RtlpInitializeAffinityManager(
  136. UCHAR Size
  137. )
  138. /*++
  139. Routine Description:
  140. This routine initialize the affinity manager. This should be done
  141. only ones into that process, before any other affinity function is invoked
  142. Arguments:
  143. Size - The number of virtual affinity entries
  144. Return Value:
  145. None
  146. --*/
  147. {
  148. //
  149. // The size of the affinity bitmap is limited to the number of bits from
  150. // an LONGLONG data type.
  151. //
  152. if (Size > HEAP_AFFINITY_LIMIT) {
  153. PrintMsg( "HEAP: Invalid size %ld for the affinity mask. Using %ld instead\n",
  154. Size,
  155. HEAP_AFFINITY_LIMIT );
  156. Size = HEAP_AFFINITY_LIMIT;
  157. }
  158. RtlpAffinityState.FreeEntries = 0;
  159. RtlpAffinityState.UsedEntries = 0;
  160. RtlpAffinityState.Limit = Size;
  161. RtlpAffinityState.CrtLimit = -1;
  162. RtlpAffinityState.AffinitySwaps = 0;
  163. RtlpAffinityState.AffinityResets = 0;
  164. RtlpAffinityState.AffinityAllocs = 0;
  165. }
  166. ULONG
  167. FASTCALL
  168. RtlpAllocateAffinityIndex(
  169. )
  170. /*++
  171. Routine Description:
  172. The function allocates a new index into the virtual affinity array
  173. Arguments:
  174. Return Value:
  175. Return the index, which the current thread can use further.
  176. --*/
  177. {
  178. ULONGLONG CapturedMask;
  179. InterlockedIncrement(&RtlpAffinityState.AffinityAllocs);
  180. RETRY:
  181. //
  182. // Check first whether we have at least a free entry in the affinity mask
  183. //
  184. if (CapturedMask = RtlpAffinityState.FreeEntries) {
  185. ULONGLONG AvailableMask;
  186. AvailableMask = CapturedMask & RtlpAffinityState.UsedEntries;
  187. if (AvailableMask) {
  188. ULONG Index = RtlpGetFirstBitSet64(AvailableMask);
  189. LONGLONG NewMask = CapturedMask & ~((LONGLONG)1 << Index);
  190. if (!LOCKCOMP64(&RtlpAffinityState.FreeEntries, NewMask, CapturedMask)) {
  191. goto RETRY;
  192. }
  193. RtlpAffinityState.OwnerTID[ Index ] = GetCrtThreadId();
  194. return Index;
  195. }
  196. }
  197. //
  198. // Nothing available. We need to allocate a new entry. We won't do this
  199. // unless it's absolutely necessary
  200. //
  201. if (RtlpAffinityState.CrtLimit < (LONG)(RtlpAffinityState.Limit - 1)) {
  202. ULONG NewLimit = InterlockedIncrement(&RtlpAffinityState.CrtLimit);
  203. //
  204. // We already postponed growing the size. We have to do now
  205. //
  206. if ( NewLimit < RtlpAffinityState.Limit) {
  207. LONGLONG CapturedUsed;
  208. LONGLONG NewMask;
  209. do {
  210. CapturedUsed = RtlpAffinityState.UsedEntries;
  211. NewMask = CapturedUsed | ((LONGLONG)1 << NewLimit);
  212. } while ( !LOCKCOMP64(&RtlpAffinityState.UsedEntries, NewMask, CapturedUsed) );
  213. RtlpAffinityState.FreeEntries = ~((LONGLONG)1 << NewLimit);
  214. RtlpAffinityState.OwnerTID[ NewLimit ] = GetCrtThreadId();
  215. return NewLimit;
  216. } else {
  217. InterlockedDecrement(&RtlpAffinityState.CrtLimit);
  218. }
  219. }
  220. if ((RtlpAffinityState.FreeEntries & RtlpAffinityState.UsedEntries) == 0) {
  221. RtlpAffinityState.FreeEntries = (LONGLONG)-1;
  222. InterlockedIncrement( &RtlpAffinityState.AffinityResets );
  223. }
  224. InterlockedIncrement( &RtlpAffinityState.AffinityLoops );
  225. goto RETRY;
  226. //
  227. // return something to make the compiler happy
  228. //
  229. return 0;
  230. }
  231. ULONG
  232. FORCEINLINE
  233. RtlpGetThreadAffinity(
  234. )
  235. /*++
  236. Routine Description:
  237. The function returns the affinity which the current thread can use.
  238. This function is designed to be called pretty often. It has a quick path,
  239. which test whether the last affinity assigned did not expired.
  240. If the number of threads is less than the number of processors, the threads will
  241. never get moved from an index to another.
  242. Arguments:
  243. None
  244. Return Value:
  245. Return the index, which the current thread can use further.
  246. --*/
  247. {
  248. LONG NewAffinity;
  249. LONG CapturedAffinity = TEBDesiredAffinity - 1;
  250. if (CapturedAffinity >= 0) {
  251. if (RtlpAffinityState.OwnerTID[CapturedAffinity] == GetCrtThreadId()) {
  252. if (RtlpAffinityState.FreeEntries & ((LONGLONG)1 << CapturedAffinity)) {
  253. LONGLONG NewMask = RtlpAffinityState.FreeEntries & ~(((LONGLONG)1 << CapturedAffinity));
  254. LOCKCOMP64(&RtlpAffinityState.FreeEntries, NewMask, RtlpAffinityState.FreeEntries);
  255. }
  256. return CapturedAffinity;
  257. }
  258. } else {
  259. //
  260. // A new thread came up. Reset the affinity
  261. //
  262. RtlpAffinityState.FreeEntries = (LONGLONG) -1;
  263. }
  264. NewAffinity = RtlpAllocateAffinityIndex();
  265. if ((NewAffinity + 1) != TEBDesiredAffinity) {
  266. InterlockedIncrement( &RtlpAffinityState.AffinitySwaps );
  267. }
  268. TEBDesiredAffinity = NewAffinity + 1;
  269. return NewAffinity;
  270. }
  271. //
  272. // Low fragmentation heap tunning constants
  273. //
  274. #define LOCALPROC FORCEINLINE
  275. //
  276. // The total number of buckets. The default is 128 which coveres
  277. // blocks up to 16 K
  278. //
  279. // N.B. HEAP_BUCKETS_COUNT must be > 32 and multiple of 16
  280. //
  281. #define HEAP_BUCKETS_COUNT 128
  282. //
  283. // Defining the limits for the number of blocks that can exist in sub-segment
  284. // Number of blocks >= 2^HEAP_MIN_BLOCK_CLASS
  285. // &&
  286. // Number of blocks <= 2^HEAP_MAX_BLOCK_CLASS
  287. // &&
  288. // sub-segment size <= HEAP_MAX_SUBSEGMENT_SIZE
  289. //
  290. #define HEAP_MIN_BLOCK_CLASS 4
  291. #define HEAP_MAX_BLOCK_CLASS 10
  292. #define HEAP_MAX_SUBSEGMENT_SIZE (0x0000F000 << HEAP_GRANULARITY_SHIFT) // must be smaller than HEAP_MAXIMUM_BLOCK_SIZE
  293. //
  294. // If a size become very popular, LFH increases the number of blocks
  295. // that could be placed into the subsegments, with the formula below.
  296. //
  297. #define RtlpGetDesiredBlockNumber(Aff,T) \
  298. ((Aff) ? (((T) >> 4) / (RtlpHeapMaxAffinity)) : ((T) >> 4))
  299. //
  300. // LFH uses only a few different sizes for subsegments. These have sizes
  301. // of power of two between
  302. // 2^HEAP_LOWEST_USER_SIZE_INDEX and 2^HEAP_HIGHEST_USER_SIZE_INDEX
  303. //
  304. #define HEAP_LOWEST_USER_SIZE_INDEX 7
  305. #define HEAP_HIGHEST_USER_SIZE_INDEX 18
  306. //
  307. // The sub-segments descriptors are allocated in zones, based on
  308. // processor affinity. These descriptors are in general small structures,
  309. // so ignoring the affinity impacts the performance on MP machines with
  310. // caches > sizeof( HEAP_SUBSEGMENT ).
  311. // Also it significantly reduces the number of calls to the NT heap
  312. //
  313. #define HEAP_DEFAULT_ZONE_SIZE (1024 - sizeof(HEAP_ENTRY)) // allocate 1 K at ones
  314. //
  315. // Each bucket holds a number of subsegments into a cache, in order
  316. // to find the emptiest one for reusage. FREE_CACHE_SIZE defines the
  317. // number of sub-segments that will be searched
  318. //
  319. #define FREE_CACHE_SIZE 16
  320. //
  321. // On low memory, the subsegments that are almost free can be converted to
  322. // the regular NT heap. HEAP_CONVERT_LIMIT gives the maximum space that can be
  323. // converted at ones
  324. //
  325. #define HEAP_CONVERT_LIMIT 0x1000000 // Do not convert more than 16 MBytes at ones
  326. //
  327. // Cache tunning constants.
  328. // LFH keeps the subsegments into a cache to be easy reused for different allocations
  329. // This significantly reduces the number of calls to the NT heap with huge impact
  330. // in scalability, performance and footprint for the most common cases.
  331. // The only problem is in a shrinking phase, when the app frees the most part
  332. // of the memory, and we want to reduce the commited space for the heap.
  333. // We handle this case with the following two constants:
  334. // - HEAP_CACHE_FREE_THRESHOLD
  335. // - HEAP_CACHE_SHIFT_THRESHOLD
  336. //
  337. // The heap will free a block to the NT heap only if these conditions are TRUE:
  338. // - The number of blocks in cache for that size > HEAP_CACHE_FREE_THRESHOLD
  339. // - The number of blocks in cache for that size >
  340. // (Total number of blocks of that size) >> HEAP_CACHE_SHIFT_THRESHOLD
  341. //
  342. //
  343. #define HEAP_CACHE_FREE_THRESHOLD 8
  344. #define HEAP_CACHE_SHIFT_THRESHOLD 2
  345. //
  346. // Other definitions
  347. //
  348. #define NO_MORE_ENTRIES 0xFFFF
  349. //
  350. // Locking constants
  351. //
  352. #define HEAP_USERDATA_LOCK 1
  353. #define HEAP_PUBLIC_LOCK 2
  354. #define HEAP_ACTIVE_LOCK 4
  355. #define HEAP_CONVERT_LOCK 8
  356. #define HEAP_FREE_BLOCK_SUCCESS 1
  357. #define HEAP_FREE_BLOCK_CONVERTED 2
  358. #define HEAP_FREE_SEGMENT_EMPTY 3
  359. //
  360. // Low fragmentation heap data structures
  361. //
  362. typedef union _HEAP_BUCKET_COUNTERS{
  363. struct {
  364. volatile ULONG TotalBlocks;
  365. volatile ULONG SubSegmentCounts;
  366. };
  367. volatile LONGLONG Aggregate64;
  368. } HEAP_BUCKET_COUNTERS, *PHEAP_BUCKET_COUNTERS;
  369. //
  370. // The HEAP_BUCKET structure handles same size allocations
  371. //
  372. typedef struct _HEAP_BUCKET {
  373. HEAP_BUCKET_COUNTERS Counters;
  374. USHORT BlockUnits;
  375. UCHAR SizeIndex;
  376. UCHAR UseAffinity;
  377. LONG Conversions;
  378. } HEAP_BUCKET, *PHEAP_BUCKET;
  379. //
  380. // LFH heap uses zones to allocate sub-segment descriptors. This will preallocate
  381. // a large block and then for each individual sub-segment request will move the
  382. // water mark pointer with a non-blocking operation
  383. //
  384. typedef struct _LFH_BLOCK_ZONE {
  385. LIST_ENTRY ListEntry;
  386. PVOID FreePointer;
  387. PVOID Limit;
  388. } LFH_BLOCK_ZONE, *PLFH_BLOCK_ZONE;
  389. typedef struct _HEAP_LOCAL_SEGMENT_INFO {
  390. PHEAP_SUBSEGMENT Hint;
  391. PHEAP_SUBSEGMENT ActiveSubsegment;
  392. PHEAP_SUBSEGMENT CachedItems[ FREE_CACHE_SIZE ];
  393. SLIST_HEADER SListHeader;
  394. SIZE_T BusyEntries;
  395. SIZE_T LastUsed;
  396. } HEAP_LOCAL_SEGMENT_INFO, *PHEAP_LOCAL_SEGMENT_INFO;
  397. typedef struct _HEAP_LOCAL_DATA {
  398. //
  399. // We reserve the 128 bytes below to avoid sharing memory
  400. // into the same cacheline on MP machines
  401. //
  402. UCHAR Reserved[128];
  403. volatile PLFH_BLOCK_ZONE CrtZone;
  404. struct _LFH_HEAP * LowFragHeap;
  405. HEAP_LOCAL_SEGMENT_INFO SegmentInfo[HEAP_BUCKETS_COUNT];
  406. SLIST_HEADER DeletedSubSegments;
  407. ULONG Affinity;
  408. ULONG Reserved1;
  409. } HEAP_LOCAL_DATA, *PHEAP_LOCAL_DATA;
  410. //
  411. // Fixed size large block cache data structures & definitions
  412. // This holds in S-Lists the blocks that can be free, but it
  413. // delay the free until no other thread is doing a heap operation
  414. // This helps reducing the contention on the heap lock,
  415. // improve the scalability with a relatively low memory footprint
  416. //
  417. #define HEAP_USER_ENTRIES (HEAP_HIGHEST_USER_SIZE_INDEX - HEAP_LOWEST_USER_SIZE_INDEX + 1)
  418. typedef struct _USER_MEMORY_CACHE {
  419. SLIST_HEADER UserBlocks[ HEAP_USER_ENTRIES ];
  420. ULONG FreeBlocks;
  421. ULONG Sequence;
  422. ULONG MinDepth[ HEAP_USER_ENTRIES ];
  423. ULONG AvailableBlocks[ HEAP_USER_ENTRIES ];
  424. } USER_MEMORY_CACHE, *PUSER_MEMORY_CACHE;
  425. typedef struct _LFH_HEAP {
  426. RTL_CRITICAL_SECTION Lock;
  427. LIST_ENTRY SubSegmentZones;
  428. SIZE_T ZoneBlockSize;
  429. HANDLE Heap;
  430. LONG Conversions;
  431. LONG ConvertedSpace;
  432. ULONG SegmentChange; //
  433. ULONG SegmentCreate; // Various counters (optional)
  434. ULONG SegmentInsertInFree; //
  435. ULONG SegmentDelete; //
  436. USER_MEMORY_CACHE UserBlockCache;
  437. //
  438. // Bucket data
  439. //
  440. HEAP_BUCKET Buckets[HEAP_BUCKETS_COUNT];
  441. //
  442. // The LocalData array must be the last field in LFH structures
  443. // The sizes of the array is choosen depending upon the
  444. // number of processors.
  445. //
  446. HEAP_LOCAL_DATA LocalData[1];
  447. } LFH_HEAP, *PLFH_HEAP;
  448. //
  449. // Debugging macros.
  450. //
  451. #ifdef _HEAP_DEBUG
  452. LONG RtlpColissionCounter = 0;
  453. #define LFHEAPASSERT(exp) \
  454. if (!(exp)) { \
  455. PrintMsg( "\nERROR: %s\n\tSource File: %s, line %ld\n", #exp, __FILE__, __LINE__);\
  456. DbgBreakPoint(); \
  457. }
  458. #define LFHEAPWARN(exp) \
  459. if (!(exp)) PrintMsg( "\nWARNING: %s\n\tSource File: %s, line %ld\n", #exp, __FILE__, __LINE__);
  460. #define LFH_DECLARE_COUNTER ULONG __Counter = 0;
  461. #define LFH_UPDATE_COUNTER \
  462. if ((++__Counter) > 1) { \
  463. InterlockedIncrement(&RtlpColissionCounter); \
  464. }
  465. #else
  466. #define LFHEAPASSERT(exp)
  467. #define LFHEAPWARN(exp)
  468. #define LFH_DECLARE_COUNTER
  469. #define LFH_UPDATE_COUNTER
  470. #endif
  471. BOOLEAN
  472. FORCEINLINE
  473. RtlpLockSubSegment(
  474. PHEAP_SUBSEGMENT SubSegment,
  475. ULONG LockMask
  476. );
  477. BOOLEAN
  478. LOCALPROC
  479. RtlpUnlockSubSegment(
  480. PHEAP_LOCAL_DATA LocalData,
  481. PHEAP_SUBSEGMENT SubSegment,
  482. ULONG LockMask
  483. );
  484. BOOLEAN
  485. FASTCALL
  486. RtlpConvertSegmentToHeap (
  487. PLFH_HEAP LowFragHeap,
  488. PHEAP_SUBSEGMENT SubSegment
  489. );
  490. ULONG
  491. RtlpFlushLFHeapCache (
  492. PLFH_HEAP LowFragHeap
  493. );
  494. //
  495. // Heap manager globals
  496. //
  497. SIZE_T RtlpBucketBlockSizes[HEAP_BUCKETS_COUNT];
  498. ULONG RtlpHeapMaxAffinity = 0;
  499. //
  500. // User block management private functions
  501. //
  502. SIZE_T
  503. FORCEINLINE
  504. RtlpConvertSizeIndexToSize(
  505. UCHAR SizeIndex
  506. )
  507. /*++
  508. Routine Description:
  509. The function converts a size index into a memory block size.
  510. LFH requests only these particular sizes from the NT heap
  511. Arguments:
  512. SizeIndex - The size category
  513. Return Value:
  514. The size in bytes to be requested from the NT heap
  515. --*/
  516. {
  517. SIZE_T Size = 1 << SizeIndex;
  518. LFHEAPASSERT( SizeIndex >= HEAP_LOWEST_USER_SIZE_INDEX );
  519. LFHEAPASSERT( SizeIndex <= HEAP_HIGHEST_USER_SIZE_INDEX );
  520. if (Size > HEAP_MAX_SUBSEGMENT_SIZE) {
  521. Size = HEAP_MAX_SUBSEGMENT_SIZE;
  522. }
  523. return Size - sizeof(HEAP_ENTRY);
  524. }
  525. PVOID
  526. FASTCALL
  527. RtlpAllocateUserBlock(
  528. PLFH_HEAP LowFragHeap,
  529. UCHAR SizeIndex
  530. )
  531. /*++
  532. Routine Description:
  533. The function allocates a large block for the sub-segment user data
  534. It tries first to allocate from the cache. So it makes an NT heap call
  535. only if the first one fails. The blocks allocated with this routine
  536. can only have power of 2 sizes (from 256, 512, ....)
  537. Arguments:
  538. LowFragHeap - The pointer to the LF heap
  539. SizeIndex - The category size to be allocated
  540. Return Value:
  541. Returns a pointer to the new allocated block, or NULL if the operation fails
  542. --*/
  543. {
  544. PVOID ListEntry;
  545. PHEAP_USERDATA_HEADER UserBlock = NULL;
  546. LFHEAPASSERT(SizeIndex >= HEAP_LOWEST_USER_SIZE_INDEX);
  547. LFHEAPASSERT(SizeIndex <= HEAP_HIGHEST_USER_SIZE_INDEX);
  548. //
  549. // Allocates first from the slist cache
  550. //
  551. __try {
  552. //
  553. // Search first into the indicated index
  554. //
  555. if (ListEntry = RtlpSubSegmentPop(&LowFragHeap->UserBlockCache.UserBlocks[SizeIndex - HEAP_LOWEST_USER_SIZE_INDEX])) {
  556. UserBlock = CONTAINING_RECORD(ListEntry, HEAP_USERDATA_HEADER, SFreeListEntry);
  557. leave;
  558. }
  559. //
  560. // Look for a smaller size
  561. //
  562. if (SizeIndex > HEAP_LOWEST_USER_SIZE_INDEX) {
  563. if (ListEntry = RtlpSubSegmentPop(&LowFragHeap->UserBlockCache.UserBlocks[SizeIndex - HEAP_LOWEST_USER_SIZE_INDEX - 1])) {
  564. UserBlock = CONTAINING_RECORD(ListEntry, HEAP_USERDATA_HEADER, SFreeListEntry);
  565. leave;
  566. }
  567. }
  568. } __except (EXCEPTION_EXECUTE_HANDLER) {
  569. //
  570. // Nothing to do
  571. //
  572. }
  573. if (UserBlock == NULL) {
  574. //
  575. // There is no available blocks into the cache. We need to
  576. // allocate the subsegment from the NT heap
  577. //
  578. InterlockedIncrement(&LowFragHeap->UserBlockCache.AvailableBlocks[ SizeIndex - HEAP_LOWEST_USER_SIZE_INDEX ]);
  579. UserBlock = HeapAlloc(LowFragHeap->Heap, HEAP_NO_CACHE_BLOCK, RtlpConvertSizeIndexToSize(SizeIndex));
  580. if (UserBlock) {
  581. UserBlock->SizeIndex = SizeIndex;
  582. }
  583. }
  584. return UserBlock;
  585. }
  586. VOID
  587. FASTCALL
  588. RtlpFreeUserBlock(
  589. PLFH_HEAP LowFragHeap,
  590. PHEAP_USERDATA_HEADER UserBlock
  591. )
  592. /*++
  593. Routine Description:
  594. Free a block previously allocated with RtlpAllocateUserBlock.
  595. Arguments:
  596. LowFragHeap - The pointer to the LF heap
  597. UserBlock - The block to be freed
  598. Return Value:
  599. None.
  600. --*/
  601. {
  602. ULONG Depth;
  603. ULONG SizeIndex = (ULONG)UserBlock->SizeIndex;
  604. PSLIST_HEADER ListHeader = &LowFragHeap->UserBlockCache.UserBlocks[UserBlock->SizeIndex - HEAP_LOWEST_USER_SIZE_INDEX];
  605. if (UserBlock->SizeIndex == 0) {
  606. //
  607. // This block was converted before to NT heap block
  608. //
  609. HeapFree(LowFragHeap->Heap, 0, UserBlock);
  610. return;
  611. }
  612. LFHEAPASSERT(UserBlock->SizeIndex >= HEAP_LOWEST_USER_SIZE_INDEX);
  613. LFHEAPASSERT(UserBlock->SizeIndex <= HEAP_HIGHEST_USER_SIZE_INDEX);
  614. LFHEAPASSERT( RtlpConvertSizeIndexToSize((UCHAR)UserBlock->SizeIndex) ==
  615. HeapSize(LowFragHeap->Heap, 0, UserBlock) );
  616. Depth = QueryDepthSList(&LowFragHeap->UserBlockCache.UserBlocks[SizeIndex - HEAP_LOWEST_USER_SIZE_INDEX]);
  617. if ((Depth > HEAP_CACHE_FREE_THRESHOLD)
  618. &&
  619. (Depth > (LowFragHeap->UserBlockCache.AvailableBlocks[ SizeIndex - HEAP_LOWEST_USER_SIZE_INDEX ] >> HEAP_CACHE_SHIFT_THRESHOLD))) {
  620. PVOID ListEntry;
  621. HeapFree(LowFragHeap->Heap, 0, UserBlock);
  622. ListEntry = NULL;
  623. __try {
  624. ListEntry = RtlpSubSegmentPop(&LowFragHeap->UserBlockCache.UserBlocks[SizeIndex - HEAP_LOWEST_USER_SIZE_INDEX]);
  625. } __except (EXCEPTION_EXECUTE_HANDLER) {
  626. }
  627. if (ListEntry != NULL) {
  628. UserBlock = CONTAINING_RECORD(ListEntry, HEAP_USERDATA_HEADER, SFreeListEntry);
  629. HeapFree(LowFragHeap->Heap, 0, UserBlock);
  630. InterlockedDecrement(&LowFragHeap->UserBlockCache.AvailableBlocks[ SizeIndex - HEAP_LOWEST_USER_SIZE_INDEX ]);
  631. }
  632. InterlockedDecrement(&LowFragHeap->UserBlockCache.AvailableBlocks[ SizeIndex - HEAP_LOWEST_USER_SIZE_INDEX ]);
  633. } else {
  634. RtlpSubSegmentPush( ListHeader,
  635. &UserBlock->SFreeListEntry);
  636. }
  637. }
  638. VOID
  639. FORCEINLINE
  640. RtlpMarkLFHBlockBusy (
  641. PHEAP_ENTRY Block
  642. )
  643. /*++
  644. Routine Description:
  645. This function marks a LFH block as busy. Because the convert routine can
  646. be invoked any time, the LFH cannot use the same flag as the regular heap
  647. (LFH access any fields unsynchronized, but the block flags are supposed to
  648. be accessed while holding the heap lock)
  649. Arguments:
  650. Block - The block being marked as busy
  651. Return Value:
  652. None
  653. --*/
  654. {
  655. Block->SmallTagIndex = 1;
  656. }
  657. VOID
  658. FORCEINLINE
  659. RtlpMarkLFHBlockFree (
  660. PHEAP_ENTRY Block
  661. )
  662. /*++
  663. Routine Description:
  664. This function marks a LFH block as free. Because the convert routine can
  665. be invoked any time, the LFH cannot use the same flag as the regular heap
  666. (LFH access any fields unsynchronized, but the block flags are supposed to
  667. be accessed while holding the heap lock)
  668. Arguments:
  669. Block - The block to be marked free
  670. Return Value:
  671. None
  672. --*/
  673. {
  674. Block->SmallTagIndex = 0;
  675. }
  676. BOOLEAN
  677. FORCEINLINE
  678. RtlpIsLFHBlockBusy (
  679. PHEAP_ENTRY Block
  680. )
  681. /*++
  682. Routine Description:
  683. The function returns whether the block is busy or free
  684. Arguments:
  685. Block - The heap block tested
  686. Return Value:
  687. Return TRUE if the block is busy
  688. --*/
  689. {
  690. return (Block->SmallTagIndex == 1);
  691. }
  692. VOID
  693. FORCEINLINE
  694. RtlpUpdateLastEntry (
  695. PHEAP Heap,
  696. PHEAP_ENTRY Block
  697. )
  698. /*++
  699. Routine Description:
  700. The function updates the last entry in segment. This is mandatory each time
  701. a new block become the last.
  702. Arguments:
  703. Heap - The NT heap structure
  704. Block - The block being tested for LAST_ENTRY flag
  705. Return Value:
  706. None
  707. --*/
  708. {
  709. if (Block->Flags & HEAP_ENTRY_LAST_ENTRY) {
  710. PHEAP_SEGMENT Segment;
  711. Segment = Heap->Segments[Block->SegmentIndex];
  712. Segment->LastEntryInSegment = Block;
  713. }
  714. }
  715. BOOLEAN
  716. FORCEINLINE
  717. RtlpIsSubSegmentEmpty(
  718. PHEAP_SUBSEGMENT SubSegment
  719. )
  720. /*++
  721. Routine Description:
  722. This function tests whether the subsegment does contain available sub-blocks
  723. Arguments:
  724. SubSegment - The subsegment being tested
  725. Return Value:
  726. TRUE if no blocks are available.
  727. --*/
  728. {
  729. return SubSegment->AggregateExchg.OffsetAndDepth == (NO_MORE_ENTRIES << 16);
  730. }
  731. VOID
  732. FORCEINLINE
  733. RtlpUpdateBucketCounters (
  734. PHEAP_BUCKET Bucket,
  735. LONG TotalBlocks
  736. )
  737. /*++
  738. Routine Description:
  739. The function updates the total number of blocks from a bucket and the
  740. number of sub-segments with a single interlocked operation. This function should be
  741. called each time a new segment is allocated / deleted to/from this bucket
  742. Arguments:
  743. Bucket - The heap bucket that needs to be updated
  744. TotalBlocks - The number of blocks added / subtracted from the bucket. A positive
  745. value means the bucket increased with a new segment, and a positive value
  746. means a sub-segment with that many blocks was deleted.
  747. Return Value:
  748. None
  749. --*/
  750. {
  751. HEAP_BUCKET_COUNTERS CapturedValue, NewValue;
  752. LFH_DECLARE_COUNTER;
  753. do {
  754. //
  755. // Capture the current value for counters
  756. //
  757. CapturedValue.Aggregate64 = Bucket->Counters.Aggregate64;
  758. //
  759. // Calculate the new value depending upon the captured state
  760. //
  761. NewValue.TotalBlocks = CapturedValue.TotalBlocks + TotalBlocks;
  762. if (TotalBlocks > 0) {
  763. NewValue.SubSegmentCounts = CapturedValue.SubSegmentCounts + 1;
  764. } else {
  765. NewValue.SubSegmentCounts = CapturedValue.SubSegmentCounts - 1;
  766. }
  767. LFH_UPDATE_COUNTER;
  768. //
  769. // try to replace the original value with the current one. If the
  770. // lockcomp below fails, retry all the ops above
  771. //
  772. } while ( !LOCKCOMP64(&Bucket->Counters.Aggregate64, NewValue.Aggregate64, CapturedValue.Aggregate64) );
  773. //
  774. // It's invalid to have negative numbers of blocks or sub-segments
  775. //
  776. LFHEAPASSERT(((LONG)NewValue.SubSegmentCounts) >= 0);
  777. LFHEAPASSERT(((LONG)NewValue.TotalBlocks) >= 0);
  778. }
  779. ULONG
  780. FORCEINLINE
  781. RtlpGetThreadAffinityIndex(
  782. PHEAP_BUCKET HeapBucket
  783. )
  784. /*++
  785. Routine Description:
  786. The affinity is managed independenlty on each bucket. This will spin up the number of sub-segments
  787. that can be accessed simultanely only for most used buckets.
  788. The routinw will hash the thread ID givving the right affinity index depending
  789. the affinity size for that bucket.
  790. Arguments:
  791. Bucket - The heap bucket queried
  792. Return Value:
  793. The affinity the current thread should use for allocation from this bucket
  794. --*/
  795. {
  796. if (HeapBucket->UseAffinity) {
  797. return 1 + RtlpGetThreadAffinity();
  798. }
  799. return 0;
  800. }
  801. BOOLEAN
  802. FORCEINLINE
  803. RtlpIsSubSegmentLocked(
  804. PHEAP_SUBSEGMENT SubSegment,
  805. ULONG LockMask
  806. )
  807. /*++
  808. Routine Description:
  809. This function tests whether the subsegment has the given lock bits set
  810. Arguments:
  811. SubSegment - The sub-segment tested
  812. LockMask - contains the bits to be tested
  813. Return Value:
  814. It returns false if any bit from the mask is not set
  815. --*/
  816. {
  817. return ((SubSegment->Lock & LockMask) == LockMask);
  818. }
  819. BOOLEAN
  820. LOCALPROC
  821. RtlpAddToSegmentInfo(
  822. PHEAP_LOCAL_DATA LocalData,
  823. IN PHEAP_LOCAL_SEGMENT_INFO SegmentInfo,
  824. IN PHEAP_SUBSEGMENT NewItem
  825. )
  826. {
  827. ULONG Index;
  828. for (Index = 0; Index < FREE_CACHE_SIZE; Index++) {
  829. ULONG i = (Index + (ULONG)SegmentInfo->LastUsed) & (FREE_CACHE_SIZE - 1);
  830. PHEAP_SUBSEGMENT CrtSubSegment = SegmentInfo->CachedItems[i];
  831. if (CrtSubSegment == NULL ) {
  832. if (InterlockedCompareExchangePointer( &SegmentInfo->CachedItems[i], NewItem, NULL) == NULL) {
  833. SegmentInfo->BusyEntries += 1;
  834. return TRUE;
  835. }
  836. } else {
  837. if (!RtlpIsSubSegmentLocked(CrtSubSegment, HEAP_USERDATA_LOCK)) {
  838. if (InterlockedCompareExchangePointer( &SegmentInfo->CachedItems[i], NewItem, CrtSubSegment) == CrtSubSegment) {
  839. RtlpUnlockSubSegment(LocalData, CrtSubSegment, HEAP_PUBLIC_LOCK);
  840. return TRUE;
  841. }
  842. }
  843. }
  844. }
  845. return FALSE;
  846. }
  847. PHEAP_SUBSEGMENT
  848. LOCALPROC
  849. RtlpRemoveFromSegmentInfo(
  850. PHEAP_LOCAL_DATA LocalData,
  851. IN PHEAP_LOCAL_SEGMENT_INFO SegmentInfo
  852. )
  853. {
  854. ULONG i;
  855. PHEAP_SUBSEGMENT * Location = NULL;
  856. ULONG LargestDepth = 0;
  857. PHEAP_SUBSEGMENT CapturedSegment;
  858. RETRY:
  859. for (i = 0; i < FREE_CACHE_SIZE; i++) {
  860. ULONG Depth;
  861. PHEAP_SUBSEGMENT CrtSubsegment = SegmentInfo->CachedItems[i];
  862. if ( CrtSubsegment
  863. &&
  864. (Depth = CrtSubsegment->AggregateExchg.Depth) > LargestDepth) {
  865. CapturedSegment = CrtSubsegment;
  866. LargestDepth = Depth;
  867. Location = &SegmentInfo->CachedItems[i];
  868. }
  869. }
  870. if (Location) {
  871. PHEAP_SUBSEGMENT NextEntry;
  872. while (NextEntry = (PHEAP_SUBSEGMENT)RtlpSubSegmentPop(&SegmentInfo->SListHeader)) {
  873. NextEntry = CONTAINING_RECORD(NextEntry, HEAP_SUBSEGMENT, SFreeListEntry);
  874. #ifdef _HEAP_DEBUG
  875. NextEntry->SFreeListEntry.Next = NULL;
  876. #endif
  877. if (RtlpIsSubSegmentLocked(NextEntry, HEAP_USERDATA_LOCK)) {
  878. break;
  879. }
  880. RtlpUnlockSubSegment(LocalData, NextEntry, HEAP_PUBLIC_LOCK);
  881. }
  882. if (InterlockedCompareExchangePointer( Location, NextEntry, CapturedSegment) == CapturedSegment) {
  883. if (NextEntry == NULL) {
  884. SegmentInfo->BusyEntries -= 1;
  885. SegmentInfo->LastUsed = Location - &SegmentInfo->CachedItems[0];
  886. LFHEAPASSERT(SegmentInfo->LastUsed < FREE_CACHE_SIZE);
  887. }
  888. return CapturedSegment;
  889. } else if (NextEntry){
  890. RtlpSubSegmentPush( &SegmentInfo->SListHeader,
  891. &NextEntry->SFreeListEntry);
  892. }
  893. Location = NULL;
  894. LargestDepth = 0;
  895. goto RETRY;
  896. }
  897. return NULL;
  898. }
  899. PHEAP_SUBSEGMENT
  900. LOCALPROC
  901. RtlpRemoveFreeSubSegment(
  902. PHEAP_LOCAL_DATA LocalData,
  903. ULONG SizeIndex
  904. )
  905. /*++
  906. Routine Description:
  907. This function remove a sub-segments that has free sub-blocks from the free list.
  908. Arguments:
  909. LowFragHeap - The Low Fragmentation Heap handle
  910. HeapBucket - The bucket where we need to reuse a free block
  911. Return Value:
  912. A subsegment that contains free blocks
  913. --*/
  914. {
  915. PVOID Entry;
  916. LONG Depth;
  917. PHEAP_LOCAL_SEGMENT_INFO FreeSList;
  918. PHEAP_SUBSEGMENT SubSegment;
  919. SubSegment = RtlpRemoveFromSegmentInfo(LocalData, &LocalData->SegmentInfo[SizeIndex]);
  920. if (SubSegment) {
  921. if ( RtlpUnlockSubSegment(LocalData, SubSegment, HEAP_PUBLIC_LOCK)){
  922. return SubSegment;
  923. }
  924. }
  925. FreeSList = &LocalData->SegmentInfo[SizeIndex];
  926. while (Entry = RtlpSubSegmentPop(&FreeSList->SListHeader) ) {
  927. SubSegment = CONTAINING_RECORD(Entry, HEAP_SUBSEGMENT, SFreeListEntry);
  928. #ifdef _HEAP_DEBUG
  929. SubSegment->SFreeListEntry.Next = NULL;
  930. #endif
  931. LFHEAPASSERT( RtlpIsSubSegmentLocked(SubSegment, HEAP_PUBLIC_LOCK) );
  932. LFHEAPASSERT( SizeIndex == SubSegment->SizeIndex );
  933. //
  934. // If we have a non-empty subsegments we'll return it
  935. //
  936. if ( RtlpUnlockSubSegment(LocalData, SubSegment, HEAP_PUBLIC_LOCK)
  937. &&
  938. (SubSegment->AggregateExchg.Depth != 0)) {
  939. return SubSegment;
  940. }
  941. }
  942. return NULL;
  943. }
  944. BOOLEAN
  945. LOCALPROC
  946. RtlpInsertFreeSubSegment(
  947. PHEAP_LOCAL_DATA LocalData,
  948. PHEAP_SUBSEGMENT SubSegment
  949. )
  950. /*++
  951. Routine Description:
  952. The function inserts a Subsegments that has certain number of free blocks into the list
  953. with free segments. The insertion is done according with the current thread affinity.
  954. Arguments:
  955. SubSegment - The sub-segment being inserted into the bucket's free list
  956. Return Value:
  957. TRUE if succeeds. False if someone else inserted the segment meanwhile, of freed it
  958. --*/
  959. {
  960. if ( RtlpLockSubSegment(SubSegment, HEAP_PUBLIC_LOCK) ) {
  961. PHEAP_LOCAL_SEGMENT_INFO FreeSList;
  962. if (RtlpAddToSegmentInfo(LocalData, &LocalData->SegmentInfo[SubSegment->SizeIndex], SubSegment)) {
  963. return TRUE;
  964. }
  965. FreeSList = &LocalData->SegmentInfo[SubSegment->SizeIndex];
  966. #ifdef _HEAP_DEBUG
  967. InterlockedIncrement(&LocalData->LowFragHeap->SegmentInsertInFree);
  968. #endif
  969. LFHEAPASSERT( RtlpIsSubSegmentLocked(SubSegment, HEAP_PUBLIC_LOCK) );
  970. LFHEAPASSERT( SubSegment->SFreeListEntry.Next == NULL );
  971. RtlpSubSegmentPush( &FreeSList->SListHeader,
  972. &SubSegment->SFreeListEntry);
  973. return TRUE;
  974. }
  975. return FALSE;
  976. }
  977. BOOLEAN
  978. LOCALPROC
  979. RtlpTrySetActiveSubSegment (
  980. PHEAP_LOCAL_DATA LocalData,
  981. PHEAP_BUCKET HeapBucket,
  982. PHEAP_SUBSEGMENT SubSegment
  983. )
  984. /*++
  985. Routine Description:
  986. This function tries to elevate the active segment into an active state. An active state is
  987. defined here the state where a segment is used for allocations. There is no guarantee a segment
  988. can be set into an active state because of non-blocking algorithms. An other thread can free
  989. it meanwhile or set it before. In these cases the function will fail.
  990. Arguments:
  991. LowFragHeap - The LFH pointer
  992. HeapBucket - The bucket containing the active sub-segment
  993. Affinity - the required affinity for that segment
  994. SubSegment - The subsegment being activated. If NULL, this function will remove only
  995. the current active segment.
  996. Return Value:
  997. TRUE if succeeds.
  998. --*/
  999. {
  1000. PHEAP_SUBSEGMENT PreviousSubSegment;
  1001. if (SubSegment) {
  1002. //
  1003. // If we received a sub-segment we need to lock it exclusively in order
  1004. // to protect against other threads trying to do the same thing
  1005. // at the same time
  1006. //
  1007. if ( !RtlpLockSubSegment(SubSegment, HEAP_ACTIVE_LOCK | HEAP_PUBLIC_LOCK) ) {
  1008. return FALSE;
  1009. }
  1010. //
  1011. // We have granted exclusive access to this sub-segment at this point.
  1012. // We need to test whether this subsegment wasn't freed meanwhile and reused
  1013. // for other allocations (for a different bucket)
  1014. //
  1015. if (SubSegment->Bucket != HeapBucket) {
  1016. //
  1017. // Someone freed it before and reuse it. We need to back out
  1018. // whatever we've done before. We cannot insert it into this bucket,
  1019. // since it contains different block sizes
  1020. //
  1021. if (RtlpUnlockSubSegment(LocalData, SubSegment, HEAP_ACTIVE_LOCK | HEAP_PUBLIC_LOCK)) {
  1022. if (SubSegment->AggregateExchg.Depth) {
  1023. RtlpInsertFreeSubSegment(LocalData, SubSegment);
  1024. }
  1025. }
  1026. return FALSE;
  1027. }
  1028. LFHEAPASSERT( SubSegment->SFreeListEntry.Next == NULL );
  1029. LFHEAPASSERT( HeapBucket == SubSegment->Bucket);
  1030. LFHEAPASSERT( RtlpIsSubSegmentLocked(SubSegment, HEAP_PUBLIC_LOCK));
  1031. #ifdef _HEAP_DEBUG
  1032. SubSegment->SFreeListEntry.Next = (PSINGLE_LIST_ENTRY)(ULONG_PTR)0xEEEEEEEE;
  1033. #endif
  1034. LFHEAPASSERT( SubSegment->AffinityIndex == (UCHAR)LocalData->Affinity );
  1035. }
  1036. //
  1037. // Try to set this sub-segment as active an capture the previous active segment
  1038. //
  1039. do {
  1040. PreviousSubSegment = *((PHEAP_SUBSEGMENT volatile *)&LocalData->SegmentInfo[HeapBucket->SizeIndex].ActiveSubsegment);
  1041. } while ( InterlockedCompareExchangePointer( &LocalData->SegmentInfo[HeapBucket->SizeIndex].ActiveSubsegment,
  1042. SubSegment,
  1043. PreviousSubSegment) != PreviousSubSegment );
  1044. if ( PreviousSubSegment ) {
  1045. //
  1046. // We had a previous active segment. We need to unlock it, and if it has enough
  1047. // free space we'll mark it ready for reuse
  1048. //
  1049. LFHEAPASSERT( HeapBucket == PreviousSubSegment->Bucket );
  1050. LFHEAPASSERT( RtlpIsSubSegmentLocked(PreviousSubSegment, HEAP_PUBLIC_LOCK) );
  1051. LFHEAPASSERT( PreviousSubSegment->SFreeListEntry.Next == ((PSINGLE_LIST_ENTRY)(ULONG_PTR)0xEEEEEEEE) );
  1052. #ifdef _HEAP_DEBUG
  1053. PreviousSubSegment->SFreeListEntry.Next = 0;
  1054. #endif
  1055. if (RtlpUnlockSubSegment(LocalData, PreviousSubSegment, HEAP_ACTIVE_LOCK | HEAP_PUBLIC_LOCK)) {
  1056. //
  1057. // That was not the last lock reference for that sub-segment.
  1058. //
  1059. if (PreviousSubSegment->AggregateExchg.Depth) {
  1060. RtlpInsertFreeSubSegment(LocalData, PreviousSubSegment);
  1061. }
  1062. }
  1063. }
  1064. #ifdef _HEAP_DEBUG
  1065. LocalData->LowFragHeap->SegmentChange++;
  1066. #endif
  1067. return TRUE;
  1068. }
  1069. VOID
  1070. FASTCALL
  1071. RtlpSubSegmentInitialize (
  1072. IN PLFH_HEAP LowFragHeap,
  1073. IN PHEAP_SUBSEGMENT SubSegment,
  1074. IN PHEAP_USERDATA_HEADER UserBuffer,
  1075. IN SIZE_T BlockSize,
  1076. IN SIZE_T AllocatedSize,
  1077. IN PVOID Bucket
  1078. )
  1079. /*++
  1080. Routine Description:
  1081. The routine initialize a sub-segment descriptor.
  1082. N.B. The Sub-Segment structure can be accessed simultanely by some other threads
  1083. that captured it to allocate, but they were suspended before alloc completed. If meanwhile
  1084. the sub-segment was deleted, the descriptor can be reused for a new subblock.
  1085. Arguments:
  1086. SubSegment - The sub-segment structure being initialized
  1087. UserBuffer - The block allocated for the user data
  1088. BlockSize - The size of each sub-block
  1089. AllocatedSize - the size of the allocated buffer
  1090. Bucket - The bucket that will own this heap sub-segment
  1091. Return Value:
  1092. None
  1093. --*/
  1094. {
  1095. ULONG i, NumItems;
  1096. PVOID Buffer = UserBuffer + 1;
  1097. PBLOCK_ENTRY BlockEntry;
  1098. USHORT BlockUnits;
  1099. USHORT CrtBlockOffset = 0;
  1100. INTERLOCK_SEQ CapturedValue, NewValue;
  1101. CapturedValue.Exchg = SubSegment->AggregateExchg.Exchg;
  1102. //
  1103. // Add the block header overhead
  1104. //
  1105. BlockSize += sizeof(HEAP_ENTRY);
  1106. BlockUnits = (USHORT)(BlockSize >> HEAP_GRANULARITY_SHIFT);
  1107. //
  1108. // The debug version will check the state for the subsegment
  1109. // testing whether the state was modified
  1110. //
  1111. LFHEAPASSERT(((PHEAP_BUCKET)Bucket)->BlockUnits == BlockUnits);
  1112. LFHEAPASSERT(SubSegment->Lock == 0);
  1113. LFHEAPASSERT(CapturedValue.OffsetAndDepth == (NO_MORE_ENTRIES << 16));
  1114. LFHEAPASSERT(SubSegment->UserBlocks == NULL);
  1115. LFHEAPASSERT(SubSegment->SFreeListEntry.Next == 0);
  1116. //
  1117. // Initialize the user segment. Note that we don't touch the
  1118. // sub-segment descriptor, as some other threads can still use it.
  1119. //
  1120. UserBuffer->SubSegment = SubSegment;
  1121. UserBuffer->HeapHandle = LowFragHeap->Heap;
  1122. NumItems = (ULONG)((AllocatedSize - sizeof(HEAP_USERDATA_HEADER)) / BlockSize);
  1123. CrtBlockOffset = sizeof(HEAP_USERDATA_HEADER) >> HEAP_GRANULARITY_SHIFT;
  1124. NewValue.FreeEntryOffset = CrtBlockOffset;
  1125. for (i = 0; i < NumItems; i++) {
  1126. BlockEntry = (PBLOCK_ENTRY) Buffer;
  1127. //
  1128. // Initialize the block
  1129. //
  1130. BlockEntry->SubSegment = SubSegment;
  1131. BlockEntry->SegmentIndex = HEAP_LFH_INDEX;
  1132. //
  1133. // Points to the next free block
  1134. //
  1135. CrtBlockOffset += BlockUnits;
  1136. Buffer = (PCHAR)Buffer + BlockSize;
  1137. BlockEntry->LinkOffset = CrtBlockOffset;
  1138. BlockEntry->Flags = HEAP_ENTRY_BUSY;
  1139. BlockEntry->UnusedBytes = sizeof(HEAP_ENTRY);
  1140. RtlpMarkLFHBlockFree( (PHEAP_ENTRY)BlockEntry );
  1141. #if defined(_WIN64)
  1142. BlockEntry->SubSegment = SubSegment;
  1143. #endif
  1144. #ifdef _HEAP_DEBUG
  1145. BlockEntry->Reserved2 = 0xFEFE;
  1146. #endif
  1147. }
  1148. //
  1149. // Mark the last block from the list
  1150. //
  1151. BlockEntry->LinkOffset = NO_MORE_ENTRIES;
  1152. SubSegment->BlockSize = BlockUnits;
  1153. SubSegment->BlockCount = (USHORT)NumItems;
  1154. SubSegment->Bucket = Bucket;
  1155. SubSegment->SizeIndex = ((PHEAP_BUCKET)Bucket)->SizeIndex;
  1156. //
  1157. // Determine the thresholds depending upon the total number of blocks
  1158. //
  1159. SubSegment->UserBlocks = UserBuffer;
  1160. RtlpUpdateBucketCounters(Bucket, NumItems);
  1161. NewValue.Depth = (USHORT)NumItems;
  1162. NewValue.Sequence = CapturedValue.Sequence + 1;
  1163. SubSegment->Lock = HEAP_USERDATA_LOCK;
  1164. //
  1165. // At this point everything is set, so we can with an interlocked operation set the
  1166. // entire slist to the segment.
  1167. //
  1168. if (!LOCKCOMP64(&SubSegment->AggregateExchg.Exchg, NewValue, CapturedValue)) {
  1169. //
  1170. // Someone changed the state for the heap structure, so the
  1171. // initialization failed. We make noise in the debug version.
  1172. // (This should never happen)
  1173. //
  1174. LFHEAPASSERT( FALSE );
  1175. }
  1176. }
  1177. VOID
  1178. LOCALPROC
  1179. RtlpFreeUserBuffer(
  1180. PLFH_HEAP LowFragHeap,
  1181. PHEAP_SUBSEGMENT SubSegment
  1182. )
  1183. /*++
  1184. Routine Description:
  1185. When all blocks within segment are free we can go ahead and free the whole user buffer
  1186. The caller should receive a notification from the last free call that the sub-segment
  1187. does not have any allocated block
  1188. Arguments:
  1189. LowFragHeap - The LFH
  1190. SubSegment - The sub-segment being released
  1191. Return Value:
  1192. None.
  1193. --*/
  1194. {
  1195. PHEAP_BUCKET HeapBucket;
  1196. SIZE_T UserBlockSize;
  1197. HeapBucket = (PHEAP_BUCKET)SubSegment->Bucket;
  1198. LFHEAPASSERT( RtlpIsSubSegmentLocked(SubSegment, HEAP_USERDATA_LOCK) );
  1199. #ifdef _HEAP_DEBUG
  1200. UserBlockSize = HeapSize(LowFragHeap->Heap, 0, (PVOID)SubSegment->UserBlocks);
  1201. LFHEAPASSERT((LONG_PTR)UserBlockSize > 0);
  1202. #endif
  1203. SubSegment->UserBlocks->Signature = 0;
  1204. RtlpFreeUserBlock(LowFragHeap, (PHEAP_USERDATA_HEADER)SubSegment->UserBlocks);
  1205. //
  1206. // Update the counters
  1207. //
  1208. RtlpUpdateBucketCounters (HeapBucket, -SubSegment->BlockCount);
  1209. SubSegment->UserBlocks = NULL;
  1210. LFHEAPASSERT( RtlpIsSubSegmentLocked(SubSegment, HEAP_USERDATA_LOCK) );
  1211. //
  1212. // This is a slow path any way. It doesn't harm a rare global interlocked
  1213. // in order to estimate the frequency of slower calls
  1214. //
  1215. InterlockedIncrement(&LowFragHeap->SegmentDelete);
  1216. }
  1217. BOOLEAN
  1218. FORCEINLINE
  1219. RtlpLockSubSegment(
  1220. PHEAP_SUBSEGMENT SubSegment,
  1221. ULONG LockMask
  1222. )
  1223. /*++
  1224. Routine Description:
  1225. The function locks a given set of bits to that segment, with a single atomic operation.
  1226. If any of the bits is already locked the function will fail. If the sub-segment is deleted
  1227. it will fail too.
  1228. Arguments:
  1229. SubSegment - The SubSegemnt being locked
  1230. LockMask - An ULONG value specifying the bits needed locked
  1231. Return Value:
  1232. TRUE if succeeds.
  1233. --*/
  1234. {
  1235. ULONG CapturedLock;
  1236. do {
  1237. CapturedLock = *((ULONG volatile *)&SubSegment->Lock);
  1238. if ((CapturedLock == 0)
  1239. ||
  1240. (CapturedLock & LockMask)) {
  1241. return FALSE;
  1242. }
  1243. } while ( InterlockedCompareExchange((PLONG)&SubSegment->Lock, CapturedLock | LockMask, CapturedLock) != CapturedLock );
  1244. return TRUE;
  1245. }
  1246. BOOLEAN
  1247. LOCALPROC
  1248. RtlpUnlockSubSegment(
  1249. PHEAP_LOCAL_DATA LocalData,
  1250. PHEAP_SUBSEGMENT SubSegment,
  1251. ULONG LockMask
  1252. )
  1253. /*++
  1254. Routine Description:
  1255. The function unlocks the given sub-segment. If the last lock went away, the segment
  1256. descriptor will be deleted and inserted into the recycle queue to be reused
  1257. further for other allocations.
  1258. Arguments:
  1259. LowFragHeap - The LFH
  1260. SubSegment - The sub-segment being unlocked
  1261. LockMask - the bits that will be released
  1262. Return Value:
  1263. Returns false if unlocking the segment caused deletion. TRUE if there are still
  1264. other locks keeping this sub-segment descriptor alive.
  1265. --*/
  1266. {
  1267. ULONG CapturedLock;
  1268. do {
  1269. CapturedLock = *((ULONG volatile *)&SubSegment->Lock);
  1270. //
  1271. // Unlock can be called exclusively, ONLY the if lock operation succeded
  1272. // It's an invalid state to have the segment already unlocked
  1273. // We assert this in debug version
  1274. //
  1275. LFHEAPASSERT((CapturedLock & LockMask) == LockMask);
  1276. } while ( InterlockedCompareExchange((PLONG)&SubSegment->Lock, CapturedLock & ~LockMask, CapturedLock) != CapturedLock );
  1277. //
  1278. // If That was the last lock released, we go ahead and
  1279. // free the sub-segment to the SLists
  1280. //
  1281. if (CapturedLock == LockMask) {
  1282. SubSegment->Bucket = NULL;
  1283. SubSegment->AggregateExchg.Sequence += 1;
  1284. LFHEAPASSERT( RtlpIsSubSegmentEmpty(SubSegment) );
  1285. LFHEAPASSERT(SubSegment->Lock == 0);
  1286. LFHEAPASSERT(SubSegment->SFreeListEntry.Next == 0);
  1287. RtlpSubSegmentPush(&LocalData->DeletedSubSegments, &SubSegment->SFreeListEntry);
  1288. return FALSE;
  1289. }
  1290. return TRUE;
  1291. }
  1292. PVOID
  1293. LOCALPROC
  1294. RtlpSubSegmentAllocate (
  1295. PHEAP_BUCKET HeapBucket,
  1296. PHEAP_SUBSEGMENT SubSegment
  1297. )
  1298. /*++
  1299. Routine Description:
  1300. The function allocates a block from a sub-segment with a interlocked instruction.
  1301. N.B. Since the access to this sub-segment is done unsynchronized, a tread can play
  1302. with reading some the the sub-segment fields while another thread deleted it. For this
  1303. reason the sub-segment descriptors are always allocated, so reading the interlocked
  1304. counter is consistent over with the states that produced deletion. Every delete or
  1305. init will increment the sequence counter, so the alloc will simple fail if it ends up
  1306. using a different sub-segment.
  1307. This function also handles the contention (interlocked operation failed) on this bucket.
  1308. If we have too mani concurent access on this bucket, it will spin up the affinity
  1309. limit on an MP machine.
  1310. Arguments:
  1311. HeapBucket - The bucket from which we allocate a blocks
  1312. SubSegment - The subsegment currently in use
  1313. Return Value:
  1314. The allocated block pointer, if succeeds.
  1315. --*/
  1316. {
  1317. ULONGLONG CapturedValue, NewValue;
  1318. PBLOCK_ENTRY BlockEntry;
  1319. PHEAP_USERDATA_HEADER UserBlocks;
  1320. SHORT Depth;
  1321. LFH_DECLARE_COUNTER;
  1322. RETRY:
  1323. CapturedValue = SubSegment->AggregateExchg.Exchg;
  1324. //
  1325. // We need the memory barrier because we are accessing
  1326. // another shared data below : UserBlocks
  1327. // This has to be fetched in the same order
  1328. // We declared these volatile, and on IA64 (MP) we need the
  1329. // memory barrier as well
  1330. //
  1331. RtlMemoryBarrier();
  1332. if ((Depth = (USHORT)CapturedValue)
  1333. &&
  1334. (UserBlocks = (PHEAP_USERDATA_HEADER)SubSegment->UserBlocks)
  1335. &&
  1336. (SubSegment->Bucket == HeapBucket)
  1337. &&
  1338. !RtlpIsSubSegmentLocked(SubSegment, HEAP_CONVERT_LOCK)) {
  1339. BlockEntry = (PBLOCK_ENTRY)((PCHAR)UserBlocks + ((((ULONG)CapturedValue) >> 16) << HEAP_GRANULARITY_SHIFT));
  1340. //
  1341. // Accessing BlockEntry->LinkOffset can produce an AV if another thread freed the buffer
  1342. // meanwhile and the memory was decommitted. The caller of this function should
  1343. // have a try - except around this call. If the memory was used for other blocks
  1344. // the interlockedcompare should fail because the sequence number was incremented
  1345. //
  1346. LFHEAPASSERT(!(((CapturedValue >> 16) == SubSegment->AggregateExchg.Sequence)
  1347. &&
  1348. (BlockEntry->LinkOffset != NO_MORE_ENTRIES)
  1349. &&
  1350. (BlockEntry->LinkOffset > (SubSegment->BlockCount * SubSegment->BlockSize))
  1351. &&
  1352. ((CapturedValue >> 16) == SubSegment->AggregateExchg.Sequence)));
  1353. LFHEAPASSERT(!(((CapturedValue >> 16) == SubSegment->AggregateExchg.Sequence)
  1354. &&
  1355. (BlockEntry->LinkOffset == NO_MORE_ENTRIES)
  1356. &&
  1357. (Depth != 0)
  1358. &&
  1359. ((CapturedValue >> 16) == SubSegment->AggregateExchg.Sequence)));
  1360. LFHEAPASSERT(!(((CapturedValue >> 16) == SubSegment->AggregateExchg.Sequence)
  1361. &&
  1362. (SubSegment->Bucket != HeapBucket)
  1363. &&
  1364. ((CapturedValue >> 16) == SubSegment->AggregateExchg.Sequence)));
  1365. NewValue = ((CapturedValue - 1) & (~(ULONGLONG)0xFFFF0000)) | ((ULONG)(BlockEntry->LinkOffset) << 16);
  1366. if (LOCKCOMP64(&SubSegment->AggregateExchg.Exchg, NewValue, CapturedValue)) {
  1367. //
  1368. // if the segment has been converted, the bucket will be invalid.
  1369. //
  1370. // LFHEAPASSERT(SubSegment->Bucket == HeapBucket);
  1371. // LFHEAPASSERT(RtlpIsSubSegmentLocked(SubSegment, HEAP_USERDATA_LOCK));
  1372. LFHEAPASSERT( !RtlpIsLFHBlockBusy( (PHEAP_ENTRY)BlockEntry ) );
  1373. LFHEAPASSERT(((NewValue >> 24) != NO_MORE_ENTRIES)
  1374. ||
  1375. ((USHORT)NewValue == 0));
  1376. #ifdef _HEAP_DEBUG
  1377. LFHEAPASSERT((BlockEntry->Reserved2 == 0xFFFC)
  1378. ||
  1379. (BlockEntry->Reserved2 == 0xFEFE));
  1380. //
  1381. // In the debug version write something there
  1382. //
  1383. BlockEntry->LinkOffset = 0xFFFA;
  1384. BlockEntry->Reserved2 = 0xFFFB;
  1385. #endif
  1386. RtlpMarkLFHBlockBusy( (PHEAP_ENTRY)BlockEntry );
  1387. //
  1388. // If we had an interlocked compare failure, we must have another thread playing
  1389. // with the same subsegment at the same time. If this happens to often
  1390. // we need to increase the affinity limit on this bucket.
  1391. //
  1392. return ((PHEAP_ENTRY)BlockEntry + 1);
  1393. }
  1394. } else {
  1395. return NULL;
  1396. }
  1397. if (!HeapBucket->UseAffinity) {
  1398. HeapBucket->UseAffinity = 1;
  1399. }
  1400. LFH_UPDATE_COUNTER;
  1401. goto RETRY;
  1402. return NULL;
  1403. }
  1404. ULONG
  1405. LOCALPROC
  1406. RtlpSubSegmentFree (
  1407. PLFH_HEAP LowfHeap,
  1408. PHEAP_SUBSEGMENT SubSegment,
  1409. PBLOCK_ENTRY BlockEntry
  1410. )
  1411. /*++
  1412. Routine Description:
  1413. This function frees a block from a sub-segment. Because a sub-segment lives
  1414. as long as there is at least an allocated block inside, we don't have the problem
  1415. we have for alloc.
  1416. If the block being freed is happening to be the last one, we'll mark with an
  1417. interlocked instruction the whole sub-segment as being free. The caller needs then to
  1418. release the descriptor structure
  1419. Arguments:
  1420. SubSegment - The subsegment that ownd the block
  1421. BlockEntry - The block being free.
  1422. Return Value:
  1423. TRUE if that was the last block, and it's safe now to recycle the descriptor.
  1424. FALSE otherwise.
  1425. --*/
  1426. {
  1427. ULONGLONG CapturedValue, NewValue;
  1428. ULONG ReturnStatus;
  1429. ULONG_PTR UserBlocksRef = (ULONG_PTR)SubSegment->UserBlocks;
  1430. LFH_DECLARE_COUNTER;
  1431. LFHEAPASSERT( RtlpIsLFHBlockBusy((PHEAP_ENTRY)BlockEntry) );
  1432. RtlpMarkLFHBlockFree((PHEAP_ENTRY)BlockEntry);
  1433. do {
  1434. LFH_UPDATE_COUNTER;
  1435. //
  1436. // We need to capture the sequence at the first step
  1437. // Then we'll capture the other fields from the segment
  1438. // If interlock operation below succeeds, means that none
  1439. // of the sub-segment fields (UserBlocks, Bucket ....)
  1440. // we changed. So the new state was built upon a consistent state
  1441. //
  1442. CapturedValue = SubSegment->AggregateExchg.Exchg;
  1443. RtlMemoryBarrier();
  1444. NewValue = (CapturedValue + 0x100000001) & (~(ULONGLONG)0xFFFF0000);
  1445. if (RtlpIsSubSegmentLocked(SubSegment, HEAP_CONVERT_LOCK)
  1446. ||
  1447. !RtlpIsSubSegmentLocked(SubSegment, HEAP_USERDATA_LOCK)
  1448. ||
  1449. (BlockEntry->SegmentIndex != HEAP_LFH_INDEX)) {
  1450. return HEAP_FREE_BLOCK_CONVERTED;
  1451. }
  1452. //
  1453. // Depth and FreeEntryOffset are fetched at at the same time. They need
  1454. // to be consistent
  1455. //
  1456. LFHEAPASSERT(!(((USHORT)CapturedValue > 1) && (((ULONG)(NewValue >> 16)) == NO_MORE_ENTRIES)));
  1457. if ((((USHORT)NewValue) != SubSegment->BlockCount)) {
  1458. ReturnStatus = HEAP_FREE_BLOCK_SUCCESS;
  1459. BlockEntry->LinkOffset = (USHORT)(CapturedValue >> 16);
  1460. NewValue |= ((((ULONG_PTR)BlockEntry - UserBlocksRef) >> HEAP_GRANULARITY_SHIFT) << 16);
  1461. } else {
  1462. //
  1463. // This was the last block. Instead pushing it into the list
  1464. // we'll take the all blocks from the sub-segment to allow releasing the
  1465. // subsegment
  1466. //
  1467. ReturnStatus = HEAP_FREE_SEGMENT_EMPTY;
  1468. NewValue = (NewValue & 0xFFFFFFFF00000000) | 0xFFFF0000;
  1469. }
  1470. } while ( !LOCKCOMP64(&SubSegment->AggregateExchg.Exchg, NewValue, CapturedValue) );
  1471. if (!(USHORT)CapturedValue/*
  1472. &&
  1473. !RtlpIsSubSegmentLocked(SubSegment, HEAP_PUBLIC_LOCK)*/) {
  1474. RtlpInsertFreeSubSegment(&LowfHeap->LocalData[SubSegment->AffinityIndex], SubSegment);
  1475. }
  1476. return ReturnStatus;
  1477. }
  1478. PHEAP_BUCKET
  1479. FORCEINLINE
  1480. RtlpGetBucket(
  1481. PLFH_HEAP LowFragHeap,
  1482. SIZE_T Index
  1483. )
  1484. /*++
  1485. Routine Description:
  1486. The function simple returns the appropriate bucket for the given allocation index.
  1487. The index should be < HEAP_BUCKETS_COUNT. This routine does not perform any range checking, it is supposed
  1488. to be called with appropriate parameters
  1489. Arguments:
  1490. LowFragHeap - The LFH
  1491. Index - The allocation index
  1492. Return Value:
  1493. The bucket that should be used for that allocation index.
  1494. --*/
  1495. {
  1496. return &LowFragHeap->Buckets[Index];
  1497. }
  1498. HANDLE
  1499. FASTCALL
  1500. RtlpCreateLowFragHeap(
  1501. HANDLE Heap
  1502. )
  1503. /*++
  1504. Routine Description:
  1505. The function creates a Low fragmentation heap, using for the allocations the heap handle
  1506. passed in.
  1507. Arguments:
  1508. Heap - The NT heap handle
  1509. Return Value:
  1510. Returns a handle to a Lof Fragmentation Heap.
  1511. --*/
  1512. {
  1513. PLFH_HEAP LowFragHeap;
  1514. ULONG i;
  1515. PUCHAR Buffer;
  1516. SIZE_T TotalSize;
  1517. //
  1518. // Determine the size of the LFH structure based upon the current affinity limit.
  1519. //
  1520. TotalSize = sizeof(LFH_HEAP) + sizeof(HEAP_LOCAL_DATA) * RtlpHeapMaxAffinity;
  1521. LowFragHeap = HeapAlloc(Heap, HEAP_NO_CACHE_BLOCK, TotalSize);
  1522. if (LowFragHeap) {
  1523. memset(LowFragHeap, 0, TotalSize);
  1524. RtlInitializeCriticalSection( &LowFragHeap->Lock );
  1525. //
  1526. // Initialize the heap zones.
  1527. //
  1528. InitializeListHead(&LowFragHeap->SubSegmentZones);
  1529. LowFragHeap->ZoneBlockSize = ROUND_UP_TO_POWER2(sizeof(HEAP_SUBSEGMENT), HEAP_GRANULARITY);
  1530. LowFragHeap->Heap = Heap;
  1531. //
  1532. // Initialize the heap buckets
  1533. //
  1534. for (i = 0; i < HEAP_BUCKETS_COUNT; i++) {
  1535. LowFragHeap->Buckets[i].UseAffinity = 0;
  1536. LowFragHeap->Buckets[i].SizeIndex = (UCHAR)i;
  1537. LowFragHeap->Buckets[i].BlockUnits = (USHORT)(RtlpBucketBlockSizes[i] >> HEAP_GRANULARITY_SHIFT) + 1;
  1538. }
  1539. for (i = 0; i <= RtlpHeapMaxAffinity; i++) {
  1540. LowFragHeap->LocalData[i].LowFragHeap = LowFragHeap;
  1541. LowFragHeap->LocalData[i].Affinity = i;
  1542. }
  1543. }
  1544. return LowFragHeap;
  1545. }
  1546. VOID
  1547. FASTCALL
  1548. RtlpDestroyLowFragHeap(
  1549. HANDLE LowFragHeapHandle
  1550. )
  1551. /*++
  1552. Routine Description:
  1553. The function should be called to destroy the LFH.
  1554. This function should be called only when the NT heap goes away. We cannot rolback
  1555. everything we've done with this heap. The NT heap is supposed to release all memory
  1556. this heap allocated.
  1557. Arguments:
  1558. LowFragHeapHandle - The low fragmentation heap
  1559. Return Value:
  1560. None.
  1561. --*/
  1562. {
  1563. //
  1564. // This cannot be called unless the entire heap will go away
  1565. // It only delete the critical section, the all blocks allocated here will
  1566. // be deleted by RltDestroyHeap when it destroys the segments.
  1567. //
  1568. RtlDeleteCriticalSection(&((PLFH_HEAP)LowFragHeapHandle)->Lock);
  1569. }
  1570. PVOID
  1571. FASTCALL
  1572. RtlpLowFragHeapAllocateFromZone(
  1573. PLFH_HEAP LowFragHeap,
  1574. ULONG Affinity
  1575. )
  1576. /*++
  1577. Routine Description:
  1578. This function allocates a sub-segment descriptor structure from the heap zone
  1579. Arguments:
  1580. LowFragHeap - the LFH
  1581. Return Value:
  1582. The pointer to a new sub-segment descriptor structure.
  1583. --*/
  1584. {
  1585. PLFH_BLOCK_ZONE CrtZone;
  1586. RETRY_ALLOC:
  1587. CrtZone = LowFragHeap->LocalData[Affinity].CrtZone;
  1588. if (CrtZone) {
  1589. PVOID CapturedFreePointer = CrtZone->FreePointer;
  1590. PVOID NextFreePointer = (PCHAR)CapturedFreePointer + LowFragHeap->ZoneBlockSize;
  1591. //
  1592. // See if we have that sub-segment already preallocated
  1593. //
  1594. if (NextFreePointer < CrtZone->Limit) {
  1595. if ( InterlockedCompareExchangePointer( &CrtZone->FreePointer,
  1596. NextFreePointer,
  1597. CapturedFreePointer) == CapturedFreePointer) {
  1598. //
  1599. // The allocation succeeded, we can return that pointer
  1600. //
  1601. return CapturedFreePointer;
  1602. }
  1603. goto RETRY_ALLOC;
  1604. }
  1605. }
  1606. //
  1607. // we need to grow the heap zone. We acquire a lock here to avoid more threads doing the
  1608. // same thing
  1609. //
  1610. RtlEnterCriticalSection(&LowFragHeap->Lock);
  1611. //
  1612. // Test whether meanwhile another thread already increased the zone
  1613. //
  1614. if (CrtZone == LowFragHeap->LocalData[Affinity].CrtZone) {
  1615. CrtZone = HeapAlloc(LowFragHeap->Heap, HEAP_NO_CACHE_BLOCK, HEAP_DEFAULT_ZONE_SIZE);
  1616. if (CrtZone == NULL) {
  1617. RtlpFlushLFHeapCache(LowFragHeap);
  1618. CrtZone = HeapAlloc(LowFragHeap->Heap, HEAP_NO_CACHE_BLOCK, HEAP_DEFAULT_ZONE_SIZE);
  1619. RtlLeaveCriticalSection(&LowFragHeap->Lock);
  1620. return NULL;
  1621. }
  1622. InsertTailList(&LowFragHeap->SubSegmentZones, &CrtZone->ListEntry);
  1623. CrtZone->Limit = (PCHAR)CrtZone + HEAP_DEFAULT_ZONE_SIZE;
  1624. CrtZone->FreePointer = CrtZone + 1;
  1625. CrtZone->FreePointer = (PVOID)ROUND_UP_TO_POWER2((ULONG_PTR)CrtZone->FreePointer, HEAP_GRANULARITY);
  1626. //
  1627. // Everything is set. We can go ahead and set this as the default zone
  1628. //
  1629. LowFragHeap->LocalData[Affinity].CrtZone = CrtZone;
  1630. }
  1631. RtlLeaveCriticalSection(&LowFragHeap->Lock);
  1632. goto RETRY_ALLOC;
  1633. }
  1634. SIZE_T
  1635. FORCEINLINE
  1636. RtlpSubSegmentGetIndex(
  1637. SIZE_T BlockUnits
  1638. )
  1639. /*++
  1640. Routine Description:
  1641. This routine converts the block size (in block units >> HEAP_GRANULARITY_SHIFT)
  1642. into heap bucket index.
  1643. Arguments:
  1644. BlockUnits - the block size >> HEAP_GRANULARITY_SHIFT
  1645. Return Value:
  1646. The index for the bucket that should handle these sizes.
  1647. --*/
  1648. {
  1649. SIZE_T SizeClass;
  1650. SIZE_T Bucket;
  1651. if (BlockUnits <= 32) {
  1652. return BlockUnits - 1;
  1653. }
  1654. SizeClass = 5; // Add 1 << 5 == 32
  1655. while (BlockUnits >> SizeClass) {
  1656. SizeClass += 1;
  1657. }
  1658. SizeClass -= 5;
  1659. BlockUnits = ROUND_UP_TO_POWER2(BlockUnits, (1 << SizeClass));
  1660. Bucket = ((SizeClass << 4) + (BlockUnits >> SizeClass) - 1);
  1661. return Bucket;
  1662. }
  1663. SIZE_T
  1664. FORCEINLINE
  1665. RtlpGetSubSegmentSizeIndex(
  1666. PLFH_HEAP LowFragHeap,
  1667. SIZE_T BlockSize,
  1668. ULONG NumBlocks,
  1669. CHAR AffinityCorrection
  1670. )
  1671. /*++
  1672. Routine Description:
  1673. This function calculate the appropriate size for a sub-segment depending upon the
  1674. block size and the minimal number of blocks that should be there.
  1675. Arguments:
  1676. BlockSize - The size of the block, in bytes
  1677. NumBlocks - the minimal number of the blocks.
  1678. Return Value:
  1679. Returns the next power of 2 size that can satisfy the request
  1680. --*/
  1681. {
  1682. SIZE_T MinSize;
  1683. ULONG SizeShift = HEAP_LOWEST_USER_SIZE_INDEX;
  1684. SIZE_T ReturnSize;
  1685. LFHEAPASSERT(AffinityCorrection < HEAP_MIN_BLOCK_CLASS);
  1686. if (BlockSize < 256) {
  1687. AffinityCorrection -= 1;
  1688. }
  1689. if (RtlpAffinityState.CrtLimit > (LONG)(RtlpHeapMaxAffinity >> 1)) {
  1690. AffinityCorrection += 1;
  1691. }
  1692. if (NumBlocks < ((ULONG)1 << (HEAP_MIN_BLOCK_CLASS - AffinityCorrection))) {
  1693. NumBlocks = 1 << (HEAP_MIN_BLOCK_CLASS - AffinityCorrection);
  1694. }
  1695. if (LowFragHeap->Conversions) {
  1696. NumBlocks = HEAP_MIN_BLOCK_CLASS;
  1697. }
  1698. if (NumBlocks > (1 << HEAP_MAX_BLOCK_CLASS)) {
  1699. NumBlocks = 1 << HEAP_MAX_BLOCK_CLASS;
  1700. }
  1701. MinSize = ((BlockSize + sizeof(HEAP_ENTRY) ) * NumBlocks) + sizeof(HEAP_USERDATA_HEADER) + sizeof(HEAP_ENTRY);
  1702. if (MinSize > HEAP_MAX_SUBSEGMENT_SIZE) {
  1703. MinSize = HEAP_MAX_SUBSEGMENT_SIZE;
  1704. }
  1705. while (MinSize >> SizeShift) {
  1706. SizeShift += 1;
  1707. }
  1708. if (SizeShift > HEAP_HIGHEST_USER_SIZE_INDEX) {
  1709. SizeShift = HEAP_HIGHEST_USER_SIZE_INDEX;
  1710. }
  1711. return SizeShift;
  1712. }
  1713. PVOID
  1714. FASTCALL
  1715. RtlpLowFragHeapAlloc(
  1716. HANDLE LowFragHeapHandle,
  1717. SIZE_T BlockSize
  1718. )
  1719. /*++
  1720. Routine Description:
  1721. This function allocates a block from the LFH.
  1722. Arguments:
  1723. Heap - the NT heap handle
  1724. LowFragHeapHandle - The LFH heap handle
  1725. BlockSize - the requested size, in bytes
  1726. Return Value:
  1727. A pointer to a new allocated block if succeeds. If the requested size is > 16K this
  1728. function will fail too.
  1729. --*/
  1730. {
  1731. SIZE_T BlockUnits;
  1732. SIZE_T Bucket;
  1733. PLFH_HEAP LowFragHeap = (PLFH_HEAP)LowFragHeapHandle;
  1734. PVOID Block;
  1735. PHEAP_LOCAL_DATA LocalData;
  1736. //
  1737. // Get the appropriate bucket depending upon the requested size
  1738. //
  1739. BlockUnits = (BlockSize + HEAP_GRANULARITY - 1) >> HEAP_GRANULARITY_SHIFT;
  1740. Bucket = RtlpSubSegmentGetIndex( BlockUnits );
  1741. if (Bucket < HEAP_BUCKETS_COUNT) {
  1742. PHEAP_BUCKET HeapBucket = RtlpGetBucket(LowFragHeap, Bucket);
  1743. SIZE_T SubSegmentSize;
  1744. SIZE_T SubSegmentSizeIndex;
  1745. PHEAP_SUBSEGMENT SubSegment, NewSubSegment;
  1746. PHEAP_USERDATA_HEADER UserData;
  1747. PHEAP_LOCAL_SEGMENT_INFO SegmentInfo;
  1748. LocalData = &LowFragHeap->LocalData[ RtlpGetThreadAffinityIndex(HeapBucket) ];
  1749. SegmentInfo = &LocalData->SegmentInfo[Bucket];
  1750. //
  1751. // If we have some memory converted to the NT heap
  1752. // we need to allocate from NT heap first in order to reuse that memory
  1753. //
  1754. //
  1755. // Try first to allocate from the last segment used for free.
  1756. // This will provide a better performance because the data is likely to
  1757. // be still in the processor cache
  1758. //
  1759. if (SubSegment = SegmentInfo->Hint) {
  1760. //
  1761. // Accessing the user data can generate an exception if another thread freed
  1762. // the subsegment meanwhile.
  1763. //
  1764. LFHEAPASSERT( LocalData->Affinity == SubSegment->AffinityIndex );
  1765. __try {
  1766. Block = RtlpSubSegmentAllocate(HeapBucket, SubSegment);
  1767. } __except (EXCEPTION_EXECUTE_HANDLER) {
  1768. Block = NULL;
  1769. }
  1770. if (Block) {
  1771. RtlpSetUnusedBytes(LowFragHeap->Heap, ((PHEAP_ENTRY)Block - 1), ( ((SIZE_T)HeapBucket->BlockUnits) << HEAP_GRANULARITY_SHIFT) - BlockSize);
  1772. return Block;
  1773. }
  1774. SegmentInfo->Hint = NULL;
  1775. }
  1776. RETRY_ALLOC:
  1777. //
  1778. // Try to allocate from the current active sub-segment
  1779. //
  1780. if (SubSegment = SegmentInfo->ActiveSubsegment) {
  1781. //
  1782. // Accessing the user data can generate an exception if another thread freed
  1783. // the subsegment meanwhile.
  1784. //
  1785. LFHEAPASSERT( LocalData->Affinity == SubSegment->AffinityIndex );
  1786. __try {
  1787. Block = RtlpSubSegmentAllocate(HeapBucket, SubSegment);
  1788. } __except (EXCEPTION_EXECUTE_HANDLER) {
  1789. Block = NULL;
  1790. }
  1791. if (Block) {
  1792. RtlpSetUnusedBytes(LowFragHeap->Heap, ((PHEAP_ENTRY)Block - 1), ( ((SIZE_T)HeapBucket->BlockUnits) << HEAP_GRANULARITY_SHIFT) - BlockSize);
  1793. return Block;
  1794. }
  1795. }
  1796. if (NewSubSegment = RtlpRemoveFreeSubSegment(LocalData, (LONG)Bucket)) {
  1797. RtlpTrySetActiveSubSegment(LocalData, HeapBucket, NewSubSegment);
  1798. goto RETRY_ALLOC;
  1799. }
  1800. if (LowFragHeap->ConvertedSpace) {
  1801. InterlockedExchangeAdd((PLONG)&LowFragHeap->ConvertedSpace, -(LONG)(BlockSize >> 1));
  1802. if ((LONG)LowFragHeap->ConvertedSpace < 0) {
  1803. LowFragHeap->ConvertedSpace = 0;
  1804. }
  1805. return NULL;
  1806. }
  1807. //
  1808. // At this point we don't have any sub-segment we can use to allocate this
  1809. // size. We need to create a new one.
  1810. //
  1811. SubSegmentSizeIndex = RtlpGetSubSegmentSizeIndex( LowFragHeap,
  1812. RtlpBucketBlockSizes[Bucket],
  1813. RtlpGetDesiredBlockNumber( HeapBucket->UseAffinity,
  1814. HeapBucket->Counters.TotalBlocks),
  1815. HeapBucket->UseAffinity
  1816. );
  1817. UserData = RtlpAllocateUserBlock( LowFragHeap, (UCHAR)SubSegmentSizeIndex );
  1818. if (UserData == NULL) {
  1819. //
  1820. // Low memory condition. Flush the caches and retry the allocation.
  1821. // On low memory, the heap will use smaller sizes for sub-segments.
  1822. //
  1823. RtlpFlushLFHeapCache(LowFragHeap);
  1824. SubSegmentSizeIndex = RtlpGetSubSegmentSizeIndex( LowFragHeap,
  1825. RtlpBucketBlockSizes[Bucket],
  1826. RtlpGetDesiredBlockNumber(HeapBucket->UseAffinity,
  1827. HeapBucket->Counters.TotalBlocks),
  1828. HeapBucket->UseAffinity
  1829. );
  1830. UserData = RtlpAllocateUserBlock( LowFragHeap, (UCHAR)SubSegmentSizeIndex );
  1831. }
  1832. if (UserData) {
  1833. PVOID Entry;
  1834. SubSegmentSize = RtlpConvertSizeIndexToSize((UCHAR)UserData->SizeIndex);
  1835. LFHEAPASSERT( SubSegmentSize == HeapSize(LowFragHeap->Heap, 0, UserData) );
  1836. //
  1837. // This is a slow path any way, and it is exercised just in rare cases,
  1838. // when a bigger sub-segment is allocated. It doesn't hurt if we have an
  1839. // extra interlocked-increment.
  1840. //
  1841. InterlockedIncrement(&LowFragHeap->SegmentCreate);
  1842. //
  1843. // Allocate a sub-segment descriptor structiure. If there isn't any in the
  1844. // recycle list we allocate one from the zones.
  1845. //
  1846. Entry = RtlpSubSegmentPop(&LocalData->DeletedSubSegments);
  1847. if (Entry == NULL) {
  1848. NewSubSegment = RtlpLowFragHeapAllocateFromZone(LowFragHeap, LocalData->Affinity);
  1849. #ifdef _HEAP_DEBUG
  1850. //
  1851. // We need to do some more extra initializations for
  1852. // the debug version, to verify the state of the subsegment
  1853. // in the next RtlpSubSegmentInitialize call
  1854. //
  1855. NewSubSegment->Lock = 0;
  1856. NewSubSegment->AggregateExchg.OffsetAndDepth = NO_MORE_ENTRIES << 16;
  1857. NewSubSegment->UserBlocks = NULL;
  1858. #endif
  1859. } else {
  1860. NewSubSegment = CONTAINING_RECORD(Entry, HEAP_SUBSEGMENT, SFreeListEntry);
  1861. }
  1862. if (NewSubSegment) {
  1863. UserData->Signature = HEAP_LFH_USER_SIGNATURE;
  1864. NewSubSegment->AffinityIndex = (UCHAR)LocalData->Affinity;
  1865. #ifdef _HEAP_DEBUG
  1866. //
  1867. // We need to do some more extra initializations for
  1868. // the debug version, to verify the state of the subsegment
  1869. // in the next RtlpSubSegmentInitialize call
  1870. //
  1871. NewSubSegment->SFreeListEntry.Next = 0;
  1872. #endif
  1873. RtlpSubSegmentInitialize( LowFragHeap,
  1874. NewSubSegment,
  1875. UserData,
  1876. RtlpBucketBlockSizes[Bucket],
  1877. SubSegmentSize,
  1878. HeapBucket
  1879. );
  1880. //
  1881. // When the segment initialization was completed some other threads
  1882. // can access this subsegment (because they captured the pointer before
  1883. // if the subsegment was recycled).
  1884. // This can change the state for this segment, even it can delete.
  1885. // This should be very rare cases, so we'll print a message in
  1886. // debugger. However. If this happens too often it's an indication of
  1887. // a possible bug in LFH code, or a corruption.
  1888. //
  1889. LFHEAPWARN( NewSubSegment->Lock == HEAP_USERDATA_LOCK );
  1890. LFHEAPWARN( NewSubSegment->UserBlocks );
  1891. LFHEAPWARN( NewSubSegment->BlockSize == HeapBucket->BlockUnits );
  1892. if (!RtlpTrySetActiveSubSegment(LocalData, HeapBucket, NewSubSegment)) {
  1893. RtlpInsertFreeSubSegment(LocalData, NewSubSegment);
  1894. }
  1895. goto RETRY_ALLOC;
  1896. } else {
  1897. HeapFree(LowFragHeap->Heap, 0, UserData);
  1898. }
  1899. }
  1900. }
  1901. return NULL;
  1902. }
  1903. BOOLEAN
  1904. FASTCALL
  1905. RtlpLowFragHeapFree(
  1906. HANDLE LowFragHeapHandle,
  1907. PVOID p
  1908. )
  1909. /*++
  1910. Routine Description:
  1911. The function free a block allocated with RtlpLowFragHeapAlloc.
  1912. Arguments:
  1913. Heap - the NT heap handle
  1914. LowFragHeapHandle - The LFH heap handle
  1915. Flags - Free flags
  1916. p - The pointer to the block to be freed
  1917. Return Value:
  1918. TRUE if succeeds.
  1919. --*/
  1920. {
  1921. PLFH_HEAP LowFragHeap = (PLFH_HEAP)LowFragHeapHandle;
  1922. PBLOCK_ENTRY Block = (PBLOCK_ENTRY)((PHEAP_ENTRY)p - 1);
  1923. PHEAP_SUBSEGMENT SubSegment;
  1924. PHEAP_BUCKET HeapBucket;
  1925. ULONG FreeStatus;
  1926. SubSegment = Block->SubSegment;
  1927. RtlMemoryBarrier();
  1928. RETRY_FREE:
  1929. //
  1930. // Test whether the block belongs to the LFH
  1931. //
  1932. if (Block->SegmentIndex != HEAP_LFH_INDEX) {
  1933. if ( Block->SegmentIndex == HEAP_LFH_IN_CONVERSION ) {
  1934. //
  1935. // This should happen rarely, at high memory preasure.
  1936. // The subsegment was converted meanwhile from another thread
  1937. // We need to return FALSE and let the NT heap finish the free
  1938. //
  1939. // Acquire the heap lock and release it. This way we make sure that
  1940. // the conversion completed.
  1941. //
  1942. HeapLock(LowFragHeap->Heap);
  1943. HeapUnlock(LowFragHeap->Heap);
  1944. }
  1945. return FALSE;
  1946. }
  1947. #ifdef _HEAP_DEBUG
  1948. Block->Reserved2 = 0xFFFC;
  1949. #endif // _HEAP_DEBUG
  1950. //
  1951. // Free the block to the appropriate sub-segment
  1952. //
  1953. FreeStatus = RtlpSubSegmentFree(LowFragHeap, SubSegment, Block);
  1954. switch (FreeStatus) {
  1955. case HEAP_FREE_SEGMENT_EMPTY:
  1956. {
  1957. PHEAP_LOCAL_DATA LocalData = &LowFragHeap->LocalData[SubSegment->AffinityIndex];
  1958. //
  1959. // The free call above returned TRUE, meanning that the sub-segment can be deleted
  1960. // Remove it from the active state (to prevent other threads using it)
  1961. //
  1962. RtlpTrySetActiveSubSegment(LocalData, SubSegment->Bucket, NULL);
  1963. //
  1964. // Free thye user buffer
  1965. //
  1966. RtlpFreeUserBuffer(LowFragHeap, SubSegment);
  1967. //
  1968. // Unlock the sub-segment structure. This will actually recycle the descriptor
  1969. // if that was the last lock.
  1970. //
  1971. RtlpUnlockSubSegment(LocalData, SubSegment, HEAP_USERDATA_LOCK);
  1972. }
  1973. break;
  1974. case HEAP_FREE_BLOCK_SUCCESS:
  1975. {
  1976. PHEAP_LOCAL_DATA LocalData = &LowFragHeap->LocalData[SubSegment->AffinityIndex];
  1977. LocalData->SegmentInfo[SubSegment->SizeIndex].Hint = SubSegment;
  1978. }
  1979. break;
  1980. case HEAP_FREE_BLOCK_CONVERTED:
  1981. //
  1982. // In some rare cases the segment is locked for conversion, but the conversion
  1983. // process is abandoned for some reasons. In that case we need to retry
  1984. // freeing to the LFH
  1985. //
  1986. //
  1987. // Acquire the heap lock and release it. This way we make sure that
  1988. // the conversion completed.
  1989. //
  1990. HeapLock(LowFragHeap->Heap);
  1991. HeapUnlock(LowFragHeap->Heap);
  1992. goto RETRY_FREE;
  1993. }
  1994. return TRUE;
  1995. }
  1996. BOOLEAN
  1997. FASTCALL
  1998. RtlpConvertSegmentToHeap (
  1999. PLFH_HEAP LowFragHeap,
  2000. PHEAP_SUBSEGMENT SubSegment
  2001. )
  2002. /*++
  2003. Routine Description:
  2004. The function converts a sub-segment to regular heap blocks
  2005. This function should be invocked with the PUBLIC lock held for
  2006. this subsegment
  2007. Arguments:
  2008. LowFragHeap - The Low Fragmentation Heap pointer
  2009. SubSegment - The subsegment to be converted
  2010. Return Value:
  2011. Returns TRUE if succeeds
  2012. --*/
  2013. {
  2014. INTERLOCK_SEQ CapturedValue, NewValue;
  2015. BOOLEAN SubSegmentEmpty;
  2016. SHORT Depth;
  2017. PHEAP_USERDATA_HEADER UserBlocks;
  2018. PHEAP_ENTRY UserBlockHeapEntry;
  2019. UCHAR SegmentIndex;
  2020. SIZE_T FreeSize = 0;
  2021. PVOID EndingFreeBlock = NULL;
  2022. SIZE_T SegmentUnitsUsed;
  2023. LFHEAPASSERT(RtlpIsSubSegmentLocked(SubSegment, HEAP_PUBLIC_LOCK));
  2024. //
  2025. // Since this function directly access the NT heap data
  2026. // we need first to lock the heap
  2027. //
  2028. if ( !HeapLock(LowFragHeap->Heap) ) {
  2029. return FALSE;
  2030. }
  2031. //
  2032. // Mark the segment as being converted. This will block other
  2033. // threads to allocate/free from this segment further. These threads will
  2034. // make the NT call, which can succeeds only after the conversions is done
  2035. // (because the heap lock is held)
  2036. //
  2037. if (!RtlpLockSubSegment(SubSegment, HEAP_CONVERT_LOCK)) {
  2038. HeapUnlock(LowFragHeap->Heap);
  2039. return FALSE;
  2040. }
  2041. SegmentUnitsUsed = SubSegment->BlockCount * SubSegment->BlockSize +
  2042. ((sizeof( HEAP_ENTRY ) + sizeof(HEAP_USERDATA_HEADER)) >> HEAP_GRANULARITY_SHIFT);
  2043. do {
  2044. //
  2045. // We need to capture the sequence at the first step
  2046. // Then we'll capture the other fields from the segment
  2047. // If interlock operation below succeeds, means that none
  2048. // of the sub-segment fields (UserBlocks, Bucket ....)
  2049. // we changed. So the new state was built upon a consistent state
  2050. //
  2051. CapturedValue.Sequence = SubSegment->AggregateExchg.Sequence;
  2052. CapturedValue.OffsetAndDepth = SubSegment->AggregateExchg.OffsetAndDepth;
  2053. Depth = (SHORT)CapturedValue.OffsetAndDepth;
  2054. UserBlocks = (PHEAP_USERDATA_HEADER)SubSegment->UserBlocks;
  2055. //
  2056. // Test whether the sub-segment was not deleted meanwhile
  2057. //
  2058. if ((Depth == SubSegment->BlockCount)
  2059. ||
  2060. (UserBlocks == NULL)
  2061. ||
  2062. RtlpIsSubSegmentEmpty(SubSegment)) {
  2063. RtlpUnlockSubSegment(&LowFragHeap->LocalData[SubSegment->AffinityIndex], SubSegment, HEAP_CONVERT_LOCK);
  2064. HeapUnlock(LowFragHeap->Heap);
  2065. return FALSE;
  2066. }
  2067. //
  2068. // Do not convert sub-segments which have a trailing block of
  2069. // one unit size.
  2070. //
  2071. UserBlockHeapEntry = (PHEAP_ENTRY)UserBlocks - 1;
  2072. if ( (UserBlockHeapEntry->Size - SegmentUnitsUsed) == 1) {
  2073. RtlpUnlockSubSegment(&LowFragHeap->LocalData[SubSegment->AffinityIndex], SubSegment, HEAP_CONVERT_LOCK);
  2074. HeapUnlock(LowFragHeap->Heap);
  2075. return FALSE;
  2076. }
  2077. //
  2078. // We have some blocks in this segment. We try to take them
  2079. // at ones and free to the heap like regular blocks
  2080. //
  2081. NewValue.Sequence = CapturedValue.Sequence + 1;
  2082. NewValue.OffsetAndDepth = NO_MORE_ENTRIES << 16;
  2083. } while ( !LOCKCOMP64(&SubSegment->AggregateExchg.Exchg, NewValue, CapturedValue) );
  2084. //
  2085. // We suceeded to take all blocks from the s-list. From now on
  2086. // the convert cannot fail
  2087. //
  2088. InterlockedDecrement(&LowFragHeap->UserBlockCache.AvailableBlocks[ UserBlocks->SizeIndex - HEAP_LOWEST_USER_SIZE_INDEX ]);
  2089. UserBlocks->SizeIndex = 0;
  2090. if (InterlockedIncrement(&(((PHEAP_BUCKET)SubSegment->Bucket))->Conversions) == 1) {
  2091. InterlockedIncrement(&LowFragHeap->Conversions);
  2092. }
  2093. SegmentIndex = UserBlockHeapEntry->SegmentIndex;
  2094. //
  2095. // Convert the heap block headers to NT heap style
  2096. //
  2097. {
  2098. LONG i;
  2099. LONG TotalSize;
  2100. PVOID Buffer = UserBlocks + 1;
  2101. PHEAP_ENTRY BlockEntry;
  2102. PHEAP_ENTRY PreviousBlockEntry = (PHEAP_ENTRY)UserBlocks - 1;
  2103. PHEAP_ENTRY NextBlock = NULL;
  2104. TotalSize = PreviousBlockEntry->Size;
  2105. InterlockedExchangeAdd(&LowFragHeap->ConvertedSpace, (TotalSize << HEAP_GRANULARITY_SHIFT));
  2106. if (!(PreviousBlockEntry->Flags & HEAP_ENTRY_LAST_ENTRY)) {
  2107. NextBlock = PreviousBlockEntry + PreviousBlockEntry->Size;
  2108. }
  2109. PreviousBlockEntry->Size = (sizeof(HEAP_ENTRY) + sizeof(HEAP_USERDATA_HEADER)) >> HEAP_GRANULARITY_SHIFT;
  2110. PreviousBlockEntry->UnusedBytes = sizeof(HEAP_ENTRY);
  2111. TotalSize -= PreviousBlockEntry->Size;
  2112. //
  2113. // Walk the heap sub-segment, and set the attributes for each block
  2114. // according with the NT heap rules
  2115. //
  2116. for (i = 0; i < SubSegment->BlockCount; i++) {
  2117. HEAP_ENTRY TempEntry;
  2118. BlockEntry = (PHEAP_ENTRY) Buffer;
  2119. //
  2120. // Initialize the block
  2121. //
  2122. BlockEntry->SegmentIndex = HEAP_LFH_IN_CONVERSION;
  2123. BlockEntry->Size = *((volatile USHORT *)&SubSegment->BlockSize);
  2124. BlockEntry->PreviousSize = *((volatile USHORT *)&PreviousBlockEntry->Size);
  2125. //
  2126. // Restore the index since we are done with initialization
  2127. //
  2128. BlockEntry->SegmentIndex = SegmentIndex;
  2129. TotalSize -= SubSegment->BlockSize;
  2130. //
  2131. // Points to the next free block
  2132. //
  2133. Buffer = (PCHAR)Buffer + ((SIZE_T)SubSegment->BlockSize << HEAP_GRANULARITY_SHIFT);
  2134. PreviousBlockEntry = BlockEntry;
  2135. #if defined(_WIN64)
  2136. BlockEntry->SubSegment = NULL;
  2137. #endif
  2138. }
  2139. LFHEAPASSERT(TotalSize >= 0);
  2140. //
  2141. // The last block into the segment is a special case. In general it can be smaller than
  2142. // other blocks. If the size is less than 2 heap units
  2143. // (a block header + a small minimal block) we attach it to the last block
  2144. // Otherwise, we create a separate block and initialize it properly
  2145. //
  2146. if (TotalSize >= 2) { // TotalSize in heap units
  2147. BlockEntry = BlockEntry + BlockEntry->Size;
  2148. //
  2149. // Initialize the block
  2150. //
  2151. BlockEntry->SegmentIndex = SegmentIndex;
  2152. BlockEntry->PreviousSize = PreviousBlockEntry->Size;
  2153. BlockEntry->Flags = HEAP_ENTRY_BUSY;
  2154. BlockEntry->Size = (USHORT)TotalSize;
  2155. EndingFreeBlock = BlockEntry + 1;
  2156. }
  2157. //
  2158. // If we have a next block, we need to correct the PreviousSize field
  2159. //
  2160. if (NextBlock) {
  2161. NextBlock->PreviousSize = BlockEntry->Size;
  2162. } else {
  2163. //
  2164. // Transfer the LAST_ENTRY flag, if any, to the last block
  2165. //
  2166. BlockEntry->Flags |= HEAP_ENTRY_LAST_ENTRY;
  2167. UserBlockHeapEntry->Flags &= ~HEAP_ENTRY_LAST_ENTRY;
  2168. RtlpUpdateLastEntry((PHEAP)LowFragHeap->Heap, BlockEntry);
  2169. }
  2170. }
  2171. HeapUnlock(LowFragHeap->Heap);
  2172. //
  2173. // Now free all blocks from the captured s-list to the NT heap
  2174. // We use HEAP_NO_SERIALIZE flag because we're already holding the lock
  2175. //
  2176. if (Depth){
  2177. ULONG CrtOffset = CapturedValue.FreeEntryOffset;
  2178. while (CrtOffset != NO_MORE_ENTRIES) {
  2179. PBLOCK_ENTRY BlockEntry;
  2180. PHEAP_ENTRY PreviousBlockEntry;
  2181. BlockEntry = (PBLOCK_ENTRY)((PCHAR)UserBlocks + (CrtOffset << HEAP_GRANULARITY_SHIFT));
  2182. CrtOffset = BlockEntry->LinkOffset;
  2183. FreeSize += BlockEntry->Size;
  2184. HeapFree(LowFragHeap->Heap, 0, (PVOID)((PHEAP_ENTRY)BlockEntry + 1));
  2185. }
  2186. }
  2187. //
  2188. // Free also the last block, if any.
  2189. //
  2190. if (EndingFreeBlock) {
  2191. HeapFree(LowFragHeap->Heap, 0, EndingFreeBlock);
  2192. }
  2193. LFHEAPASSERT(HeapValidate(LowFragHeap->Heap, 0, 0));
  2194. RtlpFreeUserBuffer(LowFragHeap, SubSegment);
  2195. RtlpUnlockSubSegment(&LowFragHeap->LocalData[SubSegment->AffinityIndex], SubSegment, HEAP_CONVERT_LOCK | HEAP_PUBLIC_LOCK | HEAP_USERDATA_LOCK);
  2196. return TRUE;
  2197. }
  2198. ULONG
  2199. RtlpConvertHeapBucket (
  2200. PLFH_HEAP LowFragHeap,
  2201. ULONG SizeIndex
  2202. )
  2203. /*++
  2204. Routine Description:
  2205. The function converts the segments from a given bucket index
  2206. to regular heap blocks
  2207. N.B. This is a operation can take a long time to complete. The heap is
  2208. calling this if the allocation of a new segment fails.
  2209. Arguments:
  2210. LowFragHeap - The Low Fragmentation Heap pointer
  2211. SizeIndex - Bucket index (in range 0 - 127)
  2212. Return Value:
  2213. The number of segments converted
  2214. --*/
  2215. {
  2216. PHEAP_BUCKET HeapBucket = RtlpGetBucket(LowFragHeap, SizeIndex);
  2217. ULONG Affinity;
  2218. ULONG TotalConverted = 0;
  2219. SINGLE_LIST_ENTRY Non_ConvertedList;
  2220. PVOID Entry;
  2221. Non_ConvertedList.Next = NULL;
  2222. for (Affinity = 0; Affinity < RtlpHeapMaxAffinity; Affinity++) {
  2223. PHEAP_LOCAL_SEGMENT_INFO FreeSList = &LowFragHeap->LocalData[ Affinity ].SegmentInfo[ SizeIndex ];
  2224. while (Entry = RtlpSubSegmentPop(&FreeSList->SListHeader) ) {
  2225. PHEAP_SUBSEGMENT SubSegment;
  2226. SubSegment = CONTAINING_RECORD(Entry, HEAP_SUBSEGMENT, SFreeListEntry);
  2227. #ifdef _HEAP_DEBUG
  2228. SubSegment->SFreeListEntry.Next = NULL;
  2229. #endif
  2230. LFHEAPASSERT( RtlpIsSubSegmentLocked(SubSegment, HEAP_PUBLIC_LOCK) );
  2231. LFHEAPASSERT( SizeIndex == SubSegment->SizeIndex );
  2232. //
  2233. // While we still hold the subsegment public lock, we try to convert
  2234. // it to regular NT blocks
  2235. //
  2236. if (RtlpConvertSegmentToHeap( LowFragHeap, SubSegment )) {
  2237. TotalConverted += 1;
  2238. if (LowFragHeap->ConvertedSpace > HEAP_CONVERT_LIMIT) {
  2239. while (TRUE) {
  2240. PHEAP_SUBSEGMENT xSubSegment;
  2241. Entry = PopEntryList(&Non_ConvertedList);
  2242. if (Entry == NULL) {
  2243. break;
  2244. }
  2245. xSubSegment = CONTAINING_RECORD(Entry, HEAP_SUBSEGMENT, SFreeListEntry);
  2246. RtlpSubSegmentPush(&FreeSList->SListHeader, &xSubSegment->SFreeListEntry);
  2247. }
  2248. return TotalConverted;
  2249. }
  2250. } else {
  2251. PushEntryList(&Non_ConvertedList, &SubSegment->SFreeListEntry);
  2252. }
  2253. }
  2254. while (TRUE) {
  2255. PHEAP_SUBSEGMENT SubSegment;
  2256. Entry = PopEntryList(&Non_ConvertedList);
  2257. if (Entry == NULL) {
  2258. break;
  2259. }
  2260. SubSegment = CONTAINING_RECORD(Entry, HEAP_SUBSEGMENT, SFreeListEntry);
  2261. RtlpSubSegmentPush(&FreeSList->SListHeader, &SubSegment->SFreeListEntry);
  2262. }
  2263. if (!HeapBucket->UseAffinity) {
  2264. break;
  2265. }
  2266. }
  2267. return TotalConverted;
  2268. }
  2269. ULONG
  2270. RtlpFlushLFHeapCache (
  2271. PLFH_HEAP LowFragHeap
  2272. )
  2273. /*++
  2274. Routine Description:
  2275. The function converts the segments from the free lists
  2276. to regular heap blocks
  2277. N.B. This is a operation can take a long time to complete. The heap is
  2278. calling this if the allocation of a new segment fails.
  2279. Arguments:
  2280. LowFragHeap - The Low Fragmentation Heap pointer
  2281. Return Value:
  2282. The number of segments converted
  2283. --*/
  2284. {
  2285. LONG SizeIndex;
  2286. ULONG TotalSegments = 0;
  2287. //
  2288. // Convert to regular heap blocks starting with the upper buckets (with large segments)
  2289. //
  2290. for (SizeIndex = HEAP_BUCKETS_COUNT - 1; SizeIndex >= 0; SizeIndex--) {
  2291. TotalSegments += RtlpConvertHeapBucket(LowFragHeap, SizeIndex);
  2292. if (LowFragHeap->ConvertedSpace > HEAP_CONVERT_LIMIT) {
  2293. break;
  2294. }
  2295. }
  2296. #ifdef _HEAP_DEBUG
  2297. HeapValidate(LowFragHeap->Heap, 0, 0);
  2298. #endif // _HEAP_DEBUG
  2299. return TotalSegments;
  2300. }
  2301. SIZE_T
  2302. FASTCALL
  2303. RtlpLowFragHeapGetBlockSize(
  2304. HANDLE HeapHandle,
  2305. ULONG Flags,
  2306. PVOID p
  2307. )
  2308. /*++
  2309. Routine Description:
  2310. The function returns the size of a LFH block
  2311. Arguments:
  2312. HeapHandle - The handle of the heap (not used yet)
  2313. Flags - Not used yet
  2314. p - The pointer being queried
  2315. Return Value:
  2316. The size of that block.
  2317. --*/
  2318. {
  2319. PBLOCK_ENTRY Block = (PBLOCK_ENTRY)((PHEAP_ENTRY)p - 1);
  2320. PHEAP_SUBSEGMENT SubSegment = (PHEAP_SUBSEGMENT)Block->SubSegment;
  2321. //
  2322. // Test whether the block belongs to LFH. We need to capture the
  2323. // subsegment before to protect against segment conversions
  2324. //
  2325. if (Block->SegmentIndex == HEAP_LFH_INDEX) {
  2326. return (((SIZE_T)SubSegment->BlockSize) << HEAP_GRANULARITY_SHIFT) - sizeof(HEAP_ENTRY);
  2327. }
  2328. return 0;
  2329. }
  2330. VOID
  2331. RtlpInitializeLowFragHeapManager()
  2332. /*++
  2333. Routine Description:
  2334. This function initialize the global variables for the low fragmention heap manager.
  2335. Arguments:
  2336. Return Value:
  2337. --*/
  2338. {
  2339. SIZE_T Granularity = HEAP_GRANULARITY;
  2340. ULONG i;
  2341. SIZE_T PreviousSize = 0;
  2342. SYSTEM_BASIC_INFORMATION SystemInformation;
  2343. //
  2344. // prevent the second initialization
  2345. //
  2346. if (RtlpHeapMaxAffinity) {
  2347. return;
  2348. }
  2349. #ifdef _HEAP_DEBUG
  2350. PrintMsg("Debug version\n");
  2351. #endif
  2352. //
  2353. // Query the number of processors
  2354. //
  2355. if (NT_SUCCESS(NtQuerySystemInformation (SystemBasicInformation, &SystemInformation, sizeof(SystemInformation), NULL))) {
  2356. ULONG Shift = 0;
  2357. RtlpHeapMaxAffinity = SystemInformation.NumberOfProcessors;
  2358. if (RtlpHeapMaxAffinity > 1) {
  2359. RtlpHeapMaxAffinity = (RtlpHeapMaxAffinity << 1);
  2360. }
  2361. if (RtlpHeapMaxAffinity > HEAP_AFFINITY_LIMIT) {
  2362. RtlpHeapMaxAffinity = HEAP_AFFINITY_LIMIT;
  2363. }
  2364. } else {
  2365. PrintMsg("NtQuerySystemInformation failed\n");
  2366. RtlpHeapMaxAffinity = 1;
  2367. }
  2368. #ifdef _HEAP_DEBUG
  2369. if (RtlpHeapMaxAffinity > 1) {
  2370. PrintMsg("Affinity enabled at %ld\n", RtlpHeapMaxAffinity);
  2371. }
  2372. #endif
  2373. RtlpInitializeAffinityManager( (UCHAR)RtlpHeapMaxAffinity );
  2374. //
  2375. // Generate the Bucket size table
  2376. //
  2377. for (i = 0; i < 32; i++) {
  2378. PreviousSize = RtlpBucketBlockSizes[i] = PreviousSize + Granularity;
  2379. }
  2380. for (i = 32; i < HEAP_BUCKETS_COUNT; i++) {
  2381. if ((i % 16) == 0) {
  2382. Granularity <<= 1;
  2383. }
  2384. PreviousSize = RtlpBucketBlockSizes[i] = PreviousSize + Granularity;
  2385. }
  2386. }