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.

520 lines
13 KiB

  1. /***
  2. *heapmin.c - Minimize the heap
  3. *
  4. * Copyright (c) 1989-2001, Microsoft Corporation. All rights reserved.
  5. *
  6. *Purpose:
  7. * Minimize the heap freeing as much memory as possible back
  8. * to the OS.
  9. *
  10. *Revision History:
  11. * 08-28-89 JCR Module created.
  12. * 11-06-89 JCR Improved, partitioned
  13. * 11-13-89 GJF Added MTHREAD support, also fixed copyright
  14. * 12-14-89 GJF Couple of bug fixes, some tuning, cleaned up the
  15. * formatting a bit and changed header file name to
  16. * heap.h
  17. * 12-20-89 GJF Removed references to plastdesc
  18. * 03-11-90 GJF Replaced _cdecl with _CALLTYPE1, added #include
  19. * <cruntime.h> and removed #include <register.h>.
  20. * 03-29-90 GJF Made _heapmin_region() and _free_partial_region()
  21. * _CALLTYPE4.
  22. * 07-24-90 SBM Compiles cleanly with -W3 (tentatively removed
  23. * unreferenced labels and unreachable code), removed
  24. * '32' from API names
  25. * 09-28-90 GJF New-style function declarators. Also, rewrote expr.
  26. * to avoid using cast as lvalue.
  27. * 12-04-90 SRW Changed to include <oscalls.h> instead of <doscalls.h>
  28. * 12-06-90 SRW Added _CRUISER_ and _WIN32 conditionals.
  29. * 12-28-90 SRW Added cast of void * to char * for Mips C Compiler
  30. * 03-05-91 GJF Changed strategy for rover - old version available
  31. * by #define-ing _OLDROVER_.
  32. * 04-06-93 SKS Replace _CRTAPI* with __cdecl
  33. * 03-03-94 GJF Changed references to _GETEMPTY macro to calls to
  34. * the __getempty function. Added graceful handling for
  35. * failure of the call to __getempty in _heapmin_region.
  36. * However, failure in _free_partial_region will still
  37. * result in termination via _heap_abort (very difficult
  38. * to handle any other way, very unlikely to occur).
  39. * 02-07-95 GJF Merged in Mac version. Removed obsolete _OLDROVER_
  40. * support.
  41. * 04-30-95 GJF Spliced on winheap version.
  42. * 03-07-96 GJF Added support for small-block heap.
  43. * 05-22-97 RDK New small-block heap scheme implemented.
  44. * 09-26-97 BWT Remove POSIX
  45. * 12-17-97 GJF Exception-safe locking.
  46. * 09-30-98 GJF Bypass all small-block heap code when __sbh_initialized
  47. * is 0.
  48. * 11-19-98 GJF Merged in VC++ 5.0 small-block heap support.
  49. * 05-01-99 PML Disable small-block heap for Win64.
  50. * 05-17-99 PML Remove all Macintosh support.
  51. * 06-22-99 GJF Removed old small-block heap from static libs.
  52. *
  53. *******************************************************************************/
  54. #ifdef WINHEAP
  55. #include <cruntime.h>
  56. #include <windows.h>
  57. #include <errno.h>
  58. #include <malloc.h>
  59. #include <mtdll.h>
  60. #include <stdlib.h>
  61. #include <winheap.h>
  62. /***
  63. *_heapmin() - Minimize the heap
  64. *
  65. *Purpose:
  66. * Minimize the heap freeing as much memory as possible back
  67. * to the OS.
  68. *
  69. *Entry:
  70. * (void)
  71. *
  72. *Exit:
  73. *
  74. * 0 = no error has occurred
  75. * -1 = an error has occurred (errno is set)
  76. *
  77. *Exceptions:
  78. *
  79. *******************************************************************************/
  80. int __cdecl _heapmin(void)
  81. {
  82. #ifndef _WIN64
  83. if ( __active_heap == __V6_HEAP ) {
  84. #ifdef _MT
  85. _mlock( _HEAP_LOCK );
  86. __try {
  87. #endif
  88. __sbh_heapmin();
  89. #ifdef _MT
  90. }
  91. __finally {
  92. _munlock( _HEAP_LOCK );
  93. }
  94. #endif
  95. }
  96. #ifdef CRTDLL
  97. else if ( __active_heap == __V5_HEAP ) {
  98. /*
  99. * Minimize the small-block heap by calling _sbh_decommit_pages()
  100. * with a big enough count to ensure every page which can be
  101. * decommitted, is.
  102. */
  103. #ifdef _MT
  104. _mlock( _HEAP_LOCK );
  105. __try {
  106. #endif
  107. __old_sbh_decommit_pages( 2 * _OLD_PAGES_PER_COMMITMENT );
  108. #ifdef _MT
  109. }
  110. __finally {
  111. _munlock( _HEAP_LOCK );
  112. }
  113. #endif
  114. }
  115. #endif /* CRTDLL */
  116. #endif /* ndef _WIN64 */
  117. if ( HeapCompact( _crtheap, 0 ) == 0 ) {
  118. if ( GetLastError() == ERROR_CALL_NOT_IMPLEMENTED ) {
  119. _doserrno = ERROR_CALL_NOT_IMPLEMENTED;
  120. errno = ENOSYS;
  121. }
  122. return -1;
  123. }
  124. else {
  125. return 0;
  126. }
  127. }
  128. #else /* ndef WINHEAP */
  129. #include <cruntime.h>
  130. #include <heap.h>
  131. #include <malloc.h>
  132. #include <mtdll.h>
  133. #include <stdlib.h>
  134. #include <windows.h>
  135. static int __cdecl _heapmin_region(int, void *, _PBLKDESC);
  136. static void __cdecl _free_partial_region(_PBLKDESC, unsigned, int);
  137. /***
  138. *_heapmin() - Minimize the heap
  139. *
  140. *Purpose:
  141. * Minimize the heap freeing as much memory as possible back
  142. * to the OS.
  143. *
  144. *Entry:
  145. * (void)
  146. *
  147. *Exit:
  148. * 0 = no error has occurred
  149. * -1 = an error has occurred (errno is set)
  150. *
  151. *Exceptions:
  152. *
  153. *******************************************************************************/
  154. int __cdecl _heapmin(void)
  155. {
  156. REG1 int index;
  157. _PBLKDESC pdesc;
  158. REG2 _PBLKDESC pdesc2;
  159. void * regend;
  160. int region_min_count = 0;
  161. /*
  162. * Lock the heap
  163. */
  164. _mlock(_HEAP_LOCK);
  165. /*
  166. * Coalesce the heap (should return NULL)
  167. */
  168. if ( _heap_search((unsigned)_HEAP_COALESCE) != NULL )
  169. _heap_abort();
  170. /*
  171. * Loop through the region descriptor table freeing as much
  172. * memory to the OS as possible.
  173. */
  174. for ( index=0 ; index < _HEAP_REGIONMAX ; index++ ) {
  175. if ( _heap_regions[index]._regbase == NULL )
  176. continue; /* region entry is empty */
  177. /*
  178. * Get the entry that contains the last address of
  179. * the region (allocated so far, that is).
  180. */
  181. regend = (char *) _heap_regions[index]._regbase +
  182. _heap_regions[index]._currsize - 1;
  183. if ( _heap_findaddr(regend, &pdesc) != _HEAPFIND_WITHIN )
  184. _heap_abort(); /* last address not within a block */
  185. /*
  186. * See if the containing block is free
  187. */
  188. if ( !(_IS_FREE(pdesc)) )
  189. continue; /* block is not free */
  190. /*
  191. * Region ends with a free block, go free as much mem
  192. * as possible.
  193. */
  194. region_min_count += _heapmin_region(index, regend, pdesc);
  195. } /* region loop */
  196. /*
  197. * By minimizing the heap, we've likely invalidated the rover and
  198. * may have produced contiguous dummy blocks so:
  199. *
  200. * (1) reset the rover
  201. * (2) coalesce contiguous dummy blocks
  202. */
  203. if ( region_min_count ) {
  204. /*
  205. * Set proverdesc to pfirstdesc
  206. */
  207. _heap_desc.proverdesc = _heap_desc.pfirstdesc;
  208. for ( pdesc = _heap_desc.pfirstdesc ; pdesc !=
  209. &_heap_desc.sentinel ; pdesc = pdesc->pnextdesc ) {
  210. /*
  211. * Check and remove consecutive dummy blocks
  212. */
  213. if ( _IS_DUMMY(pdesc) ) {
  214. for ( pdesc2 = pdesc->pnextdesc ;
  215. _IS_DUMMY(pdesc2) ;
  216. pdesc2 = pdesc->pnextdesc ) {
  217. /*
  218. * coalesce the dummy blocks
  219. */
  220. pdesc->pnextdesc = pdesc2->pnextdesc;
  221. _PUTEMPTY(pdesc2);
  222. } /* dummy loop */
  223. } /* if */
  224. } /* heap loop */
  225. } /* region_min_count */
  226. /*
  227. * Good return
  228. */
  229. /* goodrtn: unreferenced label to be removed */
  230. /*
  231. * Release the heap lock
  232. */
  233. _munlock(_HEAP_LOCK);
  234. return(0);
  235. }
  236. /***
  237. *_heapmin_region() - Minimize a region
  238. *
  239. *Purpose:
  240. * Free as much of a region back to the OS as possible.
  241. *
  242. *Entry:
  243. * int index = index of the region in the region table
  244. * void * regend = last valid address in region
  245. * pdesc = pointer to the last block of memory in the region
  246. * (it has already been determined that this block is free)
  247. *
  248. *Exit:
  249. * int 1 = minimized region
  250. * 0 = no change to region
  251. *
  252. *Exceptions:
  253. *
  254. *******************************************************************************/
  255. static int __cdecl _heapmin_region (
  256. int index,
  257. void * regend,
  258. REG1 _PBLKDESC pdesc
  259. )
  260. {
  261. unsigned size;
  262. REG2 _PBLKDESC pnew;
  263. /*
  264. * Init some variables
  265. *
  266. * regend = 1st address AFTER region
  267. * size = amount of free memory at end of current region
  268. */
  269. regend = (char *) regend + 1; /* "regend++" give compiler error... */
  270. size = ((char *)regend - (char *)_ADDRESS(pdesc));
  271. /*
  272. * See if there's enough free memory to release to the OS.
  273. * (NOTE: Need more than a page since we may need a back pointer.)
  274. */
  275. if ( size <= _PAGESIZE_ )
  276. return(0); /* 0 = no change to region */
  277. /*
  278. * We're going to free some memory to the OS. See if the
  279. * free block crosses the end of the region and, if so,
  280. * split up the block appropriately.
  281. */
  282. if ( (_MEMSIZE(pdesc) - size) != 0 ) {
  283. /*
  284. * The free block spans the end of the region.
  285. * Divide it up.
  286. */
  287. /*
  288. * Get an empty descriptor
  289. */
  290. if ( (pnew = __getempty()) == NULL )
  291. return(0);
  292. pnew->pblock = regend; /* init block pointer */
  293. * (_PBLKDESC*)regend = pnew; /* init back pointer */
  294. _SET_FREE(pnew); /* set the block free */
  295. pnew->pnextdesc = pdesc->pnextdesc; /* link it in */
  296. pdesc->pnextdesc = pnew;
  297. }
  298. /*
  299. * At this point, we have a free block of memory that goes
  300. * up to (but not exceeding) the end of the region.
  301. *
  302. * pdesc = descriptor of the last free block in region
  303. * size = amount of free mem at end of region (i.e., _MEMSIZE(pdesc))
  304. * regend = 1st address AFTER end of region
  305. */
  306. /*
  307. * See if we should return the whole region of only part of it.
  308. */
  309. if ( _ADDRESS(pdesc) == _heap_regions[index]._regbase ) {
  310. /*
  311. * Whole region is free, return it to OS
  312. */
  313. _heap_free_region(index);
  314. /*
  315. * Put a dummy block in the heap to hold space for
  316. * the memory we just freed up.
  317. */
  318. _SET_DUMMY(pdesc);
  319. }
  320. else {
  321. /*
  322. * Whole region is NOT free, return part of it to OS
  323. */
  324. _free_partial_region(pdesc, size, index);
  325. }
  326. /*
  327. * Exit paths
  328. */
  329. return(1); /* 1 = minimized region */
  330. }
  331. /***
  332. *_free_partial_region() - Free part of a region to the OS
  333. *
  334. *Purpose:
  335. * Free a portion of a region to the OS
  336. *
  337. *Entry:
  338. * pdesc = descriptor of last free block in region
  339. * size = amount of free mem at end of region (i.e., _MEMSIZE(pdesc))
  340. * index = index of region
  341. *
  342. *Exit:
  343. *
  344. *Exceptions:
  345. *
  346. *******************************************************************************/
  347. static void __cdecl _free_partial_region (
  348. REG1 _PBLKDESC pdesc,
  349. unsigned size,
  350. int index
  351. )
  352. {
  353. unsigned left;
  354. void * base;
  355. REG2 _PBLKDESC pnew;
  356. /*
  357. * Init a few variables.
  358. */
  359. left = (size & (_PAGESIZE_-1));
  360. base = (char *)_ADDRESS(pdesc);
  361. /*
  362. * We return memory to the OS in page multiples. If the
  363. * free block is not page aligned, we'll insert a new free block
  364. * to fill in the difference.
  365. */
  366. if ( left != 0 ) {
  367. /*
  368. * The block is not a multiple of pages so we need
  369. * to adjust variables accordingly.
  370. */
  371. size -= left;
  372. base = (char *)base + left;
  373. }
  374. /*
  375. * Return the free pages to the OS.
  376. */
  377. if (!VirtualFree(base, size, MEM_DECOMMIT))
  378. _heap_abort();
  379. /*
  380. * Adjust the region table entry
  381. */
  382. _heap_regions[index]._currsize -= size;
  383. /*
  384. * Adjust the heap according to whether we released the whole
  385. * free block or not. (Don't worry about consecutive dummies,
  386. * we'll coalesce them later.)
  387. *
  388. * base = address of block we just gave back to OS
  389. * size = size of block we gave back to OS
  390. * left = size of block we did NOT give back to OS
  391. */
  392. if ( left == 0 ) {
  393. /*
  394. * The free block was released to the OS in its
  395. * entirety. Make the free block a dummy place holder.
  396. */
  397. _SET_DUMMY(pdesc);
  398. }
  399. else {
  400. /*
  401. * Did NOT release the whole free block to the OS.
  402. * There's a block of free memory we want to leave
  403. * in the heap. Insert a dummy entry after it.
  404. */
  405. if ( (pnew = __getempty()) == NULL )
  406. _heap_abort();
  407. pnew->pblock = (char *)base;
  408. _SET_DUMMY(pnew);
  409. pnew->pnextdesc = pdesc->pnextdesc;
  410. pdesc->pnextdesc = pnew;
  411. }
  412. }
  413. #endif /* WINHEAP */