mirror of https://github.com/lianthony/NT4.0
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
10 KiB
520 lines
10 KiB
/***
|
|
*heapmin.c - Minimize the heap
|
|
*
|
|
* Copyright (c) 1989-1991, 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_.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#include <cruntime.h>
|
|
#include <oscalls.h>
|
|
#include <heap.h>
|
|
#include <malloc.h>
|
|
#include <os2dll.h>
|
|
#include <stdlib.h>
|
|
|
|
static int _CALLTYPE4 _heapmin_region(int, void *, _PBLKDESC);
|
|
static void _CALLTYPE4 _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 _CALLTYPE1 _heapmin(void)
|
|
{
|
|
REG1 int index;
|
|
_PBLKDESC pdesc;
|
|
|
|
#ifdef _OLDROVER_
|
|
REG2 _PBLKDESC pprev;
|
|
#else /* ndef _OLDROVER_ */
|
|
REG2 _PBLKDESC pdesc2;
|
|
#endif /* _OLDROVER_ */
|
|
|
|
void * regend;
|
|
int region_min_count = 0;
|
|
|
|
/*
|
|
* Lock the heap
|
|
*/
|
|
|
|
_mlock(_HEAP_LOCK);
|
|
|
|
/*
|
|
* Coalesce the heap (should return NULL)
|
|
*/
|
|
|
|
if ( _heap_search(_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 ) {
|
|
|
|
#ifdef _OLDROVER_
|
|
|
|
for ( _heap_desc.proverdesc = pprev = pdesc =
|
|
_heap_desc.pfirstdesc ; pdesc != &_heap_desc.sentinel ;
|
|
pprev = pdesc, pdesc = pdesc->pnextdesc ) {
|
|
|
|
/*
|
|
* set rover to first free block
|
|
*/
|
|
|
|
if ( _IS_FREE(pdesc) &&
|
|
!_IS_FREE(_heap_desc.proverdesc) )
|
|
_heap_desc.proverdesc = pdesc;
|
|
|
|
/*
|
|
* Check and remove consecutive dummy blocks
|
|
*/
|
|
|
|
if ( _IS_DUMMY(pprev) ) {
|
|
|
|
while ( _IS_DUMMY(pdesc) ) {
|
|
|
|
/*
|
|
* be sure that pdesc and pprev are
|
|
* not the same (i.e., not both equal
|
|
* to pfirstdesc)
|
|
*/
|
|
|
|
if ( pdesc == pprev )
|
|
break;
|
|
|
|
/*
|
|
* coalesce the dummy blocks
|
|
*/
|
|
|
|
pprev->pnextdesc = pdesc->pnextdesc;
|
|
_PUTEMPTY(pdesc);
|
|
|
|
/*
|
|
* advance pdesc and check for the
|
|
* sentinel
|
|
*/
|
|
|
|
if ( (pdesc = pprev->pnextdesc)
|
|
== &_heap_desc.sentinel )
|
|
goto endloop;
|
|
|
|
} /* dummy loop */
|
|
|
|
} /* if */
|
|
|
|
} /* heap loop */
|
|
|
|
/*
|
|
* If still necessary, reset the rover descriptor pointer
|
|
*/
|
|
|
|
endloop:
|
|
|
|
if ( !_IS_FREE(_heap_desc.proverdesc) )
|
|
_heap_desc.proverdesc = &_heap_desc.sentinel;
|
|
|
|
#else /* ndef _OLDROVER_ */
|
|
|
|
/*
|
|
* 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 */
|
|
|
|
#endif /* _OLDROVER_ */
|
|
|
|
} /* region_min_count */
|
|
|
|
/*
|
|
* Good return
|
|
*/
|
|
|
|
/* goodrtn: unreferenced label to be removed */
|
|
/*
|
|
* Release the heap lock
|
|
*/
|
|
|
|
_munlock(_HEAP_LOCK);
|
|
|
|
return(0);
|
|
|
|
#if 0
|
|
/* unreachable code tentatively removed */
|
|
|
|
/*
|
|
* Error return
|
|
*/
|
|
|
|
errrtn:
|
|
/*
|
|
* Release the heap lock
|
|
*/
|
|
|
|
_munlock(_HEAP_LOCK);
|
|
|
|
/* *** SET ERRNO *** */
|
|
return(-1);
|
|
#endif
|
|
}
|
|
|
|
|
|
/***
|
|
*_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 _CALLTYPE4 _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.
|
|
*/
|
|
|
|
_GETEMPTY(pnew); /* get a new block */
|
|
|
|
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 _CALLTYPE4 _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.
|
|
*/
|
|
|
|
#ifdef _CRUISER_
|
|
|
|
if ( DOSSETMEM(base, size, _DECOMMIT) != 0 )
|
|
_heap_abort();
|
|
|
|
#else /* ndef _CRUISER_ */
|
|
|
|
#ifdef _WIN32_
|
|
|
|
if (!VirtualFree(base, size, MEM_DECOMMIT))
|
|
_heap_abort();
|
|
|
|
#else /* ndef _WIN32_ */
|
|
|
|
#error ERROR - ONLY CRUISER OR WIN32 TARGET SUPPORTED!
|
|
|
|
#endif /* _WIN32_ */
|
|
|
|
#endif /* _CRUISER_ */
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
_GETEMPTY(pnew);
|
|
|
|
pnew->pblock = (char *)base;
|
|
_SET_DUMMY(pnew);
|
|
|
|
pnew->pnextdesc = pdesc->pnextdesc;
|
|
pdesc->pnextdesc = pnew;
|
|
|
|
}
|
|
|
|
}
|