Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

445 lines
13 KiB

  1. /**********************************************************************/
  2. /** Microsoft Windows NT **/
  3. /** Copyright(c) Microsoft Corp., 1997 **/
  4. /**********************************************************************/
  5. /*
  6. madel.cxx
  7. This module contains the code for a memory allocation class that doesn't
  8. delete memory until the class goes away.
  9. FILE HISTORY:
  10. 1/12/98 michth created
  11. */
  12. #include "precomp.hxx"
  13. #define DLL_IMPLEMENTATION
  14. #define IMPLEMENTATION_EXPORT
  15. #include <manodel.hxx>
  16. #include <madel.hxx>
  17. MEMORY_ALLOC_DELETE::MEMORY_ALLOC_DELETE( DWORD dwAllocSize,
  18. DWORD dwAlignment,
  19. DWORD dwReserveNum,
  20. DWORD dwMinBlockMultiple,
  21. DWORD dwMaxBlockMultiple,
  22. HANDLE hHeap):
  23. m_dwAllocSize(dwAllocSize),
  24. m_dwAlignment(dwAlignment),
  25. m_dwReserveNum(dwReserveNum),
  26. m_dwBlockMultiple(dwMinBlockMultiple),
  27. m_dwMaxBlockMultiple(dwMaxBlockMultiple),
  28. m_hHeap(hHeap),
  29. m_dwNumAlloced(0),
  30. m_dwNumFree(0),
  31. m_dwNumBlocks(0),
  32. m_dwBlockHeaderSpace(sizeof(MADEL_BLOCK_HEADER)),
  33. m_dwAllocHeaderSpace(sizeof(MADEL_ALLOC_HEADER))
  34. {
  35. //DebugBreak();
  36. //DBG_ASSERT ((dwAlignment == 4) || (dwAlignment == 8));
  37. //
  38. // Make sure there really is an alignment
  39. //
  40. if (m_dwAlignment == 0) {
  41. m_dwAlignment = 4;
  42. }
  43. //
  44. // Now Make sure the alignment is a multiple of 4
  45. //
  46. AlignAdjust(m_dwAlignment, 4);
  47. //
  48. // The alloc header uses 7 bits to store the block number, so max block multiple
  49. // is 128
  50. //
  51. m_dwMaxBlockMultiple = LESSER_OF(m_dwMaxBlockMultiple, MADEL_MAX_ALLOWED_BLOCK_MULTIPLE);
  52. m_dwBlockMultiple = LESSER_OF(m_dwBlockMultiple, MADEL_MAX_ALLOWED_BLOCK_MULTIPLE);
  53. if (m_dwBlockMultiple > m_dwMaxBlockMultiple) {
  54. m_dwBlockMultiple = m_dwMaxBlockMultiple;
  55. }
  56. m_bMaxBlockMultipleEqualsMin = (m_dwMaxBlockMultiple == m_dwBlockMultiple) ? TRUE : FALSE;
  57. //
  58. // Align the size
  59. //
  60. AlignAdjust(m_dwAllocSize, m_dwAlignment);
  61. DBG_ASSERT(m_dwAllocSize != 0);
  62. DBG_ASSERT(m_dwAllocSize <= MADEL_MAX_ALLOWED_SIZE);
  63. //
  64. // Determine space to alloc for block header & Alloc header
  65. // The block header just needs to be aligned by 4. It will be
  66. // placed appropriately in the block so that the first alloc
  67. // header will be appropriately aligned.
  68. //
  69. AlignAdjust(m_dwBlockHeaderSpace, LESSER_OF(8, m_dwAlignment));
  70. AlignAdjust(m_dwAllocHeaderSpace, m_dwAlignment);
  71. if (m_dwAlignment > 8) {
  72. m_dwAlignBytes = m_dwAlignment - 8;
  73. }
  74. else {
  75. m_dwAlignBytes = 0;
  76. }
  77. //
  78. // Get Heap Handle if not passed in
  79. //
  80. if (m_hHeap == MADEL_USE_PROCESS_HEAP) {
  81. m_hHeap = GetProcessHeap();
  82. }
  83. DBG_ASSERT(m_hHeap != NULL);
  84. INITIALIZE_CRITICAL_SECTION(&m_csLock);
  85. SET_CRITICAL_SECTION_SPIN_COUNT( &m_csLock, 4000);
  86. InitializeListHead( &m_leBlockList );
  87. InitializeListHead( &m_leFreeList );
  88. InitializeListHead( &m_leDeleteList );
  89. }
  90. MEMORY_ALLOC_DELETE::~MEMORY_ALLOC_DELETE()
  91. {
  92. LockThis();
  93. PLIST_ENTRY pleIndex, pleNext;
  94. for ( pleIndex = m_leDeleteList.Flink ;
  95. pleIndex != &m_leDeleteList ;
  96. pleIndex = pleNext )
  97. {
  98. pleNext = pleIndex->Flink;
  99. RemoveEntryList(pleIndex);
  100. HeapFree(m_hHeap,
  101. /* Flags */ 0,
  102. (PVOID)((PBYTE)pleIndex - ((PMADEL_BLOCK_HEADER)pleIndex)->byteAlignBytes));
  103. }
  104. DBG_ASSERT(IsListEmpty(&m_leBlockList));
  105. DBG_ASSERT(IsListEmpty(&m_leFreeList));
  106. UnlockThis();
  107. DeleteCriticalSection(&m_csLock);
  108. }
  109. PVOID
  110. MEMORY_ALLOC_DELETE::Alloc()
  111. {
  112. PVOID pvAlloc = NULL;
  113. LockThis();
  114. pvAlloc = GetAllocFromList(&m_leFreeList);
  115. if (pvAlloc == NULL) {
  116. pvAlloc = GetAllocFromList(&m_leDeleteList);
  117. }
  118. if (pvAlloc == NULL) {
  119. if (AllocBlock()) {
  120. pvAlloc = GetAllocFromList(&m_leFreeList);
  121. DBG_ASSERT (pvAlloc != NULL);
  122. }
  123. }
  124. UnlockThis();
  125. return (PVOID)((PBYTE)pvAlloc + m_dwAllocHeaderSpace);
  126. }
  127. BOOL
  128. MEMORY_ALLOC_DELETE::Free (PVOID pvMem)
  129. {
  130. if (pvMem != NULL) {
  131. PMADEL_BLOCK_HEADER pmbhCurrentBlock;
  132. PMADEL_ALLOC_HEADER pmahCurrentAlloc;
  133. PLIST_ENTRY pleIndex, pleNext;
  134. LockThis();
  135. //
  136. // pvMem points to usable mem, header precedes it.
  137. //
  138. pmahCurrentAlloc = (PMADEL_ALLOC_HEADER)((PBYTE)pvMem - m_dwAllocHeaderSpace);
  139. //
  140. // First find the block this is on
  141. //
  142. pmbhCurrentBlock = GetBlockFromAlloc(pmahCurrentAlloc);
  143. //
  144. // Add it to the free list
  145. //
  146. *((PVOID *)pvMem) = pmbhCurrentBlock->pvFreeList;
  147. pmbhCurrentBlock->pvFreeList = (PVOID)pmahCurrentAlloc;
  148. pmbhCurrentBlock->byteNumFree++;
  149. m_dwNumFree++;
  150. DBG_ASSERT(&(pmbhCurrentBlock->m_leBlockList) == (PLIST_ENTRY)pmbhCurrentBlock);
  151. if (pmbhCurrentBlock->byteNumFree == pmbhCurrentBlock->byteBlockMultiple) {
  152. //
  153. // Move to Delete List
  154. //
  155. if (IsListEmpty(&m_leDeleteList)) {
  156. m_byteLeastAllocsOnFreeList = pmbhCurrentBlock->byteBlockMultiple;
  157. }
  158. else {
  159. m_byteLeastAllocsOnFreeList =
  160. LESSER_OF(m_byteLeastAllocsOnFreeList, pmbhCurrentBlock->byteBlockMultiple);
  161. }
  162. RemoveEntryList((PLIST_ENTRY)pmbhCurrentBlock);
  163. InsertHeadList(&m_leDeleteList,(PLIST_ENTRY)pmbhCurrentBlock);
  164. }
  165. else if ( pmbhCurrentBlock->byteNumFree == 1 ) {
  166. //
  167. // It's on the block list, Move to free list
  168. //
  169. RemoveEntryList((PLIST_ENTRY)pmbhCurrentBlock);
  170. InsertHeadList(&m_leFreeList, (PLIST_ENTRY)pmbhCurrentBlock);
  171. }
  172. //
  173. // Now see if a block can be deleted. This could be because one was added above,
  174. // or because m_dwNumFree is now high enough.
  175. //
  176. if (!IsListEmpty(&m_leDeleteList) && (m_dwNumFree >= (m_dwReserveNum + m_byteLeastAllocsOnFreeList))) {
  177. //
  178. // Remove Block and reset min
  179. //
  180. if (m_bMaxBlockMultipleEqualsMin) {
  181. //
  182. // Don't need to find a block that fits or recalculate the minblock multiple.
  183. // Just delete a block;
  184. //
  185. pleIndex = m_leDeleteList.Flink;
  186. RemoveEntryList(pleIndex);
  187. m_dwNumFree -= ((PMADEL_BLOCK_HEADER)pleIndex)->byteNumFree;
  188. m_dwNumAlloced -= ((PMADEL_BLOCK_HEADER)pleIndex)->byteNumFree;
  189. m_dwNumBlocks--;
  190. HeapFree(m_hHeap,
  191. /* Flags */ 0,
  192. (PVOID)((PBYTE)pleIndex - ((PMADEL_BLOCK_HEADER)pleIndex)->byteAlignBytes));
  193. }
  194. else {
  195. m_byteLeastAllocsOnFreeList = (BYTE)m_dwMaxBlockMultiple;
  196. for ( pleIndex = m_leDeleteList.Flink ;
  197. pleIndex != &m_leDeleteList ;
  198. pleIndex = pleNext )
  199. {
  200. pleNext = pleIndex->Flink;
  201. if (m_dwNumFree >= (m_dwReserveNum + ((PMADEL_BLOCK_HEADER)pleIndex)->byteBlockMultiple) ) {
  202. RemoveEntryList(pleIndex);
  203. m_dwNumFree -= ((PMADEL_BLOCK_HEADER)pleIndex)->byteNumFree;
  204. m_dwNumAlloced -= ((PMADEL_BLOCK_HEADER)pleIndex)->byteNumFree;
  205. m_dwNumBlocks--;
  206. HeapFree(m_hHeap,
  207. /* Flags */ 0,
  208. (PVOID)((PBYTE)pleIndex - ((PMADEL_BLOCK_HEADER)pleIndex)->byteAlignBytes));
  209. }
  210. else {
  211. m_byteLeastAllocsOnFreeList =
  212. LESSER_OF(m_byteLeastAllocsOnFreeList, ((PMADEL_BLOCK_HEADER)pleIndex)->byteBlockMultiple);
  213. }
  214. }
  215. }
  216. }
  217. UnlockThis();
  218. }
  219. return TRUE;
  220. }
  221. VOID
  222. MEMORY_ALLOC_DELETE::GetNewBlockMultiple()
  223. {
  224. DWORD dwCalculatedMultiple = LESSER_OF((m_dwNumAlloced / 5), m_dwMaxBlockMultiple);
  225. m_dwBlockMultiple = GREATER_OF(m_dwBlockMultiple, dwCalculatedMultiple);
  226. }
  227. PVOID
  228. MEMORY_ALLOC_DELETE::GetAllocFromList(PLIST_ENTRY pleListHead)
  229. {
  230. PVOID pvAlloc = NULL;
  231. PLIST_ENTRY pleFreeBlock;
  232. PMADEL_BLOCK_HEADER pmbhFreeBlock;
  233. //
  234. // Remove from list
  235. //
  236. if (!IsListEmpty(pleListHead)) {
  237. pleFreeBlock = RemoveHeadList(pleListHead);
  238. DBG_ASSERT(pleFreeBlock != NULL);
  239. pmbhFreeBlock = (PMADEL_BLOCK_HEADER)pleFreeBlock;
  240. DBG_ASSERT(pmbhFreeBlock == (CONTAINING_RECORD(pleFreeBlock, MADEL_BLOCK_HEADER, m_leBlockList)));
  241. pvAlloc = pmbhFreeBlock->pvFreeList;
  242. DBG_ASSERT(pvAlloc != NULL);
  243. pmbhFreeBlock->pvFreeList = *((PVOID *)((PBYTE)(pmbhFreeBlock->pvFreeList) + m_dwAllocHeaderSpace));
  244. pmbhFreeBlock->byteNumFree--;
  245. m_dwNumFree--;
  246. //
  247. // Put the block back on a list.
  248. // Just removed one element, so know it doesn't go on the Delete list.
  249. //
  250. if (pmbhFreeBlock->byteNumFree == 0) {
  251. InsertHeadList(&m_leBlockList, pleFreeBlock);
  252. }
  253. else {
  254. InsertHeadList(&m_leFreeList, pleFreeBlock);
  255. }
  256. }
  257. return pvAlloc;
  258. }
  259. BOOL
  260. MEMORY_ALLOC_DELETE::AllocBlock()
  261. {
  262. PVOID pvNewBlock = NULL;
  263. DWORD dwBlockSize;
  264. BOOL bReturn = FALSE;
  265. GetNewBlockMultiple();
  266. dwBlockSize = ((m_dwAllocSize + m_dwAllocHeaderSpace) * m_dwBlockMultiple) +
  267. m_dwBlockHeaderSpace + m_dwAlignBytes;
  268. pvNewBlock = HeapAlloc(m_hHeap,
  269. /* Flags */ 0,
  270. dwBlockSize);
  271. if (pvNewBlock == NULL) {
  272. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  273. }
  274. else {
  275. //
  276. // Put the block on the free list. Since all allocs are available,
  277. // it really belongs on the delete list, but one will immediately be
  278. // taken off, so just put it on the free list.
  279. //
  280. PBYTE pbFirstAlloc = (PBYTE)pvNewBlock + m_dwBlockHeaderSpace;
  281. if (m_dwAlignment > 8) {
  282. //
  283. // May need to put some bytes in front to align allocs
  284. // Find the first place that is aligned.
  285. //
  286. ULONG_PTR firstAlloc = (ULONG_PTR)pbFirstAlloc;
  287. AlignAdjust(firstAlloc, (ULONG_PTR)m_dwAlignment);
  288. pbFirstAlloc = (PBYTE)firstAlloc;
  289. }
  290. PMADEL_BLOCK_HEADER pmbhBlockHeader = (PMADEL_BLOCK_HEADER)((PBYTE)pbFirstAlloc - m_dwBlockHeaderSpace);
  291. InsertHeadList(&m_leFreeList, (PLIST_ENTRY)pmbhBlockHeader);
  292. pmbhBlockHeader->byteBlockMultiple = (BYTE)m_dwBlockMultiple;
  293. pmbhBlockHeader->byteNumFree = (BYTE)m_dwBlockMultiple;
  294. pmbhBlockHeader->byteAlignBytes = DIFF((PBYTE)pmbhBlockHeader - (PBYTE)pvNewBlock);
  295. CreateBlockFreeList(pmbhBlockHeader);
  296. m_dwNumAlloced += m_dwBlockMultiple;
  297. m_dwNumFree += m_dwBlockMultiple;
  298. m_dwNumBlocks++;
  299. bReturn = TRUE;
  300. }
  301. /* INTRINSA suppress = leaks */
  302. return bReturn;
  303. }
  304. VOID
  305. MEMORY_ALLOC_DELETE::CreateBlockFreeList(PMADEL_BLOCK_HEADER pmbhNewBlock)
  306. {
  307. PVOID pvEnd = (PVOID)((PBYTE)pmbhNewBlock + m_dwBlockHeaderSpace +
  308. (m_dwBlockMultiple * (m_dwAllocSize + m_dwAllocHeaderSpace)));
  309. DBG_ASSERT(pmbhNewBlock != NULL);
  310. pmbhNewBlock->pvFreeList = NULL;
  311. BYTE i;
  312. PVOID pvAllocIndex;
  313. for ((pvAllocIndex = (PVOID)((PBYTE)pmbhNewBlock + m_dwBlockHeaderSpace)), i = 0;
  314. pvAllocIndex < pvEnd;
  315. pvAllocIndex = (PVOID)((PBYTE)pvAllocIndex + m_dwAllocSize + m_dwAllocHeaderSpace), i++) {
  316. InitAllocHead((PMADEL_ALLOC_HEADER)pvAllocIndex, i);
  317. *((PVOID *)((PBYTE)pvAllocIndex + m_dwAllocHeaderSpace)) = pmbhNewBlock->pvFreeList;
  318. pmbhNewBlock->pvFreeList = pvAllocIndex;
  319. }
  320. }
  321. VOID
  322. MEMORY_ALLOC_DELETE::AlignAdjust(DWORD &rdwSize, DWORD dwAlignment)
  323. {
  324. if ((rdwSize % dwAlignment != 0)) {
  325. rdwSize &= (0xFFFFFFFF - dwAlignment + 1);
  326. rdwSize += dwAlignment;
  327. }
  328. }
  329. #ifdef _WIN64
  330. VOID
  331. MEMORY_ALLOC_DELETE::AlignAdjust(ULONG_PTR &rSize, ULONG_PTR Alignment)
  332. {
  333. rSize = ( rSize + Alignment - 1 ) & ~( Alignment - 1 );
  334. }
  335. #endif
  336. VOID
  337. MEMORY_ALLOC_DELETE::InitAllocHead(PMADEL_ALLOC_HEADER pvAlloc,
  338. DWORD dwAllocIndex)
  339. {
  340. pvAlloc->dwSize = m_dwAllocSize;
  341. pvAlloc->bNumAlloc = dwAllocIndex;
  342. pvAlloc->bMadelAlloc = 1;
  343. }
  344. PMADEL_BLOCK_HEADER
  345. MEMORY_ALLOC_DELETE::GetBlockFromAlloc(PMADEL_ALLOC_HEADER pmahMem)
  346. {
  347. return (PMADEL_BLOCK_HEADER)((PBYTE)pmahMem -
  348. ((pmahMem->bNumAlloc * (m_dwAllocSize + m_dwAllocHeaderSpace)) + m_dwBlockHeaderSpace));
  349. }