521 lines
13 KiB
C
521 lines
13 KiB
C
|
/***
|
||
|
*heapmin.c - Minimize the heap
|
||
|
*
|
||
|
* Copyright (c) 1989-2001, Microsoft Corporation. All rights reserved.
|
||
|
*
|
||
|
*Purpose:
|
||
|
* Minimize the heap freeing as much memory as possible back
|
||
|
* to the OS.
|
||
|
*
|
||
|
*Revision History:
|
||
|
* 08-28-89 JCR Module created.
|
||
|
* 11-06-89 JCR Improved, partitioned
|
||
|
* 11-13-89 GJF Added MTHREAD support, also fixed copyright
|
||
|
* 12-14-89 GJF Couple of bug fixes, some tuning, cleaned up the
|
||
|
* formatting a bit and changed header file name to
|
||
|
* heap.h
|
||
|
* 12-20-89 GJF Removed references to plastdesc
|
||
|
* 03-11-90 GJF Replaced _cdecl with _CALLTYPE1, added #include
|
||
|
* <cruntime.h> and removed #include <register.h>.
|
||
|
* 03-29-90 GJF Made _heapmin_region() and _free_partial_region()
|
||
|
* _CALLTYPE4.
|
||
|
* 07-24-90 SBM Compiles cleanly with -W3 (tentatively removed
|
||
|
* unreferenced labels and unreachable code), removed
|
||
|
* '32' from API names
|
||
|
* 09-28-90 GJF New-style function declarators. Also, rewrote expr.
|
||
|
* to avoid using cast as lvalue.
|
||
|
* 12-04-90 SRW Changed to include <oscalls.h> instead of <doscalls.h>
|
||
|
* 12-06-90 SRW Added _CRUISER_ and _WIN32 conditionals.
|
||
|
* 12-28-90 SRW Added cast of void * to char * for Mips C Compiler
|
||
|
* 03-05-91 GJF Changed strategy for rover - old version available
|
||
|
* by #define-ing _OLDROVER_.
|
||
|
* 04-06-93 SKS Replace _CRTAPI* with __cdecl
|
||
|
* 03-03-94 GJF Changed references to _GETEMPTY macro to calls to
|
||
|
* the __getempty function. Added graceful handling for
|
||
|
* failure of the call to __getempty in _heapmin_region.
|
||
|
* However, failure in _free_partial_region will still
|
||
|
* result in termination via _heap_abort (very difficult
|
||
|
* to handle any other way, very unlikely to occur).
|
||
|
* 02-07-95 GJF Merged in Mac version. Removed obsolete _OLDROVER_
|
||
|
* support.
|
||
|
* 04-30-95 GJF Spliced on winheap version.
|
||
|
* 03-07-96 GJF Added support for small-block heap.
|
||
|
* 05-22-97 RDK New small-block heap scheme implemented.
|
||
|
* 09-26-97 BWT Remove POSIX
|
||
|
* 12-17-97 GJF Exception-safe locking.
|
||
|
* 09-30-98 GJF Bypass all small-block heap code when __sbh_initialized
|
||
|
* is 0.
|
||
|
* 11-19-98 GJF Merged in VC++ 5.0 small-block heap support.
|
||
|
* 05-01-99 PML Disable small-block heap for Win64.
|
||
|
* 05-17-99 PML Remove all Macintosh support.
|
||
|
* 06-22-99 GJF Removed old small-block heap from static libs.
|
||
|
*
|
||
|
*******************************************************************************/
|
||
|
|
||
|
|
||
|
#ifdef WINHEAP
|
||
|
|
||
|
|
||
|
#include <cruntime.h>
|
||
|
#include <windows.h>
|
||
|
#include <errno.h>
|
||
|
#include <malloc.h>
|
||
|
#include <mtdll.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <winheap.h>
|
||
|
|
||
|
/***
|
||
|
*_heapmin() - Minimize the heap
|
||
|
*
|
||
|
*Purpose:
|
||
|
* Minimize the heap freeing as much memory as possible back
|
||
|
* to the OS.
|
||
|
*
|
||
|
*Entry:
|
||
|
* (void)
|
||
|
*
|
||
|
*Exit:
|
||
|
*
|
||
|
* 0 = no error has occurred
|
||
|
* -1 = an error has occurred (errno is set)
|
||
|
*
|
||
|
*Exceptions:
|
||
|
*
|
||
|
*******************************************************************************/
|
||
|
|
||
|
int __cdecl _heapmin(void)
|
||
|
{
|
||
|
#ifndef _WIN64
|
||
|
if ( __active_heap == __V6_HEAP ) {
|
||
|
#ifdef _MT
|
||
|
_mlock( _HEAP_LOCK );
|
||
|
__try {
|
||
|
#endif
|
||
|
__sbh_heapmin();
|
||
|
#ifdef _MT
|
||
|
}
|
||
|
__finally {
|
||
|
_munlock( _HEAP_LOCK );
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
#ifdef CRTDLL
|
||
|
else if ( __active_heap == __V5_HEAP ) {
|
||
|
/*
|
||
|
* Minimize the small-block heap by calling _sbh_decommit_pages()
|
||
|
* with a big enough count to ensure every page which can be
|
||
|
* decommitted, is.
|
||
|
*/
|
||
|
#ifdef _MT
|
||
|
_mlock( _HEAP_LOCK );
|
||
|
__try {
|
||
|
#endif
|
||
|
__old_sbh_decommit_pages( 2 * _OLD_PAGES_PER_COMMITMENT );
|
||
|
#ifdef _MT
|
||
|
}
|
||
|
__finally {
|
||
|
_munlock( _HEAP_LOCK );
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
#endif /* CRTDLL */
|
||
|
#endif /* ndef _WIN64 */
|
||
|
|
||
|
if ( HeapCompact( _crtheap, 0 ) == 0 ) {
|
||
|
|
||
|
if ( GetLastError() == ERROR_CALL_NOT_IMPLEMENTED ) {
|
||
|
_doserrno = ERROR_CALL_NOT_IMPLEMENTED;
|
||
|
errno = ENOSYS;
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
else {
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#else /* ndef WINHEAP */
|
||
|
|
||
|
|
||
|
#include <cruntime.h>
|
||
|
#include <heap.h>
|
||
|
#include <malloc.h>
|
||
|
#include <mtdll.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <windows.h>
|
||
|
|
||
|
static int __cdecl _heapmin_region(int, void *, _PBLKDESC);
|
||
|
static void __cdecl _free_partial_region(_PBLKDESC, unsigned, int);
|
||
|
|
||
|
/***
|
||
|
*_heapmin() - Minimize the heap
|
||
|
*
|
||
|
*Purpose:
|
||
|
* Minimize the heap freeing as much memory as possible back
|
||
|
* to the OS.
|
||
|
*
|
||
|
*Entry:
|
||
|
* (void)
|
||
|
*
|
||
|
*Exit:
|
||
|
* 0 = no error has occurred
|
||
|
* -1 = an error has occurred (errno is set)
|
||
|
*
|
||
|
*Exceptions:
|
||
|
*
|
||
|
*******************************************************************************/
|
||
|
|
||
|
int __cdecl _heapmin(void)
|
||
|
{
|
||
|
REG1 int index;
|
||
|
_PBLKDESC pdesc;
|
||
|
REG2 _PBLKDESC pdesc2;
|
||
|
void * regend;
|
||
|
int region_min_count = 0;
|
||
|
|
||
|
/*
|
||
|
* Lock the heap
|
||
|
*/
|
||
|
|
||
|
_mlock(_HEAP_LOCK);
|
||
|
|
||
|
/*
|
||
|
* Coalesce the heap (should return NULL)
|
||
|
*/
|
||
|
|
||
|
if ( _heap_search((unsigned)_HEAP_COALESCE) != NULL )
|
||
|
_heap_abort();
|
||
|
|
||
|
/*
|
||
|
* Loop through the region descriptor table freeing as much
|
||
|
* memory to the OS as possible.
|
||
|
*/
|
||
|
|
||
|
for ( index=0 ; index < _HEAP_REGIONMAX ; index++ ) {
|
||
|
|
||
|
if ( _heap_regions[index]._regbase == NULL )
|
||
|
continue; /* region entry is empty */
|
||
|
|
||
|
/*
|
||
|
* Get the entry that contains the last address of
|
||
|
* the region (allocated so far, that is).
|
||
|
*/
|
||
|
|
||
|
regend = (char *) _heap_regions[index]._regbase +
|
||
|
_heap_regions[index]._currsize - 1;
|
||
|
|
||
|
if ( _heap_findaddr(regend, &pdesc) != _HEAPFIND_WITHIN )
|
||
|
_heap_abort(); /* last address not within a block */
|
||
|
|
||
|
/*
|
||
|
* See if the containing block is free
|
||
|
*/
|
||
|
|
||
|
if ( !(_IS_FREE(pdesc)) )
|
||
|
continue; /* block is not free */
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Region ends with a free block, go free as much mem
|
||
|
* as possible.
|
||
|
*/
|
||
|
|
||
|
region_min_count += _heapmin_region(index, regend, pdesc);
|
||
|
|
||
|
|
||
|
} /* region loop */
|
||
|
|
||
|
/*
|
||
|
* By minimizing the heap, we've likely invalidated the rover and
|
||
|
* may have produced contiguous dummy blocks so:
|
||
|
*
|
||
|
* (1) reset the rover
|
||
|
* (2) coalesce contiguous dummy blocks
|
||
|
*/
|
||
|
|
||
|
if ( region_min_count ) {
|
||
|
|
||
|
/*
|
||
|
* Set proverdesc to pfirstdesc
|
||
|
*/
|
||
|
|
||
|
_heap_desc.proverdesc = _heap_desc.pfirstdesc;
|
||
|
|
||
|
for ( pdesc = _heap_desc.pfirstdesc ; pdesc !=
|
||
|
&_heap_desc.sentinel ; pdesc = pdesc->pnextdesc ) {
|
||
|
|
||
|
/*
|
||
|
* Check and remove consecutive dummy blocks
|
||
|
*/
|
||
|
|
||
|
if ( _IS_DUMMY(pdesc) ) {
|
||
|
|
||
|
for ( pdesc2 = pdesc->pnextdesc ;
|
||
|
_IS_DUMMY(pdesc2) ;
|
||
|
pdesc2 = pdesc->pnextdesc ) {
|
||
|
|
||
|
/*
|
||
|
* coalesce the dummy blocks
|
||
|
*/
|
||
|
|
||
|
pdesc->pnextdesc = pdesc2->pnextdesc;
|
||
|
_PUTEMPTY(pdesc2);
|
||
|
|
||
|
} /* dummy loop */
|
||
|
|
||
|
} /* if */
|
||
|
|
||
|
} /* heap loop */
|
||
|
|
||
|
} /* region_min_count */
|
||
|
|
||
|
/*
|
||
|
* Good return
|
||
|
*/
|
||
|
|
||
|
/* goodrtn: unreferenced label to be removed */
|
||
|
/*
|
||
|
* Release the heap lock
|
||
|
*/
|
||
|
|
||
|
_munlock(_HEAP_LOCK);
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***
|
||
|
*_heapmin_region() - Minimize a region
|
||
|
*
|
||
|
*Purpose:
|
||
|
* Free as much of a region back to the OS as possible.
|
||
|
*
|
||
|
*Entry:
|
||
|
* int index = index of the region in the region table
|
||
|
* void * regend = last valid address in region
|
||
|
* pdesc = pointer to the last block of memory in the region
|
||
|
* (it has already been determined that this block is free)
|
||
|
*
|
||
|
*Exit:
|
||
|
* int 1 = minimized region
|
||
|
* 0 = no change to region
|
||
|
*
|
||
|
*Exceptions:
|
||
|
*
|
||
|
*******************************************************************************/
|
||
|
|
||
|
static int __cdecl _heapmin_region (
|
||
|
int index,
|
||
|
void * regend,
|
||
|
REG1 _PBLKDESC pdesc
|
||
|
)
|
||
|
{
|
||
|
unsigned size;
|
||
|
REG2 _PBLKDESC pnew;
|
||
|
|
||
|
/*
|
||
|
* Init some variables
|
||
|
*
|
||
|
* regend = 1st address AFTER region
|
||
|
* size = amount of free memory at end of current region
|
||
|
*/
|
||
|
|
||
|
regend = (char *) regend + 1; /* "regend++" give compiler error... */
|
||
|
size = ((char *)regend - (char *)_ADDRESS(pdesc));
|
||
|
|
||
|
|
||
|
/*
|
||
|
* See if there's enough free memory to release to the OS.
|
||
|
* (NOTE: Need more than a page since we may need a back pointer.)
|
||
|
*/
|
||
|
|
||
|
if ( size <= _PAGESIZE_ )
|
||
|
return(0); /* 0 = no change to region */
|
||
|
|
||
|
/*
|
||
|
* We're going to free some memory to the OS. See if the
|
||
|
* free block crosses the end of the region and, if so,
|
||
|
* split up the block appropriately.
|
||
|
*/
|
||
|
|
||
|
if ( (_MEMSIZE(pdesc) - size) != 0 ) {
|
||
|
|
||
|
/*
|
||
|
* The free block spans the end of the region.
|
||
|
* Divide it up.
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* Get an empty descriptor
|
||
|
*/
|
||
|
|
||
|
if ( (pnew = __getempty()) == NULL )
|
||
|
return(0);
|
||
|
|
||
|
pnew->pblock = regend; /* init block pointer */
|
||
|
* (_PBLKDESC*)regend = pnew; /* init back pointer */
|
||
|
_SET_FREE(pnew); /* set the block free */
|
||
|
|
||
|
pnew->pnextdesc = pdesc->pnextdesc; /* link it in */
|
||
|
pdesc->pnextdesc = pnew;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* At this point, we have a free block of memory that goes
|
||
|
* up to (but not exceeding) the end of the region.
|
||
|
*
|
||
|
* pdesc = descriptor of the last free block in region
|
||
|
* size = amount of free mem at end of region (i.e., _MEMSIZE(pdesc))
|
||
|
* regend = 1st address AFTER end of region
|
||
|
*/
|
||
|
|
||
|
|
||
|
/*
|
||
|
* See if we should return the whole region of only part of it.
|
||
|
*/
|
||
|
|
||
|
if ( _ADDRESS(pdesc) == _heap_regions[index]._regbase ) {
|
||
|
|
||
|
/*
|
||
|
* Whole region is free, return it to OS
|
||
|
*/
|
||
|
|
||
|
_heap_free_region(index);
|
||
|
|
||
|
/*
|
||
|
* Put a dummy block in the heap to hold space for
|
||
|
* the memory we just freed up.
|
||
|
*/
|
||
|
|
||
|
_SET_DUMMY(pdesc);
|
||
|
|
||
|
}
|
||
|
|
||
|
else {
|
||
|
|
||
|
/*
|
||
|
* Whole region is NOT free, return part of it to OS
|
||
|
*/
|
||
|
_free_partial_region(pdesc, size, index);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Exit paths
|
||
|
*/
|
||
|
|
||
|
return(1); /* 1 = minimized region */
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/***
|
||
|
*_free_partial_region() - Free part of a region to the OS
|
||
|
*
|
||
|
*Purpose:
|
||
|
* Free a portion of a region to the OS
|
||
|
*
|
||
|
*Entry:
|
||
|
* pdesc = descriptor of last free block in region
|
||
|
* size = amount of free mem at end of region (i.e., _MEMSIZE(pdesc))
|
||
|
* index = index of region
|
||
|
*
|
||
|
*Exit:
|
||
|
*
|
||
|
*Exceptions:
|
||
|
*
|
||
|
*******************************************************************************/
|
||
|
|
||
|
static void __cdecl _free_partial_region (
|
||
|
REG1 _PBLKDESC pdesc,
|
||
|
unsigned size,
|
||
|
int index
|
||
|
)
|
||
|
{
|
||
|
unsigned left;
|
||
|
void * base;
|
||
|
REG2 _PBLKDESC pnew;
|
||
|
|
||
|
/*
|
||
|
* Init a few variables.
|
||
|
*/
|
||
|
|
||
|
left = (size & (_PAGESIZE_-1));
|
||
|
base = (char *)_ADDRESS(pdesc);
|
||
|
|
||
|
/*
|
||
|
* We return memory to the OS in page multiples. If the
|
||
|
* free block is not page aligned, we'll insert a new free block
|
||
|
* to fill in the difference.
|
||
|
*/
|
||
|
|
||
|
if ( left != 0 ) {
|
||
|
|
||
|
/*
|
||
|
* The block is not a multiple of pages so we need
|
||
|
* to adjust variables accordingly.
|
||
|
*/
|
||
|
|
||
|
size -= left;
|
||
|
base = (char *)base + left;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Return the free pages to the OS.
|
||
|
*/
|
||
|
|
||
|
if (!VirtualFree(base, size, MEM_DECOMMIT))
|
||
|
_heap_abort();
|
||
|
|
||
|
/*
|
||
|
* Adjust the region table entry
|
||
|
*/
|
||
|
|
||
|
_heap_regions[index]._currsize -= size;
|
||
|
|
||
|
/*
|
||
|
* Adjust the heap according to whether we released the whole
|
||
|
* free block or not. (Don't worry about consecutive dummies,
|
||
|
* we'll coalesce them later.)
|
||
|
*
|
||
|
* base = address of block we just gave back to OS
|
||
|
* size = size of block we gave back to OS
|
||
|
* left = size of block we did NOT give back to OS
|
||
|
*/
|
||
|
|
||
|
if ( left == 0 ) {
|
||
|
|
||
|
/*
|
||
|
* The free block was released to the OS in its
|
||
|
* entirety. Make the free block a dummy place holder.
|
||
|
*/
|
||
|
|
||
|
_SET_DUMMY(pdesc);
|
||
|
|
||
|
}
|
||
|
|
||
|
else {
|
||
|
|
||
|
/*
|
||
|
* Did NOT release the whole free block to the OS.
|
||
|
* There's a block of free memory we want to leave
|
||
|
* in the heap. Insert a dummy entry after it.
|
||
|
*/
|
||
|
|
||
|
if ( (pnew = __getempty()) == NULL )
|
||
|
_heap_abort();
|
||
|
|
||
|
pnew->pblock = (char *)base;
|
||
|
_SET_DUMMY(pnew);
|
||
|
|
||
|
pnew->pnextdesc = pdesc->pnextdesc;
|
||
|
pdesc->pnextdesc = pnew;
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
#endif /* WINHEAP */
|