437 lines
10 KiB
C
437 lines
10 KiB
C
/***
|
|
*heapgrow.c - Grow the heap
|
|
*
|
|
* Copyright (c) 1989-2001, Microsoft Corporation. All rights reserved.
|
|
*
|
|
*Purpose:
|
|
* Get memory from OS and add to the heap.
|
|
*
|
|
*Revision History:
|
|
* 06-06-89 JCR Module created.
|
|
* 07-19-89 JCR Added region support
|
|
* 11-07-89 JCR Region table is no longer "packed"
|
|
* 11-08-89 JCR Use new _ROUND/_ROUND2 macros
|
|
* 11-10-89 JCR Don't abort on ERROR_NOT_ENOUGH_MEMORY
|
|
* 11-13-89 GJF Fixed copyright
|
|
* 12-18-89 GJF Removed DEBUG286 stuff, a little tuning, cleaned up
|
|
* the formatting a bit, changed header file name to
|
|
* heap.h, also added _cdecl to functions (that didn't
|
|
* already have explicit calling type)
|
|
* 03-11-90 GJF Replaced _cdecl with _CALLTYPE1, added #include
|
|
* <cruntime.h> and removed #include <register.h>.
|
|
* 03-29-90 GJF Made _heap_new_region() _CALLTYPE4.
|
|
* 07-24-90 SBM Compiles cleanly with -W3 (tentatively removed
|
|
* unreferenced labels), removed '32' from API names
|
|
* 09-28-90 GJF New-style function declarators.
|
|
* 12-04-90 SRW Changed to include <oscalls.h> instead of <doscalls.h>
|
|
* 12-06-90 SRW Added _CRUISER_ and _WIN32 conditionals.
|
|
* 02-01-91 SRW Changed for new VirtualAlloc interface (_WIN32_)
|
|
* 04-09-91 PNT Added _MAC_ conditional
|
|
* 04-26-91 SRW Removed level 3 warnings
|
|
* 04-06-93 SKS Replace _CRTAPI* with __cdecl
|
|
* 04-26-93 SKS Change _HEAP_MAXREGIONSIZE to _heap_maxregsize
|
|
* 09-06-94 CFW Remove Cruiser support.
|
|
* 02-14-95 GJF Appended Mac version of source file.
|
|
* 04-30-95 GJF Made conditional on WINHEAP.
|
|
* 05-17-99 PML Remove all Macintosh support.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#ifndef WINHEAP
|
|
|
|
#include <cruntime.h>
|
|
#include <oscalls.h>
|
|
#include <heap.h>
|
|
#include <malloc.h>
|
|
#include <stdlib.h>
|
|
|
|
static int __cdecl _heap_new_region(unsigned, size_t);
|
|
|
|
|
|
/***
|
|
*_heap_grow() - Grow the heap
|
|
*
|
|
*Purpose:
|
|
* Get memory from the OS and add it to the heap.
|
|
*
|
|
*Entry:
|
|
* size_t _size = user's block request
|
|
*
|
|
*Exit:
|
|
* 0 = success, new mem is in the heap
|
|
* -1 = failure
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
int __cdecl _heap_grow (
|
|
REG1 size_t size
|
|
)
|
|
{
|
|
REG2 int index;
|
|
int free_entry = -1;
|
|
|
|
/*
|
|
* Bump size to include header and round to nearest page boundary.
|
|
*/
|
|
|
|
size += _HDRSIZE;
|
|
size = _ROUND2(size,_PAGESIZE_);
|
|
|
|
/*
|
|
* Loop through the region table looking for an existing region
|
|
* we can grow. Remember the index of the first null region entry.
|
|
*
|
|
* size = size of grow request
|
|
*/
|
|
|
|
for (index = 0; index < _HEAP_REGIONMAX; index++) {
|
|
|
|
if ( (_heap_regions[index]._totalsize -
|
|
_heap_regions[index]._currsize) >= size )
|
|
|
|
/*
|
|
* Grow this region to satisfy the request.
|
|
*/
|
|
|
|
return( _heap_grow_region(index, size) );
|
|
|
|
|
|
if ( (free_entry == -1) &&
|
|
(_heap_regions[index]._regbase == NULL) )
|
|
|
|
/*
|
|
* Remember 1st free table entry for later
|
|
*/
|
|
|
|
free_entry = index;
|
|
|
|
}
|
|
|
|
/*
|
|
* Could not find any existing regions to grow. Try to
|
|
* get a new region.
|
|
*
|
|
* size = size of grow request
|
|
* free_entry = index of first free entry in table
|
|
*/
|
|
|
|
if ( free_entry >= 0 )
|
|
|
|
/*
|
|
* Get a new region to satisfy the request.
|
|
*/
|
|
|
|
return( _heap_new_region(free_entry, size) );
|
|
|
|
else
|
|
/*
|
|
* No free table entries: return an error.
|
|
*/
|
|
|
|
return(-1);
|
|
|
|
}
|
|
|
|
|
|
/***
|
|
*_heap_new_region() - Get a new heap region
|
|
*
|
|
*Purpose:
|
|
* Get a new heap region and put it in the region table.
|
|
* Also, grow it large enough to support the caller's
|
|
* request.
|
|
*
|
|
* NOTES:
|
|
* (1) Caller has verified that there is room in the _heap_region
|
|
* table for another region.
|
|
* (2) The caller must have rounded the size to a page boundary.
|
|
*
|
|
*Entry:
|
|
* int index = index in table where new region data should go
|
|
* size_t size = size of request (this has been rounded to a
|
|
* page-sized boundary)
|
|
*
|
|
*Exit:
|
|
* 0 = success
|
|
* -1 = failure
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
static int __cdecl _heap_new_region (
|
|
REG1 unsigned index,
|
|
size_t size
|
|
)
|
|
{
|
|
void * region;
|
|
REG2 unsigned int regsize;
|
|
|
|
#ifdef DEBUG
|
|
|
|
int i;
|
|
|
|
/*
|
|
* Make sure the size has been rounded to a page boundary
|
|
*/
|
|
|
|
if (size & (_PAGESIZE_-1))
|
|
_heap_abort();
|
|
|
|
/*
|
|
* Make sure there's a free slot in the table
|
|
*/
|
|
|
|
for (i=0; i < _HEAP_REGIONMAX; i++) {
|
|
if (_heap_regions[i]._regbase == NULL)
|
|
break;
|
|
}
|
|
|
|
if (i >= _HEAP_REGIONMAX)
|
|
_heap_abort();
|
|
|
|
#endif
|
|
|
|
/*
|
|
* Round the heap region size to a page boundary (in case
|
|
* the user played with it).
|
|
*/
|
|
|
|
regsize = _ROUND2(_heap_regionsize, _PAGESIZE_);
|
|
|
|
/*
|
|
* To acommodate large users, request twice
|
|
* as big a region next time around.
|
|
*/
|
|
|
|
if ( _heap_regionsize < _heap_maxregsize )
|
|
_heap_regionsize *= 2 ;
|
|
|
|
/*
|
|
* See if region is big enough for request
|
|
*/
|
|
|
|
if (regsize < size)
|
|
regsize = size;
|
|
|
|
/*
|
|
* Go get the new region
|
|
*/
|
|
|
|
if (!(region = VirtualAlloc(NULL, regsize, MEM_RESERVE,
|
|
PAGE_READWRITE)))
|
|
goto error;
|
|
|
|
/*
|
|
* Put the new region in the table.
|
|
*/
|
|
|
|
_heap_regions[index]._regbase = region;
|
|
_heap_regions[index]._totalsize = regsize;
|
|
_heap_regions[index]._currsize = 0;
|
|
|
|
|
|
/*
|
|
* Grow the region to satisfy the size request.
|
|
*/
|
|
|
|
if (_heap_grow_region(index, size) != 0) {
|
|
|
|
/*
|
|
* Ouch. Allocated a region but couldn't commit
|
|
* any pages in it. Free region and return error.
|
|
*/
|
|
|
|
_heap_free_region(index);
|
|
goto error;
|
|
}
|
|
|
|
|
|
/*
|
|
* Good return
|
|
*/
|
|
|
|
/* done: unreferenced label to be removed */
|
|
return(0);
|
|
|
|
/*
|
|
* Error return
|
|
*/
|
|
|
|
error:
|
|
return(-1);
|
|
|
|
}
|
|
|
|
|
|
/***
|
|
*_heap_grow_region() - Grow a heap region
|
|
*
|
|
*Purpose:
|
|
* Grow a region and add the new memory to the heap.
|
|
*
|
|
* NOTES:
|
|
* (1) The caller must have rounded the size to a page boundary.
|
|
*
|
|
*Entry:
|
|
* unsigned index = index of region in the _heap_regions[] table
|
|
* size_t size = size of request (this has been rounded to a
|
|
* page-sized boundary)
|
|
*
|
|
*Exit:
|
|
* 0 = success
|
|
* -1 = failure
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
int __cdecl _heap_grow_region (
|
|
REG1 unsigned index,
|
|
size_t size
|
|
)
|
|
{
|
|
size_t left;
|
|
REG2 size_t growsize;
|
|
void * base;
|
|
unsigned dosretval;
|
|
|
|
|
|
/*
|
|
* Init some variables
|
|
* left = space left in region
|
|
* base = base of next section of region to validate
|
|
*/
|
|
|
|
left = _heap_regions[index]._totalsize -
|
|
_heap_regions[index]._currsize;
|
|
|
|
base = (char *) _heap_regions[index]._regbase +
|
|
_heap_regions[index]._currsize;
|
|
|
|
/*
|
|
* Make sure we can satisfy request
|
|
*/
|
|
|
|
if (left < size)
|
|
goto error;
|
|
|
|
/*
|
|
* Round size up to next _heap_growsize boundary.
|
|
* (Must round _heap_growsize itself to page boundary, in
|
|
* case user set it himself).
|
|
*/
|
|
|
|
growsize = _ROUND2(_heap_growsize, _PAGESIZE_);
|
|
growsize = _ROUND(size, growsize);
|
|
|
|
if (left < growsize)
|
|
growsize = left;
|
|
|
|
/*
|
|
* Validate the new portion of the region
|
|
*/
|
|
|
|
if (!VirtualAlloc(base, growsize, MEM_COMMIT, PAGE_READWRITE))
|
|
dosretval = GetLastError();
|
|
else
|
|
dosretval = 0;
|
|
|
|
if (dosretval)
|
|
/*
|
|
* Error committing pages. If out of memory, return
|
|
* error, else abort.
|
|
*/
|
|
|
|
if (dosretval == ERROR_NOT_ENOUGH_MEMORY)
|
|
goto error;
|
|
else
|
|
_heap_abort();
|
|
|
|
|
|
/*
|
|
* Update the region data base
|
|
*/
|
|
|
|
_heap_regions[index]._currsize += growsize;
|
|
|
|
|
|
#ifdef DEBUG
|
|
/*
|
|
* The current size should never be greater than the total size
|
|
*/
|
|
|
|
if (_heap_regions[index]._currsize > _heap_regions[index]._totalsize)
|
|
_heap_abort();
|
|
#endif
|
|
|
|
|
|
/*
|
|
* Add the memory to the heap
|
|
*/
|
|
|
|
if (_heap_addblock(base, growsize) != 0)
|
|
_heap_abort();
|
|
|
|
|
|
/*
|
|
* Good return
|
|
*/
|
|
|
|
/* done: unreferenced label to be removed */
|
|
return(0);
|
|
|
|
/*
|
|
* Error return
|
|
*/
|
|
|
|
error:
|
|
return(-1);
|
|
|
|
}
|
|
|
|
|
|
/***
|
|
*_heap_free_region() - Free up a region
|
|
*
|
|
*Purpose:
|
|
* Return a heap region to the OS and zero out
|
|
* corresponding region data entry.
|
|
*
|
|
*Entry:
|
|
* int index = index of region to be freed
|
|
*
|
|
*Exit:
|
|
* void
|
|
*
|
|
*Exceptions:
|
|
*
|
|
*******************************************************************************/
|
|
|
|
void __cdecl _heap_free_region (
|
|
REG1 int index
|
|
)
|
|
{
|
|
|
|
/*
|
|
* Give the memory back to the OS
|
|
*/
|
|
|
|
if (!VirtualFree(_heap_regions[index]._regbase, 0, MEM_RELEASE))
|
|
_heap_abort();
|
|
|
|
/*
|
|
* Zero out the heap region entry
|
|
*/
|
|
|
|
_heap_regions[index]._regbase = NULL;
|
|
_heap_regions[index]._currsize =
|
|
_heap_regions[index]._totalsize = 0;
|
|
|
|
}
|
|
|
|
|
|
#endif /* WINHEAP */
|