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.

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