/*** *malloc.c - Get a block of memory from the heap * * Copyright (c) 1989-1993, Microsoft Corporation. All rights reserved. * *Purpose: * Defines the malloc() function. Also defines the internal utility * functions _flat_malloc(), _heap_split_block(), _heap_advance_rover() * and, for multi-thread, _malloc_lk(). * *Revision History: * 06-29-89 GJF Module created (no rest for the wicked). * 07-07-89 GJF Several bug fixes * 07-21-89 GJF Added code to maintain proverdesc such that proverdesc * either points to the descriptor for the first free * block in the heap or, if there are no free blocks, is * the same as plastdesc. * 11-07-89 GJF Substantially revised to cope with 'tiling'. * 11-09-89 GJF Embarrassing bug (didn't bother to assign pdesc) * 11-10-89 GJF Added MTHREAD support. * 11-17-89 GJF Oops, must call _free_lk() instead of free()! * 12-18-89 GJF Changed name of header file to heap.h, also added * explicit _cdecl to function definitions. * 12-19-89 GJF Got rid of plastdesc from _heap_split_block() and * _heap_advance_rover(). * 03-11-90 GJF Replaced _cdecl with _CALLTYPE1, added #include * and removed #include . * 07-25-90 SBM Replaced by , replaced * by * 09-28-90 GJF New-style function declarators. * 02-26-91 SRW Optimize heap rover for _WIN32_. * 03-07-91 FAR Fix bug in heap rover * 03-11-91 FAR REALLY Fix bug in heap rover * 03-14-91 GJF Changed strategy for rover - old version available * by #define-ing _OLDROVER_. * 04-05-91 GJF Temporary hack for Win32/DOS folks - special version * of malloc that just calls HeapAlloc. Change conditioned * on _WIN32DOS_. * 05-28-91 GJF Removed M_I386 conditionals and put in _CRUISER_ * conditionals so the 'tiling' version is built only for * Cruiser. * 03-03-93 SKS Add new handler support (_pnhHeap and related code) * *******************************************************************************/ #include #include #include #include #include #include #ifndef _POSIX_ #include _PNH _pnhHeap = NULL; /* pointer to new() handler */ #endif /*** *void *malloc(size_t size) - Get a block of memory from the heap * *Purpose: * Allocate of block of memory of at least size bytes from the heap and * return a pointer to it. * * Special Notes For Cruiser Implementaton: For OS/2 2.0, malloc() is * required to ensure that allocations of not more than 64 Kb do not * cross 64 Kb boundaries in the address space. For this implementation, * the straightforward, flat-model malloc is renamed to _flat_malloc() * and a malloc() that worries about 'tiling' is built on top of this. * * Special Notes For Multi-thread: The non-multi-thread version is * renamed to _malloc_lk(). The multi-thread malloc() simply locks the * heap calls _malloc_lk, released the heap lock and returns. * *Entry: * size_t size - size of block requested * *Exit: * Success: Pointer to memory block * Failure: NULL (or some error value) * *Uses: * *Exceptions: * *******************************************************************************/ #ifdef MTHREAD void * _CALLTYPE1 malloc ( size_t size ) { void *pret; #ifndef _POSIX_ for (;;) { #endif /* lock the heap */ _mlock(_HEAP_LOCK); /* do the allocation */ pret = _malloc_lk(size); /* unlock the heap */ _munlock(_HEAP_LOCK); #ifndef _POSIX_ if (pret || _pnhHeap == NULL || (*_pnhHeap)(size) == 0) #endif return(pret); #ifndef _POSIX_ } #endif /* ndef _POSIX_ */ } /*** *void *_malloc_lk(size_t size) - non-locking form of malloc * *Purpose: * Same as malloc() except that no locking or unlocking is performed. * *Entry: * See malloc * *Exit: * See malloc * *Uses: * *Exceptions: * *******************************************************************************/ void * _CALLTYPE1 _malloc_lk ( size_t size ) #else /* ndef MTHREAD */ void * _CALLTYPE1 malloc ( size_t size ) #endif /* MTHREAD */ #ifdef _CRUISER_ { char *pblck; _PBLKDESC pdesc; size_t dist; /* round size up to the nearest whole number of dwords */ size = _ROUND2(size, 4); /* first try allocating a block of size bytes */ if ( ((pblck = _flat_malloc(size)) == NULL) || (size > _SEGSIZE_) || (_DISTTOBNDRY(pblck) >= size) ) /* all done! */ goto done; else /* doesn't meet requirements, free the allocation back to the * heap */ _free_lk(pblck); /* now, try allocating a block of 2*size bytes. if successful, the * allocated block is guaranteed to contain a region of size bytes * which does not cross a 64 K boundary. */ if ( (pblck = _flat_malloc(2*size)) == NULL ) /* allocation failed, return NULL to the caller */ goto done; /* set pdesc to point to the descriptor for the allocation block */ pdesc = _BACKPTR(pblck); /* find a subregion of at least size bytes which does not cross a * 64 Kb boundary and build a heap block around it. set pblck to * point to to allocation area of the block and pdesc to point to * its descriptor */ if ( (dist = _DISTTOBNDRY(pblck)) < size ) { /* the subregion from pblck to (pblck + size) crosses a 64 Kb * boundary, but the subregion from (pblck + dist) to (pblck + * dist + size) cannot (it starts on one). therefore, split * the block into two heap blocks, with the later block * starting at (pblck + dist - _HDRSIZE), and free the first * block. */ _heap_split_block(pdesc, dist - _HDRSIZE); _free_lk(pblck); pdesc = pdesc->pnextdesc; _SET_INUSE(pdesc); pblck += dist; } /* pblck and pdesc are now bound to allocation block whose first size * bytes do not cross any 64 Kb boundary. only detail is the block * may be too large so... */ #ifdef _OLDROVER_ _heap_split_block(pdesc, size); _SET_FREE(pdesc->pnextdesc); _heap_advance_rover(); #else /* ndef _OLDROVER_ */ if ( _BLKSIZE(pdesc) > size ) { _heap_split_block(pdesc, size); _SET_FREE(pdesc->pnextdesc); _heap_desc.proverdesc = pdesc->pnextdesc; } #endif /* _OLDROVER_ */ done: return(pblck); } /*** *void *_flat_malloc(size_t size) - Get a block of memory from the heap * *Purpose: * Allocate of block of memory of at least size bytes from the heap, * without regard to whether or not it crosses a 64 Kb boundary, and * return a pointer to it. * *Entry: * size_t size - size of block requested * *Exit: * Success: Pointer to memory block * Failure: NULL (or some error value) * *Uses: * *Exceptions: * *******************************************************************************/ void * _CALLTYPE1 _flat_malloc ( size_t size ) #endif /* _CRUISER_ */ { _PBLKDESC pdesc; /* validate size */ /***** COMMENTED OUT UNTIL _HEAP_MAXREQ IS DEFINED if ( size > _HEAP_MAXREQ ) return(NULL); *****/ /* round requested size */ size = _ROUND2(size, 4); #if !defined(_POSIX_) && !defined(MTHREAD) for (;;) { #endif /* !_POSIX && !MTHREAD */ /* try to find a big enough free block */ if ( (pdesc = _heap_search(size)) == NULL ) if ( _heap_grow(size) != -1 ) { /* try finding a big enough free block again. the * success of the call to _heap_grow should guarantee * it, but... */ if ( (pdesc = _heap_search(size)) == NULL ) /* something unexpected, and very bad, has * happened. abort! */ _heap_abort(); } #if !defined(_POSIX_) && !defined(MTHREAD) else if (!_pnhHeap || (*_pnhHeap)(size) == 0) return(NULL); else continue; else break; /* success! */ } #else /* _POSIX || MTHREAD */ else return(NULL); #endif /* !_POSIX && !MTHREAD */ /* carve the block into two pieces (if necessary). the first piece * shall be of the exact requested size, marked inuse and returned to * the caller. the leftover piece is to be marked free. */ if ( _BLKSIZE(pdesc) != size ) { /* split up the block and free the leftover piece back to * the heap */ _heap_split_block(pdesc, size); _SET_FREE(pdesc->pnextdesc); } /* mark pdesc inuse */ _SET_INUSE(pdesc); /* check proverdesc and reset, if necessary */ #ifdef _OLDROVER_ _heap_advance_rover(); #else /* ndef _OLDROVER_ */ _heap_desc.proverdesc = pdesc->pnextdesc; #endif /* _OLDROVER_ */ return( (void *)((char *)_ADDRESS(pdesc) + _HDRSIZE) ); } /*** *void _heap_split_block(pdesc, newsize) - split a heap allocation block into * two allocation blocks * *Purpose: * Split the allocation block described by pdesc into two blocks, the * first one being of newsize bytes. * * Notes: It is caller's responsibilty to set the status (i.e., free * or inuse) of the two new blocks, and to check and reset proverdesc * if necessary. See Exceptions (below) for additional requirements. * *Entry: * _PBLKDESC pdesc - pointer to the allocation block descriptor * size_t newsize - size for the first of the two sub-blocks (i.e., * (i.e., newsize == _BLKSIZE(pdesc), on exit) * *Exit: * There is no return value. * *Exceptions: * It is assumed pdesc points to a valid allocation block descriptor and * newsize is a valid heap block size as is (i.e., WITHOUT rounding). If * either of these of assumption is violated, _heap_split_block() will * likely corrupt the heap. Note also that _heap_split_block will simply * return to the caller if newsize >= _BLKSIZE(pdesc), on entry. * *******************************************************************************/ void _CALLTYPE1 _heap_split_block ( REG1 _PBLKDESC pdesc, size_t newsize ) { REG2 _PBLKDESC pdesc2; assert(("_heap_split_block: bad pdesc arg", _CHECK_PDESC(pdesc))); assert(("_heap_split_block: bad newsize arg", _ROUND2(newsize,4) == newsize)); /* carve the block into two pieces (if possible). the first piece * is to be exactly newsize bytes. */ if ( _BLKSIZE(pdesc) > newsize ) { /* get an empty decriptor */ _GETEMPTY(pdesc2) /* set it up to manage the second piece and link it in to * the list */ pdesc2->pblock = (void *)((char *)_ADDRESS(pdesc) + newsize + _HDRSIZE); *(void **)(pdesc2->pblock) = pdesc2; pdesc2->pnextdesc = pdesc->pnextdesc; pdesc->pnextdesc = pdesc2; } } #ifdef _OLDROVER_ /*** *void _heap_advance_rover(void) - check proverdesc and advance it, if necessary * *Purpose: * Check proverdesc. If it is not pointing to the descriptor of a free * block and is not equal to &sentinel, then walk up the list of heap * block descriptors until the descriptor of a free block is reached * and reset proverdesc to point to this descriptor, or until the end * of the heap is reached and reset proverdesc to &sentinel. * *Entry: * No arguments. * *Exit: * No return value. * *Exceptions: * *******************************************************************************/ void _CALLTYPE1 _heap_advance_rover(void) { REG1 _PBLKDESC pdesc; /* check proverdesc and advance it, if necessary */ #ifdef _CRUISER_ /* CRUISER TARGET */ if ( !_IS_FREE(_heap_desc.proverdesc) && (_heap_desc.proverdesc != &_heap_desc.sentinel) ) { #else /* ndef _CRUISER_ */ #ifdef _WIN32_ if ( _heap_desc.proverdesc != &_heap_desc.sentinel && (!_IS_FREE(_heap_desc.proverdesc) || _BLKSIZE(_heap_desc.proverdesc) <= 8)) { #else /* ndef _WIN32_ */ #error ERROR - ONLY CRUISER OR WIN32 TARGET SUPPORTED! #endif /* _WIN32_ */ #endif /* _CRUISER_ */ /* set pdesc to point to the descriptor for the next free * block, if any, or &sentinel, otherwise. */ for ( pdesc = (_heap_desc.proverdesc)->pnextdesc ; #ifdef _CRUISER_ /* CRUISER TARGET */ !(_IS_FREE(pdesc)) && (pdesc != &_heap_desc.sentinel) ; #else /* ndef _CRUISER_ */ #ifdef _WIN32_ pdesc != &_heap_desc.sentinel && (!(_IS_FREE(pdesc)) || _BLKSIZE(pdesc) <= 8) ; #else /* ndef _WIN32_ */ #error ERROR - ONLY CRUISER OR WIN32 TARGET SUPPORTED! #endif /* _WIN32_ */ #endif /* _CRUISER_ */ pdesc = pdesc->pnextdesc ) ; /* update proverdesc with pdesc */ _heap_desc.proverdesc = pdesc; } } #endif /* _OLDROVER_ */