source: GPL/branches/uniaud32-next/lib32/malloc.c

Last change on this file was 667, checked in by Paul Smedley, 5 years ago

Remove remaining support for non-KEE builds

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