windows-nt/Source/XPSP1/NT/base/crts/crtw32/heap/heapmin.c

521 lines
13 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/***
*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 */