/**************************************************************************** * * 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 #include #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 }