344 lines
16 KiB
C
344 lines
16 KiB
C
|
/****************************************************************************
|
||
|
*
|
||
|
* Open Watcom Project
|
||
|
*
|
||
|
* Portions Copyright (c) 1983-2002 Sybase, Inc. All Rights Reserved.
|
||
|
*
|
||
|
* ========================================================================
|
||
|
*
|
||
|
* This file contains Original Code and/or Modifications of Original
|
||
|
* Code as defined in and that are subject to the Sybase Open Watcom
|
||
|
* Public License version 1.0 (the 'License'). You may not use this file
|
||
|
* except in compliance with the License. BY USING THIS FILE YOU AGREE TO
|
||
|
* ALL TERMS AND CONDITIONS OF THE LICENSE. A copy of the License is
|
||
|
* provided with the Original Code and Modifications, and is also
|
||
|
* available at www.sybase.com/developer/opensource.
|
||
|
*
|
||
|
* The Original Code and all software distributed under the License are
|
||
|
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||
|
* EXPRESS OR IMPLIED, AND SYBASE AND ALL CONTRIBUTORS HEREBY DISCLAIM
|
||
|
* ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF
|
||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR
|
||
|
* NON-INFRINGEMENT. Please see the License for the specific language
|
||
|
* governing rights and limitations under the License.
|
||
|
*
|
||
|
* ========================================================================
|
||
|
*
|
||
|
* Description: Heart of the heap manager. Do not break
|
||
|
* unless you have a death wish.
|
||
|
*
|
||
|
****************************************************************************/
|
||
|
|
||
|
|
||
|
#include "variety.h"
|
||
|
#include <limits.h>
|
||
|
#include <malloc.h>
|
||
|
#include "heap.h"
|
||
|
|
||
|
|
||
|
#if defined(M_I86)
|
||
|
extern unsigned setup_ds( unsigned );
|
||
|
#pragma aux setup_ds = \
|
||
|
"push ax" \
|
||
|
"mov ax,ds" \
|
||
|
"pop ds" \
|
||
|
parm [ax] value [ax];
|
||
|
#define setup_segment( _x ) _x = setup_ds( _x );
|
||
|
#else
|
||
|
#define setup_segment( _x ) (void)(_x = _x);
|
||
|
#endif
|
||
|
|
||
|
//
|
||
|
// input:
|
||
|
// size - #bytes to allocate
|
||
|
// segment - 16bit Intel data selector containing heap
|
||
|
// offset - address of heap control block
|
||
|
// if 16bit Intel -> offset within segment
|
||
|
// else -> absolute pointer value
|
||
|
//
|
||
|
// output:
|
||
|
// result - address of allocated storage or zero on failure
|
||
|
// if 16bit Intel -> offset within segment
|
||
|
// else -> absolute pointer value
|
||
|
//
|
||
|
unsigned __MemAllocator( unsigned size, unsigned segment, unsigned offset )
|
||
|
{
|
||
|
frlptr result;
|
||
|
result = 0; // assume the worst
|
||
|
|
||
|
setup_segment( segment ); // setup DS for 16bit Intel
|
||
|
|
||
|
if( size != 0 ) { // quit if size is zero
|
||
|
unsigned new_size;
|
||
|
new_size = size + TAG_SIZE + ROUND_SIZE;// round up size
|
||
|
if( new_size >= size ) { // quit if overflowed
|
||
|
struct heapblkp _WCI86NEAR *heap;
|
||
|
unsigned largest;
|
||
|
heap = (struct heapblkp _WCI86NEAR *)offset;
|
||
|
size = new_size & ~ROUND_SIZE; // make size even
|
||
|
largest = heap->largest_blk;
|
||
|
if( size < FRL_SIZE ) {
|
||
|
size = FRL_SIZE;
|
||
|
}
|
||
|
if( size <= largest ) { // quit if size too big
|
||
|
frlptr pcur;
|
||
|
unsigned len;
|
||
|
pcur = heap->rover; // start at rover
|
||
|
largest = heap->b4rover;
|
||
|
if( size <= largest ) { // check size with rover
|
||
|
pcur = heap->freehead.next; // start at beginning
|
||
|
largest = 0; // reset largest block size
|
||
|
}
|
||
|
for(;;) { // search free list
|
||
|
len = pcur->len;
|
||
|
if( size <= len ) { // found one
|
||
|
break;
|
||
|
}
|
||
|
if( len > largest ) { // update largest block size
|
||
|
largest = len;
|
||
|
}
|
||
|
pcur = pcur->next; // advance to next entry
|
||
|
if( pcur == // if back at start
|
||
|
(frlptr)&(heap->freehead)) {
|
||
|
heap->largest_blk = largest; // update largest
|
||
|
setup_segment( segment ); // 16bit Intel restore
|
||
|
return( (unsigned)result ); // return 0
|
||
|
}
|
||
|
}
|
||
|
heap->b4rover = largest; // update rover size
|
||
|
heap->numalloc++; // udpate allocation count
|
||
|
len -= size; // compute leftover size
|
||
|
if( len >= FRL_SIZE ) { // if leftover big enough
|
||
|
// split into two chunks
|
||
|
frlptr pprev; // before current
|
||
|
frlptr pnext; // after current
|
||
|
frlptr pnew; // start of new piece
|
||
|
pnew = (frlptr)((PTR)pcur + size);
|
||
|
heap->rover = pnew; // update rover
|
||
|
pnew->len = len; // set new size
|
||
|
pcur->len = size; // reset current size
|
||
|
pprev = pcur->prev; // update next/prev links
|
||
|
pnew->prev = pprev;
|
||
|
pnext = pcur->next;
|
||
|
pnew->next = pnext;
|
||
|
pprev->next = pnew;
|
||
|
pnext->prev = pnew;
|
||
|
} else { // just use this chunk
|
||
|
frlptr pprev; // before current
|
||
|
frlptr pnext; // after current
|
||
|
heap->numfree--; // 1 fewer entries in free list
|
||
|
pprev = pcur->prev;
|
||
|
heap->rover = pprev; // update rover
|
||
|
pnext = pcur->next; // update next/prev links
|
||
|
pprev->next = pnext;
|
||
|
pnext->prev = pprev;
|
||
|
}
|
||
|
pcur->len |= 1; // mark as allocated
|
||
|
// get pointer to user area
|
||
|
result = (frlptr)((PTR)pcur + TAG_SIZE);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
setup_segment( segment ); // 16bit Intel restore
|
||
|
return( (unsigned)result );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// input:
|
||
|
// pointer - address of block to free
|
||
|
// if 16bit Intel -> offset within segment
|
||
|
// else -> absolute pointer value
|
||
|
// segment - 16bit Intel data selector containing heap
|
||
|
// offset - address of heap control block
|
||
|
// if 16bit Intel -> offset within segment
|
||
|
// else -> absolute pointer value
|
||
|
//
|
||
|
// output:
|
||
|
// none
|
||
|
//
|
||
|
void __MemFree( unsigned pointer, unsigned segment, unsigned offset )
|
||
|
{
|
||
|
setup_segment( segment ); // setup DS for 16bit Intel
|
||
|
|
||
|
if( pointer != 0 ) { // quit if pointer is zero
|
||
|
frlptr pfree;
|
||
|
pfree = (frlptr)(pointer - TAG_SIZE);
|
||
|
if( pfree->len & 1 ) { // quit if storage is free
|
||
|
struct heapblkp _WCI86NEAR *heap;
|
||
|
frlptr pnext;
|
||
|
frlptr pprev;
|
||
|
frlptr ptr;
|
||
|
unsigned len;
|
||
|
heap = (struct heapblkp _WCI86NEAR *)offset;
|
||
|
do { // this allows break statement
|
||
|
unsigned average;
|
||
|
unsigned numfree;
|
||
|
|
||
|
// look at next block to try and coalesce
|
||
|
len = pfree->len & ~1; // get next block
|
||
|
pnext = (frlptr)((PTR)pfree + len);
|
||
|
if( (pnext->len & 1) == 0 ) { // if it is free
|
||
|
len += pnext->len; // include the length
|
||
|
pfree->len = len; // update pfree length
|
||
|
if( pnext == heap->rover ) { // check for rover
|
||
|
heap->rover = pfree; // update rover
|
||
|
}
|
||
|
pprev = pnext->prev; // fixup next/prev links
|
||
|
pnext = pnext->next;
|
||
|
pprev->next = pnext;
|
||
|
pnext->prev = pprev;
|
||
|
heap->numfree--; // reduce numfree
|
||
|
break; // proceed to coalesce code
|
||
|
}
|
||
|
|
||
|
// following block is not free
|
||
|
// we must now try to figure out where pfree
|
||
|
// is in relation to the entries in the free list
|
||
|
pfree->len = len; // remove allocated marker
|
||
|
|
||
|
// check a few special places
|
||
|
// see if pfree is:
|
||
|
// - just before or just after the rover
|
||
|
// - at the very beginning or very end of the heap
|
||
|
pnext = heap->rover; // get rover
|
||
|
if( pfree < pnext ) { // where is pfree?
|
||
|
// pfree is before rover
|
||
|
if( pfree > pnext->prev ) { // where is pfree?
|
||
|
// pfree is next to rover
|
||
|
break; // proceed to coalesce code
|
||
|
}
|
||
|
pnext = heap->freehead.next; // get start of free list
|
||
|
if( pfree < pnext ) { // where is pfree?
|
||
|
// pfree is at start of list
|
||
|
break; // proceed to coalesce code
|
||
|
}
|
||
|
} else { // pfree is after rover
|
||
|
pnext = pnext->next; // pnext is after rover
|
||
|
if( pfree < pnext ) { // where is pfree?
|
||
|
// pfree is just after rover
|
||
|
break; // proceed to coalesce code
|
||
|
}
|
||
|
// get end of free list
|
||
|
pnext = (frlptr)&(heap->freehead);
|
||
|
pprev = pnext->prev;
|
||
|
if( pfree > pprev ) { // where is pfree?
|
||
|
// pfree is at end of list
|
||
|
break; // proceed to coalesce code
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Calculate the average number of allocated blocks we may
|
||
|
// have to skip until we expect to find a free block. If
|
||
|
// this number is less than the total number of free blocks,
|
||
|
// chances are that we can find the correct position in the
|
||
|
// free list by scanning ahead for a free block and linking
|
||
|
// this free block before the found free block. We protect
|
||
|
// ourself against the degenerate case where there is an
|
||
|
// extremely long string of allocated blocks by limiting the
|
||
|
// number of blocks we will search to twice the calculated
|
||
|
// average.
|
||
|
|
||
|
numfree = heap->numfree;
|
||
|
average = heap->numalloc / (numfree+1);
|
||
|
if( average < numfree ) {
|
||
|
|
||
|
// There are lots of allocated blocks and lots of free
|
||
|
// blocks. On average we should find a free block
|
||
|
// quickly by following the allocated blocks, but the
|
||
|
// worst case can be very bad. So, we try scanning the
|
||
|
// allocated blocks and give up once we have looked at
|
||
|
// twice the average.
|
||
|
|
||
|
unsigned worst;
|
||
|
worst = heap->numalloc - numfree;
|
||
|
average *= 2; // give up after this many
|
||
|
if( worst <= numfree ) {
|
||
|
average = UINT_MAX; // we won't give up loop
|
||
|
}
|
||
|
// point at next allocated
|
||
|
pnext = (frlptr)((PTR)pfree + pfree->len);
|
||
|
for(;;) {
|
||
|
len = pnext->len;
|
||
|
if( len & 1 ) { // pnext is allocated
|
||
|
if( len != END_TAG ) { // check for end TAG
|
||
|
len &= ~1; // advance pnext
|
||
|
pnext = (frlptr)((PTR)pnext + len);
|
||
|
average--;
|
||
|
if( !average ) { // give up search
|
||
|
break;
|
||
|
}
|
||
|
} else {
|
||
|
break; // stop at end tag
|
||
|
}
|
||
|
} else {
|
||
|
// break twice!
|
||
|
goto found_it; // we have the spot
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// when all else fails, search the free list
|
||
|
pnext = heap->rover; // begin at rover
|
||
|
if( pfree < pnext ) { // is pfree before rover?
|
||
|
// then begin at start
|
||
|
pnext = heap->freehead.next;
|
||
|
}
|
||
|
for(;;) {
|
||
|
if( pfree < pnext ) { // if pfree before pnext
|
||
|
break; // we found it
|
||
|
}
|
||
|
pnext = pnext->next; // advance pnext
|
||
|
|
||
|
if( pfree < pnext ) { // if pfree before pnext
|
||
|
break; // we found it
|
||
|
}
|
||
|
pnext = pnext->next; // advance pnext
|
||
|
|
||
|
if( pfree < pnext ) { // if pfree before pnext
|
||
|
break; // we found it
|
||
|
}
|
||
|
pnext = pnext->next; // advance pnext
|
||
|
}
|
||
|
} while( 0 ); // only do once
|
||
|
|
||
|
found_it:
|
||
|
// if we are here, then we found the spot
|
||
|
pprev = pnext->prev; // setup pprev
|
||
|
|
||
|
// pprev, pfree, pnext are all setup
|
||
|
len = pfree->len;
|
||
|
|
||
|
// check pprev and pfree
|
||
|
ptr = (frlptr)((PTR)pprev + pprev->len);
|
||
|
if( ptr == pfree ) { // are they adjacent?
|
||
|
// coalesce pprev and pfree
|
||
|
len += pprev->len; // udpate len
|
||
|
pprev->len = len;
|
||
|
if( heap->rover == pfree ) { // check rover impact
|
||
|
heap->rover = pprev; // update rover
|
||
|
}
|
||
|
pfree = pprev; // now work with coalesced blk
|
||
|
} else {
|
||
|
heap->numfree++; // one more free entry
|
||
|
pfree->next = pnext; // update next/prev entries
|
||
|
pfree->prev = pprev;
|
||
|
pprev->next = pfree;
|
||
|
pnext->prev = pfree;
|
||
|
}
|
||
|
heap->numalloc--; // one fewer allocated
|
||
|
|
||
|
if( pfree < heap->rover ) { // check rover impact
|
||
|
if( len > heap->b4rover ) { // is len bigger than b4rover
|
||
|
heap->b4rover = len; // then update b4rover
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( len > heap->largest_blk ) { // check largest block
|
||
|
heap->largest_blk = len;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
setup_segment( segment ); // 16bit Intel restore
|
||
|
}
|