diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/crt32/heap/heapmin.c | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/crt32/heap/heapmin.c')
-rw-r--r-- | private/crt32/heap/heapmin.c | 520 |
1 files changed, 520 insertions, 0 deletions
diff --git a/private/crt32/heap/heapmin.c b/private/crt32/heap/heapmin.c new file mode 100644 index 000000000..dade55a47 --- /dev/null +++ b/private/crt32/heap/heapmin.c @@ -0,0 +1,520 @@ +/*** +*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; + + } + +} |