home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / vc98 / crt / src / heapmin.c < prev    next >
C/C++ Source or Header  |  1998-06-17  |  13KB  |  513 lines

  1. /***
  2. *heapmin.c - Minimize the heap
  3. *
  4. *       Copyright (c) 1989-1997, 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. *******************************************************************************/
  11.  
  12.  
  13. #ifdef WINHEAP
  14.  
  15.  
  16. #include <cruntime.h>
  17. #include <windows.h>
  18. #include <errno.h>
  19. #include <malloc.h>
  20. #include <mtdll.h>
  21. #include <stdlib.h>
  22. #include <winheap.h>
  23.  
  24. /***
  25. *_heapmin() - Minimize the heap
  26. *
  27. *Purpose:
  28. *       Minimize the heap freeing as much memory as possible back
  29. *       to the OS.
  30. *
  31. *Entry:
  32. *       (void)
  33. *
  34. *Exit:
  35. *
  36. *        0 = no error has occurred
  37. *       -1 = an error has occurred (errno is set)
  38. *
  39. *Exceptions:
  40. *
  41. *******************************************************************************/
  42.  
  43. int __cdecl _heapmin(void)
  44. {
  45.         /*
  46.          * Minimize the small-block heap by calling _sbh_decommit_pages()
  47.          * with a big enough count to ensure every page which can be
  48.          * decommitted, is.
  49.          */
  50.         _mlock(_HEAP_LOCK);
  51.         __sbh_heapmin();
  52.         _munlock(_HEAP_LOCK);
  53.  
  54.         if ( HeapCompact( _crtheap, 0 ) == 0 ) {
  55.  
  56.             if ( GetLastError() == ERROR_CALL_NOT_IMPLEMENTED ) {
  57.                 _doserrno = ERROR_CALL_NOT_IMPLEMENTED;
  58.                 errno = ENOSYS;
  59.             }
  60.             return -1;
  61.         }
  62.         else {
  63.             return 0;
  64.         }
  65. }
  66.  
  67. #else  /* WINHEAP */
  68.  
  69.  
  70. #include <cruntime.h>
  71. #include <heap.h>
  72. #include <malloc.h>
  73. #include <mtdll.h>
  74. #include <stdlib.h>
  75.  
  76. #if defined (_M_MPPC) || defined (_M_M68K)
  77. #include <macos\memory.h>        // Mac OS interface header
  78. #else  /* defined (_M_MPPC) || defined (_M_M68K) */
  79. #include <windows.h>
  80. #endif  /* defined (_M_MPPC) || defined (_M_M68K) */
  81.  
  82. static int __cdecl _heapmin_region(int, void *, _PBLKDESC);
  83. static void __cdecl _free_partial_region(_PBLKDESC, unsigned, int);
  84.  
  85. #if defined (_M_MPPC) || defined (_M_M68K)
  86. extern Handle hHeapRegions;
  87. extern int _heap_region_table_cur;
  88. #endif  /* defined (_M_MPPC) || defined (_M_M68K) */
  89.  
  90. /***
  91. *_heapmin() - Minimize the heap
  92. *
  93. *Purpose:
  94. *   Minimize the heap freeing as much memory as possible back
  95. *   to the OS.
  96. *
  97. *Entry:
  98. *   (void)
  99. *
  100. *Exit:
  101. *    0 = no error has occurred
  102. *   -1 = an error has occurred (errno is set)
  103. *
  104. *Exceptions:
  105. *
  106. *******************************************************************************/
  107.  
  108. int __cdecl _heapmin(void)
  109. {
  110.     REG1 int index;
  111.     _PBLKDESC pdesc;
  112.     REG2 _PBLKDESC pdesc2;
  113.     void * regend;
  114.     int region_min_count = 0;
  115. #if defined (_M_MPPC) || defined (_M_M68K)
  116.     struct _heap_region_ *pHeapRegions;
  117. #endif  /* defined (_M_MPPC) || defined (_M_M68K) */
  118.     /*
  119.      * Lock the heap
  120.      */
  121.  
  122.     _mlock(_HEAP_LOCK);
  123.  
  124.     /*
  125.      * Coalesce the heap (should return NULL)
  126.      */
  127.  
  128.     if ( _heap_search((unsigned)_HEAP_COALESCE) != NULL )
  129.         _heap_abort();
  130.  
  131.     /*
  132.      * Loop through the region descriptor table freeing as much
  133.      * memory to the OS as possible.
  134.      */
  135.  
  136. #if defined (_M_MPPC) || defined (_M_M68K)
  137.  
  138.     for ( index=0 ; index < _heap_region_table_cur ; index++ ) {
  139.  
  140.         pHeapRegions = (struct _heap_region_ *)(*hHeapRegions);
  141.         if ( (pHeapRegions + index)->_regbase == NULL )
  142.             continue;       /* region entry is empty */
  143.  
  144.         /*
  145.          * Get the entry that contains the last address of
  146.          * the region (allocated so far, that is).
  147.          */
  148.  
  149.         regend = (char *) ( (pHeapRegions + index)->_regbase) +
  150.                  (pHeapRegions + index)->_currsize - 1;
  151.  
  152. #else  /* defined (_M_MPPC) || defined (_M_M68K) */
  153.  
  154.     for ( index=0 ; index < _HEAP_REGIONMAX ; index++ ) {
  155.  
  156.         if ( _heap_regions[index]._regbase == NULL )
  157.             continue;    /* region entry is empty */
  158.  
  159.         /*
  160.          * Get the entry that contains the last address of
  161.          * the region (allocated so far, that is).
  162.          */
  163.  
  164.         regend = (char *) _heap_regions[index]._regbase +
  165.                  _heap_regions[index]._currsize - 1;
  166.  
  167. #endif  /* defined (_M_MPPC) || defined (_M_M68K) */
  168.  
  169.         if ( _heap_findaddr(regend, &pdesc) != _HEAPFIND_WITHIN )
  170.             _heap_abort();  /* last address not within a block */
  171.  
  172.         /*
  173.          * See if the containing block is free
  174.          */
  175.  
  176.         if ( !(_IS_FREE(pdesc)) )
  177.             continue;    /* block is not free */
  178.  
  179.  
  180.         /*
  181.          * Region ends with a free block, go free as much mem
  182.          * as possible.
  183.          */
  184.  
  185.         region_min_count += _heapmin_region(index, regend, pdesc);
  186.  
  187.  
  188.     }  /* region loop */
  189.  
  190.     /*
  191.      * By minimizing the heap, we've likely invalidated the rover and
  192.      * may have produced contiguous dummy blocks so:
  193.      *
  194.      *  (1) reset the rover
  195.      *  (2) coalesce contiguous dummy blocks
  196.      */
  197.  
  198.     if ( region_min_count ) {
  199.  
  200.         /*
  201.          * Set proverdesc to pfirstdesc
  202.          */
  203.  
  204.         _heap_desc.proverdesc = _heap_desc.pfirstdesc;
  205.  
  206.         for ( pdesc = _heap_desc.pfirstdesc ; pdesc !=
  207.             &_heap_desc.sentinel ; pdesc = pdesc->pnextdesc ) {
  208.  
  209.             /*
  210.              * Check and remove consecutive dummy blocks
  211.              */
  212.  
  213.             if ( _IS_DUMMY(pdesc) ) {
  214.  
  215.                 for ( pdesc2 = pdesc->pnextdesc ;
  216.                     _IS_DUMMY(pdesc2) ;
  217.                     pdesc2 = pdesc->pnextdesc ) {
  218.  
  219.                     /*
  220.                      * coalesce the dummy blocks
  221.                      */
  222.  
  223.                     pdesc->pnextdesc = pdesc2->pnextdesc;
  224.                     _PUTEMPTY(pdesc2);
  225.  
  226.                 }  /* dummy loop */
  227.  
  228.             }  /* if */
  229.  
  230.         }  /* heap loop */
  231.  
  232.     }  /* region_min_count */
  233.  
  234.     /*
  235.      * Good return
  236.      */
  237.  
  238.     /* goodrtn:   unreferenced label to be removed */
  239.     /*
  240.      * Release the heap lock
  241.      */
  242.  
  243.     _munlock(_HEAP_LOCK);
  244.     return(0);
  245. }
  246.  
  247.  
  248. /***
  249. *_heapmin_region() - Minimize a region
  250. *
  251. *Purpose:
  252. *   Free as much of a region back to the OS as possible.
  253. *
  254. *Entry:
  255. *   int index = index of the region in the region table
  256. *   void * regend = last valid address in region
  257. *   pdesc = pointer to the last block of memory in the region
  258. *       (it has already been determined that this block is free)
  259. *
  260. *Exit:
  261. *   int 1 = minimized region
  262. *       0 = no change to region
  263. *
  264. *Exceptions:
  265. *
  266. *******************************************************************************/
  267.  
  268. static int __cdecl _heapmin_region (
  269.     int index,
  270.     void * regend,
  271.     REG1 _PBLKDESC pdesc
  272.     )
  273. {
  274.     unsigned size;
  275.     REG2 _PBLKDESC pnew;
  276. #if defined (_M_MPPC) || defined (_M_M68K)
  277.     struct _heap_region_ *pHeapRegions;
  278. #endif  /* defined (_M_MPPC) || defined (_M_M68K) */
  279.  
  280.     /*
  281.      * Init some variables
  282.      *
  283.      * regend = 1st address AFTER region
  284.      * size = amount of free memory at end of current region
  285.      */
  286.  
  287.     regend = (char *) regend + 1;   /* "regend++" give compiler error... */
  288.     size = ((char *)regend - (char *)_ADDRESS(pdesc));
  289.  
  290.  
  291.     /*
  292.      * See if there's enough free memory to release to the OS.
  293.      * (NOTE:  Need more than a page since we may need a back pointer.)
  294.      */
  295.  
  296.     if ( size <= _PAGESIZE_ )
  297.         return(0);      /* 0 = no change to region */
  298.  
  299.     /*
  300.      * We're going to free some memory to the OS.  See if the
  301.      * free block crosses the end of the region and, if so,
  302.      * split up the block appropriately.
  303.      */
  304.  
  305.     if ( (_MEMSIZE(pdesc) - size) != 0 ) {
  306.  
  307.         /*
  308.          * The free block spans the end of the region.
  309.          * Divide it up.
  310.          */
  311.  
  312.         /*
  313.          * Get an empty descriptor
  314.          */
  315.  
  316.         if ( (pnew = __getempty()) == NULL )
  317.             return(0);
  318.  
  319.         pnew->pblock = regend;        /* init block pointer */
  320.         * (_PBLKDESC*)regend = pnew;  /* init back pointer */
  321.         _SET_FREE(pnew);              /* set the block free */
  322.  
  323.         pnew->pnextdesc = pdesc->pnextdesc; /* link it in */
  324.         pdesc->pnextdesc = pnew;
  325.  
  326.     }
  327.  
  328.  
  329.     /*
  330.      * At this point, we have a free block of memory that goes
  331.      * up to (but not exceeding) the end of the region.
  332.      *
  333.      * pdesc = descriptor of the last free block in region
  334.      * size = amount of free mem at end of region (i.e., _MEMSIZE(pdesc))
  335.      * regend = 1st address AFTER end of region
  336.      */
  337.  
  338.  
  339.     /*
  340.      * See if we should return the whole region of only part of it.
  341.      */
  342. #if defined (_M_MPPC) || defined (_M_M68K)
  343.     pHeapRegions = (struct _heap_region_ *)(*hHeapRegions);
  344.     if ( _ADDRESS(pdesc) == (pHeapRegions + index)->_regbase ) {
  345. #else  /* defined (_M_MPPC) || defined (_M_M68K) */
  346.     if ( _ADDRESS(pdesc) == _heap_regions[index]._regbase ) {
  347. #endif  /* defined (_M_MPPC) || defined (_M_M68K) */
  348.  
  349.         /*
  350.          * Whole region is free, return it to OS
  351.          */
  352.  
  353.         _heap_free_region(index);
  354.  
  355.         /*
  356.          * Put a dummy block in the heap to hold space for
  357.          * the memory we just freed up.
  358.          */
  359.  
  360.         _SET_DUMMY(pdesc);
  361.  
  362.     }
  363.  
  364.     else {
  365.  
  366.         /*
  367.          * Whole region is NOT free, return part of it to OS
  368.          */
  369. #if !defined (_M_MPPC) && !defined (_M_M68K)
  370.          _free_partial_region(pdesc, size, index);
  371. #endif  /* !defined (_M_MPPC) && !defined (_M_M68K) */
  372.     }
  373.  
  374.     /*
  375.      * Exit paths
  376.      */
  377.  
  378.     return(1);  /* 1 = minimized region */
  379.  
  380. }
  381.  
  382.  
  383. /***
  384. *_free_partial_region() - Free part of a region to the OS
  385. *
  386. *Purpose:
  387. *   Free a portion of a region to the OS
  388. *
  389. *Entry:
  390. *   pdesc = descriptor of last free block in region
  391. *   size = amount of free mem at end of region (i.e., _MEMSIZE(pdesc))
  392. *   index = index of region
  393. *
  394. *Exit:
  395. *
  396. *Exceptions:
  397. *
  398. *******************************************************************************/
  399.  
  400. static void __cdecl _free_partial_region (
  401.     REG1 _PBLKDESC pdesc,
  402.     unsigned size,
  403.     int index
  404.     )
  405. {
  406.     unsigned left;
  407.     void * base;
  408.     REG2 _PBLKDESC pnew;
  409. #if defined (_M_MPPC) || defined (_M_M68K)
  410.     struct _heap_region_ *pHeapRegions;
  411. #endif  /* defined (_M_MPPC) || defined (_M_M68K) */
  412.  
  413.     /*
  414.      * Init a few variables.
  415.      */
  416.  
  417.     left = (size & (_PAGESIZE_-1));
  418.     base = (char *)_ADDRESS(pdesc);
  419.  
  420.     /*
  421.      * We return memory to the OS in page multiples.  If the
  422.      * free block is not page aligned, we'll insert a new free block
  423.      * to fill in the difference.
  424.      */
  425.  
  426.     if ( left != 0 ) {
  427.  
  428.         /*
  429.          * The block is not a multiple of pages so we need
  430.          * to adjust variables accordingly.
  431.          */
  432.  
  433.         size -= left;
  434.         base = (char *)base + left;
  435.     }
  436.  
  437.  
  438.     /*
  439.      * Return the free pages to the OS.
  440.      */
  441.  
  442. #if defined (_M_MPPC) || defined (_M_M68K)
  443.  
  444.     if (base)
  445.     {
  446.         DisposePtr(base);
  447.     }
  448.  
  449.     /*
  450.      * Adjust the region table entry
  451.      */
  452.  
  453.     pHeapRegions = (struct _heap_region_ *)(*hHeapRegions);
  454.     (pHeapRegions + index)->_currsize -= size;
  455.  
  456. #else  /* defined (_M_MPPC) || defined (_M_M68K) */
  457.  
  458.         if (!VirtualFree(base, size, MEM_DECOMMIT))
  459.          _heap_abort();
  460.  
  461.     /*
  462.      * Adjust the region table entry
  463.      */
  464.  
  465.     _heap_regions[index]._currsize -= size;
  466.  
  467. #endif  /* defined (_M_MPPC) || defined (_M_M68K) */
  468.  
  469.     /*
  470.      * Adjust the heap according to whether we released the whole
  471.      * free block or not. (Don't worry about consecutive dummies,
  472.      * we'll coalesce them later.)
  473.      *
  474.      * base = address of block we just gave back to OS
  475.      * size = size of block we gave back to OS
  476.      * left = size of block we did NOT give back to OS
  477.      */
  478.  
  479.     if ( left == 0 ) {
  480.  
  481.         /*
  482.          * The free block was released to the OS in its
  483.          * entirety.  Make the free block a dummy place holder.
  484.          */
  485.  
  486.         _SET_DUMMY(pdesc);
  487.  
  488.     }
  489.  
  490.     else {
  491.  
  492.         /*
  493.          * Did NOT release the whole free block to the OS.
  494.          * There's a block of free memory we want to leave
  495.          * in the heap.  Insert a dummy entry after it.
  496.          */
  497.  
  498.         if ( (pnew = __getempty()) == NULL )
  499.             _heap_abort();
  500.  
  501.         pnew->pblock = (char *)base;
  502.         _SET_DUMMY(pnew);
  503.  
  504.         pnew->pnextdesc = pdesc->pnextdesc;
  505.         pdesc->pnextdesc = pnew;
  506.  
  507.     }
  508.  
  509. }
  510.  
  511.  
  512. #endif  /* WINHEAP */
  513.