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 >
Wrap
C/C++ Source or Header
|
1998-06-17
|
13KB
|
513 lines
/***
*heapmin.c - Minimize the heap
*
* Copyright (c) 1989-1997, Microsoft Corporation. All rights reserved.
*
*Purpose:
* Minimize the heap freeing as much memory as possible back
* to the OS.
*
*******************************************************************************/
#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)
{
/*
* Minimize the small-block heap by calling _sbh_decommit_pages()
* with a big enough count to ensure every page which can be
* decommitted, is.
*/
_mlock(_HEAP_LOCK);
__sbh_heapmin();
_munlock(_HEAP_LOCK);
if ( HeapCompact( _crtheap, 0 ) == 0 ) {
if ( GetLastError() == ERROR_CALL_NOT_IMPLEMENTED ) {
_doserrno = ERROR_CALL_NOT_IMPLEMENTED;
errno = ENOSYS;
}
return -1;
}
else {
return 0;
}
}
#else /* WINHEAP */
#include <cruntime.h>
#include <heap.h>
#include <malloc.h>
#include <mtdll.h>
#include <stdlib.h>
#if defined (_M_MPPC) || defined (_M_M68K)
#include <macos\memory.h> // Mac OS interface header
#else /* defined (_M_MPPC) || defined (_M_M68K) */
#include <windows.h>
#endif /* defined (_M_MPPC) || defined (_M_M68K) */
static int __cdecl _heapmin_region(int, void *, _PBLKDESC);
static void __cdecl _free_partial_region(_PBLKDESC, unsigned, int);
#if defined (_M_MPPC) || defined (_M_M68K)
extern Handle hHeapRegions;
extern int _heap_region_table_cur;
#endif /* defined (_M_MPPC) || defined (_M_M68K) */
/***
*_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;
#if defined (_M_MPPC) || defined (_M_M68K)
struct _heap_region_ *pHeapRegions;
#endif /* defined (_M_MPPC) || defined (_M_M68K) */
/*
* 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.
*/
#if defined (_M_MPPC) || defined (_M_M68K)
for ( index=0 ; index < _heap_region_table_cur ; index++ ) {
pHeapRegions = (struct _heap_region_ *)(*hHeapRegions);
if ( (pHeapRegions + 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 *) ( (pHeapRegions + index)->_regbase) +
(pHeapRegions + index)->_currsize - 1;
#else /* defined (_M_MPPC) || defined (_M_M68K) */
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;
#endif /* defined (_M_MPPC) || defined (_M_M68K) */
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;
#if defined (_M_MPPC) || defined (_M_M68K)
struct _heap_region_ *pHeapRegions;
#endif /* defined (_M_MPPC) || defined (_M_M68K) */
/*
* 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 defined (_M_MPPC) || defined (_M_M68K)
pHeapRegions = (struct _heap_region_ *)(*hHeapRegions);
if ( _ADDRESS(pdesc) == (pHeapRegions + index)->_regbase ) {
#else /* defined (_M_MPPC) || defined (_M_M68K) */
if ( _ADDRESS(pdesc) == _heap_regions[index]._regbase ) {
#endif /* defined (_M_MPPC) || defined (_M_M68K) */
/*
* 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
*/
#if !defined (_M_MPPC) && !defined (_M_M68K)
_free_partial_region(pdesc, size, index);
#endif /* !defined (_M_MPPC) && !defined (_M_M68K) */
}
/*
* 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;
#if defined (_M_MPPC) || defined (_M_M68K)
struct _heap_region_ *pHeapRegions;
#endif /* defined (_M_MPPC) || defined (_M_M68K) */
/*
* 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 defined (_M_MPPC) || defined (_M_M68K)
if (base)
{
DisposePtr(base);
}
/*
* Adjust the region table entry
*/
pHeapRegions = (struct _heap_region_ *)(*hHeapRegions);
(pHeapRegions + index)->_currsize -= size;
#else /* defined (_M_MPPC) || defined (_M_M68K) */
if (!VirtualFree(base, size, MEM_DECOMMIT))
_heap_abort();
/*
* Adjust the region table entry
*/
_heap_regions[index]._currsize -= size;
#endif /* defined (_M_MPPC) || defined (_M_M68K) */
/*
* 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 */