summaryrefslogtreecommitdiffstats
path: root/private/crt32/heap/heapmin.c
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/crt32/heap/heapmin.c
downloadNT4.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.c520
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;
+
+ }
+
+}