OSDN Git Service

2010-01-06 Tristan Gingold <gingold@adacore.com>
[pf3gnuchains/pf3gnuchains3x.git] / tcl / mac / tclMacAlloc.c
1 /*
2  * tclMacAlloc.c --
3  *
4  *      This is a very fast storage allocator.  It allocates blocks of a
5  *      small number of different sizes, and keeps free lists of each size.
6  *      Blocks that don't exactly fit are passed up to the next larger size.
7  *      Blocks over a certain size are directly allocated by calling NewPtr.
8  *
9  * Copyright (c) 1983 Regents of the University of California.
10  * Copyright (c) 1996-1997 Sun Microsystems, Inc.
11  *
12  * Portions contributed by Chris Kingsley, Jack Jansen and Ray Johnson
13  *.
14  * See the file "license.terms" for information on usage and redistribution
15  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
16  *
17  * RCS: @(#) $Id$
18  */
19
20 #include "tclInt.h"
21 #include "tclMacInt.h"
22 #include <Memory.h>
23 #include <Gestalt.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27
28 /*
29  * Flags that are used by ConfigureMemory to define how the allocator
30  * should work.  They can be or'd together.
31  */
32 #define MEMORY_ALL_SYS 1        /* All memory should come from the system
33 heap. */
34 #define MEMORY_DONT_USE_TEMPMEM 2       /* Don't use temporary memory but system memory. */
35
36 /*
37  * Amount of space to leave in the application heap for the Toolbox to work.
38  */
39
40 #define TOOLBOX_SPACE (512 * 1024)
41
42 static int memoryFlags = 0;
43 static Handle toolGuardHandle = NULL;
44                                 /* This handle must be around so that we don't
45                                  * have NewGWorld failures. This handle is
46                                  * purgeable. Before we allocate any blocks,
47                                  * we see if this handle is still around.
48                                  * If it is not, then we try to get it again.
49                                  * If we can get it, we lock it and try
50                                  * to do the normal allocation, unlocking on
51                                  * the way out. If we can't, we go to the
52                                  * system heap directly. */
53
54 static int tclUseMemTracking = 0; /* Are we tracking memory allocations?
55                                                                    * On recent versions of the MacOS this
56                                                                    * is no longer necessary, as we can use
57                                                                    * temporary memory which is freed by the
58                                                                    * OS after a quit or crash. */
59                                                                    
60 static size_t tclExtraHdlSize = 0; /* Size of extra memory allocated at the start
61                                                                         * of each block when using memory tracking
62                                                                         * ( == 0 otherwise) */
63
64 /*
65  * The following typedef and variable are used to keep track of memory
66  * blocks that are allocated directly from the System Heap.  These chunks
67  * of memory must always be freed - even if we crash.
68  */
69
70 typedef struct listEl {
71     Handle              memoryHandle;
72     struct listEl *     next;
73     struct listEl *     prec;
74 } ListEl;
75
76 static ListEl * systemMemory = NULL;
77 static ListEl * appMemory = NULL;
78
79 /*
80  * Prototypes for functions used only in this file.
81  */
82
83 static pascal void      CleanUpExitProc _ANSI_ARGS_((void));
84 void                    ConfigureMemory _ANSI_ARGS_((int flags));
85 void                    FreeAllMemory _ANSI_ARGS_((void));
86 \f
87 /*
88  *----------------------------------------------------------------------
89  *
90  * TclpSysRealloc --
91  *
92  *      This function reallocates a chunk of system memory.  If the
93  *      chunk is already big enough to hold the new block, then no
94  *      allocation happens.
95  *
96  * Results:
97  *      Returns a pointer to the newly allocated block.
98  *
99  * Side effects:
100  *      May copy the contents of the original block to the new block
101  *      and deallocate the original block.
102  *
103  *----------------------------------------------------------------------
104  */
105
106 VOID *
107 TclpSysRealloc(
108     VOID *oldPtr,               /* Original block */
109     unsigned int size)          /* New size of block. */
110 {
111     Handle hand;
112     void *newPtr;
113     int maxsize;
114     OSErr err;
115
116         if (tclUseMemTracking) {
117     hand = ((ListEl *) ((Ptr) oldPtr - tclExtraHdlSize))->memoryHandle;
118     } else {
119     hand = RecoverHandle((Ptr) oldPtr);
120         }
121     maxsize = GetHandleSize(hand) - sizeof(Handle);
122     if (maxsize < size) {
123     HUnlock(hand);
124     SetHandleSize(hand,size + tclExtraHdlSize);
125     err = MemError();
126     HLock(hand);
127     if(err==noErr){
128         newPtr=(*hand + tclExtraHdlSize);
129     } else {
130         newPtr = TclpSysAlloc(size, 1);
131         if(newPtr!=NULL) {
132         memmove(newPtr, oldPtr, maxsize);
133         TclpSysFree(oldPtr);
134         }
135         }
136     } else {
137         newPtr = oldPtr;
138     }
139     return newPtr;
140 }
141 \f
142 /*
143  *----------------------------------------------------------------------
144  *
145  * TclpSysAlloc --
146  *
147  *      Allocate a new block of memory free from the System.
148  *
149  * Results:
150  *      Returns a pointer to a new block of memory.
151  *
152  * Side effects:
153  *      May obtain memory from app or sys space.  Info is added to
154  *      overhead lists etc.
155  *
156  *----------------------------------------------------------------------
157  */
158
159 VOID *
160 TclpSysAlloc(
161     long size,          /* Size of block to allocate. */
162     int isBin)          /* Is this a bin allocation? */
163 {
164     Handle hand = NULL;
165     ListEl * newMemoryRecord;
166         int isSysMem = 0;
167         static int initialized=0;
168         
169         if (!initialized) {
170         long response = 0;
171         OSErr err = noErr;
172         int useTempMem = 0;
173         
174         /* Check if we can use temporary memory */
175         initialized=1;
176         err = Gestalt(gestaltOSAttr, &response);
177         if (err == noErr) {
178         useTempMem = response & (1 << gestaltRealTempMemory);
179         }
180         tclUseMemTracking = !useTempMem || (memoryFlags & MEMORY_DONT_USE_TEMPMEM);
181         if(tclUseMemTracking) {
182             tclExtraHdlSize = sizeof(ListEl);
183             /*
184              * We are allocating memory directly from the system
185              * heap. We need to install an exit handle 
186              * to ensure the memory is cleaned up.
187              */
188             TclMacInstallExitToShellPatch(CleanUpExitProc);
189         }
190         }
191
192     if (!(memoryFlags & MEMORY_ALL_SYS)) {
193
194         /*
195          * If the guard handle has been purged, throw it away and try
196          * to allocate it again.
197          */
198
199         if ((toolGuardHandle != NULL) && (*toolGuardHandle == NULL)) {
200             DisposeHandle(toolGuardHandle);
201             toolGuardHandle = NULL;
202         }
203
204         /*
205          * If we have never allocated the guard handle, or it was purged
206          * and thrown away, then try to allocate it again.
207          */
208
209         if (toolGuardHandle == NULL) {
210             toolGuardHandle = NewHandle(TOOLBOX_SPACE);
211             if (toolGuardHandle != NULL) {
212                 HLock(toolGuardHandle);
213                 HPurge(toolGuardHandle);
214             }
215         }
216
217         /*
218          * If we got the handle, lock it and do our allocation.
219          */
220
221         if (toolGuardHandle != NULL) {
222             HLock(toolGuardHandle);
223             hand = NewHandle(size + tclExtraHdlSize);
224             HUnlock(toolGuardHandle);
225         }
226     }
227     if (hand == NULL) {
228         /*
229          * Ran out of memory in application space.  Lets try to get
230          * more memory from system.  Otherwise, we return NULL to
231          * denote failure.
232          */
233         if(!tclUseMemTracking) {
234                 /* Use Temporary Memory instead of System Heap when available */
235                 OSErr err;
236                 isBin = 1; /* always HLockHi TempMemHandles */
237                 hand = TempNewHandle(size + tclExtraHdlSize,&err);
238                 if(err!=noErr) { hand=NULL; }
239         } else {
240         /* Use system heap when tracking memory */
241         isSysMem=1;
242         isBin = 0;
243         hand = NewHandleSys(size + tclExtraHdlSize);
244         }
245         }
246         if (hand == NULL) {
247             return NULL;
248         }
249     if (isBin) {
250         HLockHi(hand);
251     } else {
252         HLock(hand);
253     }
254         if(tclUseMemTracking) {
255         /* Only need to do this when tracking memory */
256         newMemoryRecord = (ListEl *) *hand;
257         newMemoryRecord->memoryHandle = hand;
258         newMemoryRecord->prec = NULL;
259         if(isSysMem) {
260         newMemoryRecord->next = systemMemory;
261         systemMemory = newMemoryRecord;
262         } else {
263         newMemoryRecord->next = appMemory;
264         appMemory = newMemoryRecord;
265         }
266         if(newMemoryRecord->next!=NULL) {
267         newMemoryRecord->next->prec=newMemoryRecord;
268         }
269         }
270         
271     return (*hand + tclExtraHdlSize);
272 }
273 \f
274 /*
275  *----------------------------------------------------------------------
276  *
277  * TclpSysFree --
278  *
279  *      Free memory that we allocated back to the system.
280  *
281  * Results:
282  *      None.
283  *
284  * Side effects:
285  *      Memory is freed.
286  *
287  *----------------------------------------------------------------------
288  */
289
290 void
291 TclpSysFree(
292     void * ptr)         /* Free this system memory. */
293 {
294         if(tclUseMemTracking) {
295     /* Only need to do this when tracking memory */
296     ListEl *memRecord;
297
298     memRecord = (ListEl *) ((Ptr) ptr - tclExtraHdlSize);
299     /* Remove current record from linked list */
300     if(memRecord->next!=NULL) {
301         memRecord->next->prec=memRecord->prec;
302     }
303     if(memRecord->prec!=NULL) {
304         memRecord->prec->next=memRecord->next;
305     }
306     if(memRecord==appMemory) {
307         appMemory=memRecord->next;
308     } else if(memRecord==systemMemory) {
309         systemMemory=memRecord->next;
310     }
311     DisposeHandle(memRecord->memoryHandle);
312         } else {
313     DisposeHandle(RecoverHandle((Ptr) ptr));
314         }
315 }
316 \f
317 /*
318  *----------------------------------------------------------------------
319  *
320  * CleanUpExitProc --
321  *
322  *      This procedure is invoked as an exit handler when ExitToShell
323  *      is called.  It removes any memory that was allocated directly
324  *      from the system heap.  This must be called when the application
325  *      quits or the memory will never be freed.
326  *
327  * Results:
328  *      None.
329  *
330  * Side effects:
331  *      May free memory in the system heap.
332  *
333  *----------------------------------------------------------------------
334  */
335
336 static pascal void
337 CleanUpExitProc()
338 {
339     ListEl * memRecord;
340
341     if(tclUseMemTracking) {
342     /* Only need to do this when tracking memory */
343     while (systemMemory != NULL) {
344         memRecord = systemMemory;
345         systemMemory = memRecord->next;
346         DisposeHandle(memRecord->memoryHandle);
347     }
348     }
349 }
350 \f
351 /*
352  *----------------------------------------------------------------------
353  *
354  * FreeAllMemory --
355  *
356  *      This procedure frees all memory blocks allocated by the memory
357  *      sub-system.  Make sure you don't have any code that references
358  *      any malloced data!
359  *
360  * Results:
361  *      None.
362  *
363  * Side effects:
364  *      Frees all memory allocated by TclpAlloc.
365  *
366  *----------------------------------------------------------------------
367  */
368
369 void
370 FreeAllMemory()
371 {
372     ListEl * memRecord;
373
374         if(tclUseMemTracking) {
375         /* Only need to do this when tracking memory */
376     while (systemMemory != NULL) {
377         memRecord = systemMemory;
378         systemMemory = memRecord->next;
379         DisposeHandle(memRecord->memoryHandle);
380     }
381     while (appMemory != NULL) {
382         memRecord = appMemory;
383         appMemory = memRecord->next;
384         DisposeHandle(memRecord->memoryHandle);
385         }
386     }
387 }
388 \f
389 /*
390  *----------------------------------------------------------------------
391  *
392  * ConfigureMemory --
393  *
394  *      This procedure sets certain flags in this file that control
395  *      how memory is allocated and managed.  This call must be made
396  *      before any call to TclpAlloc is made.
397  *
398  * Results:
399  *      None.
400  *
401  * Side effects:
402  *      Certain state will be changed.
403  *
404  *----------------------------------------------------------------------
405  */
406
407 void
408 ConfigureMemory(
409     int flags)          /* Flags that control memory alloc scheme. */
410 {
411     memoryFlags = flags;
412 }