source: sbliveos2/trunk/drv16/malloc.c@ 188

Last change on this file since 188 was 148, checked in by sandervl, 25 years ago

beta 0.25 update

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