source: cmedia/trunk/Drv16/malloc.c

Last change on this file was 354, checked in by stevenhl, 17 years ago

Import untested baseline cmedia sources, work products and binaries
Binaries and work products should be deleted from repository.
once new builds are verified to work.

File size: 18.2 KB
RevLine 
[354]1/* $Id: malloc.c,v 1.2 2000/04/26 18:00:58 sandervl Exp $ */
2
3/* SCCSID = %W% %E% */
4/****************************************************************************
5 * *
6 * Copyright (c) IBM Corporation 1994 - 1997. *
7 * *
8 * The following IBM OS/2 source code is provided to you solely for the *
9 * the purpose of assisting you in your development of OS/2 device drivers. *
10 * You may use this code in accordance with the IBM License Agreement *
11 * provided in the IBM Device Driver Source Kit for OS/2. *
12 * *
13 ****************************************************************************/
14/**@internal %W%
15 * Implementation of the driver's built in memory management.
16 * @version %I%
17 * @context
18 * Unless otherwise noted, all interfaces are Ring-3 and Ring-0, 16-bit,
19 * kernel stack.
20 * @notes
21 * @history
22 * 01-Jul-95 Timur Tabi Creation
23 */
24
25#include <os2.h>
26#include <string.h> // memcpy(), memset()
27#include <include.h>
28
29#include "end.h"
30#include "sizedefs.h" // HEAP_SIZE, DEFAULT_HEAP.
31 // MustBe divisible by 4.
32
33#define SIGNATURE 0xBEEFDEAD // "DeadBeef" when view in hex in word order.
34
35typedef struct _MEMBLOCK {
36 unsigned long ulSignature;
37 unsigned uSize;
38 struct _MEMBLOCK *pmbNext;
39 char achBlock[1];
40} MEMBLOCK, __near *PMEMBLOCK;
41
42#define HDR_SIZE ( sizeof(MEMBLOCK) - 1 )
43
44// We won't leave a hole in memory any smaller than MIN_FragSize.
45#define MIN_FragSize 4
46
47
48
49//--- Global Variables used to manage the heap:
50//
51PMEMBLOCK pmbUsed=0; // Points to head of list of allocated memory blocks.
52 // Newly allocated blocks are inserted into head of list.
53PMEMBLOCK pmbFree; // Points to head of list of available memory blocks.
54unsigned uMemFree; // N bytes available for allocation.
55
56// The heap. NOTE: This must be the last data definition in the HEAP segment,
57// although not done currently, we are planning to size the heap down at INIT
58// time. If this is done, any vbls in the HEAP segment that
59// appear after the HEAP would become unaddressable.
60
61#pragma data_seg ("_HEAP","ENDDS");
62 char __near acHeap[HEAP_SIZE];
63#pragma data_seg ("_DATA","DATA");
64
65USHORT fMemoryDoc = 0;
66USHORT nAlloc = 0; // Current number of allocated blocks.
67USHORT cAlloc = 0; // Total memory in allocated blocks incl. headers.
68USHORT nAllocCalls = 0; // Cumulative total, number of malloc() calls.
69USHORT nFree = 0; // Current number of free blocks.
70USHORT cFree = 0; // Total memory in free blocks incl. headers.
71USHORT nFreeCalls = 0; // Cumulative total, number of free() calls.
72USHORT nCompact = 0; // Num of times we've joined two adjacent free
73 // blocks into a single larger block.
74USHORT cTotalHeap = HEAP_SIZE;
75
76
77#ifdef DEBUG
78 #define SignatureCheck(a, b) _SignatureCheck_(a, b)
79 #define HeapCheck(a) _HeapCheck_(a)
80
81 void dumpheap(void)
82 {
83 int i;
84 PMEMBLOCK pmb;
85 unsigned u=0;
86
87 pmb=pmbUsed;
88 // ddprintf("HEAP: Heap Dump - Used blocks\n");
89 for (i=0; i<10; i++) {
90 // ddprintf(" pmb=%p, length=%ui\n",(void __far *) pmb, pmb->uSize);
91 u+=pmb->uSize;
92 pmb=pmb->pmbNext;
93 if (!pmb) break;
94 }
95 // ddprintf(" Total used = %ui\n",u);
96
97 u=0;
98 pmb=pmbFree;
99 // ddprintf("HEAP: Heap Dump - Free blocks\n");
100 for (i=0; i<50; i++) {
101 // ddprintf(" pmb=%p, length=%ui\n",(void __far *) pmb, pmb->uSize);
102 u+=pmb->uSize;
103 pmb=pmb->pmbNext;
104 if (!pmb) break;
105 }
106 // ddprintf(" Total free = %ui\n",u);
107 }
108
109#else
110 #define SignatureCheck(a, b) _SignatureCheck_(a, NULL)
111 #define HeapCheck(a) _HeapCheck_(NULL)
112
113#endif
114
115
116void _SignatureCheck_ ( PMEMBLOCK p, PSZ idText )
117{
118 bool bGoodPointer;
119
120 // Set bGoodPointer to indicate whether or not 'p' points within heap.
121 bGoodPointer = (((USHORT) p) >= ((USHORT) acHeap))
122 && (((USHORT) p) <= (((USHORT) acHeap) + HEAP_SIZE)) ;
123 //### heap might have less than HEAP_SIZE bytes
124
125 // Check for correct signature.
126 if (bGoodPointer)
127 bGoodPointer = (p->ulSignature == SIGNATURE);
128
129 if (! bGoodPointer)
130 {
131// ddprintf( "Heap pointer out of range, or signature exception: %p %s\n", p, idText );
132 int3();
133 }
134}
135
136void _HeapCheck_ ( PSZ idText )
137{
138 PMEMBLOCK p;
139 for ( nAlloc = 0, cAlloc = 0, p = pmbUsed; p; p = p->pmbNext ) {
140 ++nAlloc;
141 cAlloc += p->uSize + HDR_SIZE;
142 SignatureCheck( p,(PSZ) "HeapCheck() Used list" );
143 }
144 for ( nFree = 0, cFree = 0, p = pmbFree; p; p = p->pmbNext ) {
145 ++nFree;
146 cFree += p->uSize + HDR_SIZE;
147 SignatureCheck( p,(PSZ) "HeapCheck() Free list" );
148 }
149 if (fMemoryDoc & 1) {
150 if (cAlloc + cFree != cTotalHeap) {
151// ddprintf( "Heap Alloc + Free != Total\n" );
152// dumpheap();
153 int3();
154 }
155 }
156}
157
158
159
160
161// Threshold for creating a new free block.
162#define MIN_Leftover (HDR_SIZE + MIN_FragSize)
163
164/* make_new_free()
165 Formats the back part of an existing free MEMBLOCK as a new, smaller
166 "Free" MEMBLOCK. Doesn't update Global vbls (caller responsibility).
167 IN: pmbOldFree - pointer to existing memory block.
168 uSize - offset, which won't be included in new allocation.
169 it's assumed HDR is not included in uSize.
170 OUT: pointer to new free MEMBLOCK, is a new block of free memory,
171 is created at pmbOldFree + uRequest. The memory block header
172 in both the new block and the old block are properly initialized
173 on return, but we don't update the Free list or Allocated list.
174 OUT: NULL if the new free memory block is smaller than the
175 framentation threshold.
176
177The fragmentation consideration comes into play when we find a block that's
178big enough to hold the requested memory, but not big enough for the leftover
179part to be useful as another free block.
180
181 _______________________free block_______________________________________
182 | free | |
183 | block | space available |
184 | header | (pmbFree->uSize bytes long) |
185 |(HDR_SIZE | |
186 |__bytes)__|_____________________________________________________________|
187
188 <-- HDR --> <------------------------- l -------------------------------->
189
190Must either be allocated in total, or must become:
191
192 _______________________used block_______________________________________
193 | used | | free | |
194 | block | space allocated | block | space |
195 | header | == uSize (following DWORD align't) | header | available |
196 |(HDR_SIZE | (pmbUsed->uSize bytes long) |(HDR_SIZE | |
197 |__bytes)__|_____________________________________|__bytes)__|____________|
198
199 <-- HDR --> <-------------- n ------------------><-- HDR --> <--- m ----->
200
201To be split into an allocated and a new, smaller free block, 'm' must be at
202least MIN_FragSize bytes long.
203
204Everything should remain 4 byte aligned since the HDR_SIZE, MIN_FragSize,
205and the space allocated (after we munge it) are all multiples of 4.
206
207This means that in order for a free block to be chosen, one of the following
208equation must hold
209
210 Case 1: n <= l < (n + HDR + MIN_FragSize)
211 Here, we'll allocate the entire block. This makes the block somewhat
212 larger than the request, but prevents some degree of fragmentation.
213
214 Case 2: (n + HDR + MIN_FragSize) <= l
215 We split the block into an allocated part to satisfy the allocation request,
216 and a free block which can be allocated in a subsequent request.
217*/
218
219PMEMBLOCK make_new_free(PMEMBLOCK pmbOldFree, unsigned uRequest)
220{
221 PMEMBLOCK pmbNewFree; // If we create a new free block, it goes here.
222
223 // Which of the two cases (as described in function header) have we got?
224 // We know we're big enough to satisfy request, is there enough leftover
225 // for a new free block?
226
227 if ((uRequest + MIN_Leftover) > pmbOldFree->uSize) {
228 // Not enough leftover, allocate the entire free block. Don't
229 // change pmbOldFree->uSize.
230 pmbNewFree = 0;
231 }
232 else {
233 // We've got enough leftover to make a new free block.
234 pmbNewFree = (PMEMBLOCK) ((char __near *) pmbOldFree + HDR_SIZE + uRequest );
235 pmbNewFree->ulSignature = SIGNATURE;
236 pmbNewFree->uSize = pmbOldFree->uSize - (uRequest + HDR_SIZE);
237 pmbNewFree->pmbNext = pmbOldFree->pmbNext;
238
239 // Update the size of the free block that was passed in.
240 pmbOldFree->uSize -= (pmbNewFree->uSize + HDR_SIZE);
241 }
242
243 return pmbNewFree;
244}
245
246
247/**@internal _msize
248 */
249unsigned _msize(void __near *pvBlock)
250{
251 PMEMBLOCK pmb;
252
253 if (!pvBlock)
254 return 0;
255
256 pmb = (PMEMBLOCK) ((char __near *) pvBlock - HDR_SIZE);
257
258 return pmb->uSize;
259}
260
261
262/**@internal pmbAllocateBlock
263 * Update all data structures for allocation of one memory block. It's
264 * assumed, on entry, that the block is large enough for the requested
265 * allocation.
266 * @param PMEMBLOCK pmb - the current memory block on the Free list to
267 * allocate.
268 * @param USHORT uRequest - size of the memory request to fill, not counting
269 * memory block header.
270 * @param PMEMBLOCK pmbPrev - the memory block which precedes the block being
271 * allocated in the Free list. NULL if no predecessor (ie, pmb is pointed to
272 * by ::pmbFree).
273 * @return PMEMBLOCK - pointer to the data part of the allocated memory block,
274 * suitable for return to malloc() caller.
275 */
276void __near *
277npvAllocateBlock( PMEMBLOCK pmb, USHORT uRequest, PMEMBLOCK pmbPrev )
278{
279 //pmb points to the selected block.
280 //pmbPrev points to the block prior to the selected block, NULL if pmbFree.
281 PMEMBLOCK pmbOldNext; // Original free block that follows the block being allocated.
282 PMEMBLOCK pmbNewFree; // Leftover free memory from the allocated block.
283
284 // Split this block into an allocated + free part if it's big enough.
285 pmbNewFree = make_new_free( pmb, uRequest );
286
287 // Update global memory counter.
288 uMemFree -= (pmb->uSize + HDR_SIZE);
289
290 // Add this block into the front of the Allocated list.
291 pmbOldNext = pmb->pmbNext;
292 pmb->pmbNext = pmbUsed;
293 pmbUsed = pmb;
294
295 // Remove the new allocation from the Free list.
296 if (pmbNewFree) { // If we split off a new free block
297 pmbNewFree->pmbNext = pmbOldNext;
298 if (pmbPrev) // If we're not at head of Free list
299 pmbPrev->pmbNext = pmbNewFree;
300 else
301 pmbFree = pmbNewFree;
302 }
303 else { // We allocated the entire free block.
304 if (pmbPrev) // If we're not at head of Free list
305 pmbPrev->pmbNext = pmbOldNext;
306 else
307 pmbFree = pmbOldNext;
308 }
309
310 return (void __near *) pmb->achBlock;
311}
312
313
314/* malloc()
315This function searches the list of free blocks until it finds one big enough
316to hold the amount of memory requested, which is passed in uSize. The uSize
317request is sized up for:
318 - a memory block header
319 - four byte alignment
320 - minimum fragmentation
321
322See helper function make_new_free() for discussion of fragmentation handling.
323*/
324
325void __near *malloc( unsigned uSize )
326{
327 USHORT uRequest; // Request size after alignment.
328 PMEMBLOCK pmb, pmbPrev; // Use to walk free lists.
329 void __near *npvReturn = 0; // Return value.
330
331 ++nAllocCalls; // Diagnostics.
332 HeapCheck((PSZ) "malloc() entry" );
333
334 if (! uSize) return 0;
335
336 uRequest = (uSize + 3) & -4; // Force DWORD alignment.
337
338 if (pmbFree->uSize >= uRequest)
339 npvReturn = npvAllocateBlock( pmbFree, uRequest, NULL );
340 else {
341 pmbPrev = pmbFree;
342 for ( pmb=pmbFree->pmbNext; pmb; pmbPrev=pmb, pmb=pmb->pmbNext)
343 if (pmb->uSize >= uRequest) {
344 npvReturn = npvAllocateBlock( pmb, uRequest, pmbPrev );
345 break;
346 }
347 }
348
349 if (npvReturn) {
350 SignatureCheck( (PMEMBLOCK) (((PUCHAR) npvReturn) - HDR_SIZE),
351 (PSZ) "malloc() exit, allocated block" );
352 }
353 else {
354 // Out of Memory !!!
355 int3();
356 }
357
358 HeapCheck((PSZ) "malloc() exit" );
359 return npvReturn;
360}
361
362/* void compact(void)
363This function compacts the free blocks together. This function is a
364companion to free(), and thus the algorithm is tailored to how free()
365works. Change the algorithm in free(), and you'll have to change this
366function too.
367
368When free() frees a block, it sets the head of the free list, pmbFree, to
369point to it. Then the newly freed block points to whatever the old pmbFree
370pointed to. In other words, each new free block is added to the head of
371the free list.
372
373If compact() is always called after a block is freed, then it can be
374guaranteed that the free list is always compacted (i.e. you won't find
375two adjacent free blocks anywhere in the heap) _before_ each call to free().
376Therefore, the newly freed block can be located in only one of the
377following positions:
3781. Not adjacent to any other free blocks (compacting not necessary)
3792. Physically before another free block
3803. Physically after another free block
3814. Between two free blocks (i.e. the block occupies the entire space
382 between two free blocks)
383
384Since the newly freed block is the first in the free list, compact()
385starts with the second block in the list (i.e. pmbFree->pmbNext).
386Each free block is then compared with the newly freed block for
387adjacency. If a given block is located before the new block, then it
388can't possibly be also located after, and vice versa. Hence, the
389"else if" statement in the middle of the loop.
390
391Also, the newly freed block can only be adjacent to at most two other
392blocks. Therefore, the operation of combining two adjacent free blocks can
393only happen at most twice. The variable nFreed counts the number of times
394two blocks are combined. The function exits if nFreed reaches two. nFreed
395is initially 0.
396
397Helper macro after() takes a PMEMBLOCK (call it pmb) as a parameter,
398and calculates where an adjacent free block would exist if it were
399physically located after pmb.
400
401Helper function remove() removes an element from the free list.
402*/
403
404#define after(pmb) ((PMEMBLOCK) ((char __near *) pmb + pmb->uSize + HDR_SIZE))
405
406void remove(PMEMBLOCK pmb)
407{
408 PMEMBLOCK pmbPrev;
409
410 if (pmb == pmbFree) {
411 pmbFree = pmbFree->pmbNext;
412 return;
413 }
414
415 for (pmbPrev=pmbFree; pmbPrev; pmbPrev=pmbPrev->pmbNext)
416 if (pmbPrev->pmbNext == pmb) {
417 pmbPrev->pmbNext = pmb->pmbNext;
418 return;
419 }
420}
421
422void compact(void)
423{
424 PMEMBLOCK pmb;
425 int iFreed = 0;
426
427 for (pmb=pmbFree->pmbNext; pmb; pmb=pmb->pmbNext) {
428 if (pmb == pmb->pmbNext) {
429// ddprintf("HEAP: heap loop, %p points to itself\n", (void __far *) pmb);
430 int3();
431 }
432
433 if (after(pmb) == pmbFree) {
434// ddprintf("HEAP: compact found pointer %p (size=%ui) before pmbFree %p\n", (void __far *) pmb, pmb->uSize, (void __far *) pmbFree);
435 pmb->uSize += HDR_SIZE + pmbFree->uSize;
436 remove(pmbFree);
437 if (++iFreed == 2) goto exit;
438 } else if (after(pmbFree) == pmb) {
439// ddprintf("HEAP: compact found pointer %p (size=%ui) after pmbFree %p\n", (void __far *) pmb, pmb->uSize, (void __far *) pmbFree);
440 pmbFree->uSize += HDR_SIZE + pmb->uSize;
441 remove(pmb);
442 if (++iFreed == 2) goto exit;
443 }
444 }
445
446exit:
447 nCompact += iFreed;
448}
449
450void free(void __near *pvBlock)
451{
452 PMEMBLOCK pmb,pmbPrev,pmbBlock;
453 int fSentinel;
454
455 ++nFreeCalls;
456 if (!pvBlock) return; // support freeing of NULL
457
458 pmbBlock=(PMEMBLOCK) ((char __near *) pvBlock - HDR_SIZE);
459
460 SignatureCheck( pmbBlock,(PSZ) "free() entry, Block to be freed" );
461 HeapCheck((PSZ) "free() entry" );
462
463 uMemFree += pmbBlock->uSize + HDR_SIZE;
464
465 if (pmbBlock == pmbUsed) { // are we freeing the first block?
466 pmbUsed = pmbUsed->pmbNext; // the 2nd block is now the 1st
467 pmbBlock->pmbNext = pmbFree; // this block is now free, so it points to 1st free block
468 pmbFree = pmbBlock; // this is now the 1st free block
469 compact();
470 goto exit;
471 }
472
473 pmbPrev=pmbUsed;
474 fSentinel = FALSE;
475 for (pmb=pmbUsed->pmbNext; pmb; pmbPrev=pmb, pmb=pmb->pmbNext)
476 if (pmb == pmbBlock) {
477 if (fSentinel) {
478// ddprintf("HEAP: free sentinel triggered, pmb=%p\n", (void __far *) pmb);
479 int3();
480 }
481 pmbPrev->pmbNext = pmb->pmbNext; // delete this block from the chain
482 pmbBlock->pmbNext = pmbFree;
483 pmbFree = pmbBlock;
484 compact();
485 fSentinel = TRUE;
486 }
487
488exit: //--- Check things are still intact.
489 HeapCheck((PSZ) "free() exit" );
490}
491
492unsigned _memfree(void)
493{
494 return uMemFree;
495}
496
497void __near *realloc(void __near *pvBlock, unsigned usLength)
498{
499 void __near *pv;
500
501 if (!pvBlock) // if ptr is null, alloc block
502 return malloc(usLength);
503
504 if (!usLength) { // if length is 0, free block
505 free(pvBlock);
506 return 0;
507 }
508
509 pv=malloc(usLength); // attempt to allocate the new block
510 if (!pv) // can't do it?
511 return 0; // just fail. Version 2 will try harder
512
513 memcpy(pv, pvBlock, min( _msize(pvBlock), usLength));
514 free(pvBlock);
515 return pv;
516}
517
518#pragma code_seg ("_inittext");
519
520unsigned HeapInit(unsigned uSize)
521{
522 unsigned max_heap=(unsigned) &end_of_heap - (unsigned) &end_of_data;
523
524 if (!uSize)
525 uSize = DEFAULT_HEAP;
526
527 if (uSize > max_heap)
528 uSize = max_heap;
529
530 pmbFree=(PMEMBLOCK) acHeap;
531 pmbFree->uSize = uSize - HDR_SIZE;
532 pmbFree->ulSignature = SIGNATURE;
533 pmbFree->pmbNext = 0;
534 uMemFree = pmbFree->uSize;
535 return pmbFree->uSize;
536}
Note: See TracBrowser for help on using the repository browser.