OSDN Git Service

Please enter the commit message for your changes. Lines starting
[eos/base.git] / util / src / TclTk / tcl8.6.12 / generic / tclPreserve.c
1 /*
2  * tclPreserve.c --
3  *
4  *      This file contains a collection of functions that are used to make
5  *      sure that widget records and other data structures aren't reallocated
6  *      when there are nested functions that depend on their existence.
7  *
8  * Copyright (c) 1991-1994 The Regents of the University of California.
9  * Copyright (c) 1994-1998 Sun Microsystems, Inc.
10  *
11  * See the file "license.terms" for information on usage and redistribution of
12  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
13  */
14
15 #include "tclInt.h"
16
17 /*
18  * The following data structure is used to keep track of all the Tcl_Preserve
19  * calls that are still in effect. It grows as needed to accommodate any
20  * number of calls in effect.
21  */
22
23 typedef struct {
24     ClientData clientData;      /* Address of preserved block. */
25     int refCount;               /* Number of Tcl_Preserve calls in effect for
26                                  * block. */
27     int mustFree;               /* Non-zero means Tcl_EventuallyFree was
28                                  * called while a Tcl_Preserve call was in
29                                  * effect, so the structure must be freed when
30                                  * refCount becomes zero. */
31     Tcl_FreeProc *freeProc;     /* Function to call to free. */
32 } Reference;
33
34 /*
35  * Global data structures used to hold the list of preserved data references.
36  * These variables are protected by "preserveMutex".
37  */
38
39 static Reference *refArray = NULL;      /* First in array of references. */
40 static int spaceAvl = 0;        /* Total number of structures available at
41                                  * *firstRefPtr. */
42 static int inUse = 0;           /* Count of structures currently in use in
43                                  * refArray. */
44 TCL_DECLARE_MUTEX(preserveMutex)/* To protect the above statics */
45
46 #define INITIAL_SIZE    2       /* Initial number of reference slots to make */
47
48 /*
49  * The following data structure is used to keep track of whether an arbitrary
50  * block of memory has been deleted. This is used by the TclHandle code to
51  * avoid the more time-expensive algorithm of Tcl_Preserve(). This mechanism
52  * is mainly used when we have lots of references to a few big, expensive
53  * objects that we don't want to live any longer than necessary.
54  */
55
56 typedef struct HandleStruct {
57     void *ptr;                  /* Pointer to the memory block being tracked.
58                                  * This field will become NULL when the memory
59                                  * block is deleted. This field must be the
60                                  * first in the structure. */
61 #ifdef TCL_MEM_DEBUG
62     void *ptr2;                 /* Backup copy of the above pointer used to
63                                  * ensure that the contents of the handle are
64                                  * not changed by anyone else. */
65 #endif
66     int refCount;               /* Number of TclHandlePreserve() calls in
67                                  * effect on this handle. */
68 } HandleStruct;
69 \f
70 /*
71  *----------------------------------------------------------------------
72  *
73  * TclFinalizePreserve --
74  *
75  *      Called during exit processing to clean up the reference array.
76  *
77  * Results:
78  *      None.
79  *
80  * Side effects:
81  *      Frees the storage of the reference array.
82  *
83  *----------------------------------------------------------------------
84  */
85
86         /* ARGSUSED */
87 void
88 TclFinalizePreserve(void)
89 {
90     Tcl_MutexLock(&preserveMutex);
91     if (spaceAvl != 0) {
92         ckfree(refArray);
93         refArray = NULL;
94         inUse = 0;
95         spaceAvl = 0;
96     }
97     Tcl_MutexUnlock(&preserveMutex);
98 }
99 \f
100 /*
101  *----------------------------------------------------------------------
102  *
103  * Tcl_Preserve --
104  *
105  *      This function is used by a function to declare its interest in a
106  *      particular block of memory, so that the block will not be reallocated
107  *      until a matching call to Tcl_Release has been made.
108  *
109  * Results:
110  *      None.
111  *
112  * Side effects:
113  *      Information is retained so that the block of memory will not be freed
114  *      until at least the matching call to Tcl_Release.
115  *
116  *----------------------------------------------------------------------
117  */
118
119 void
120 Tcl_Preserve(
121     ClientData clientData)      /* Pointer to malloc'ed block of memory. */
122 {
123     Reference *refPtr;
124     int i;
125
126     /*
127      * See if there is already a reference for this pointer. If so, just
128      * increment its reference count.
129      */
130
131     Tcl_MutexLock(&preserveMutex);
132     for (i=0, refPtr=refArray ; i<inUse ; i++, refPtr++) {
133         if (refPtr->clientData == clientData) {
134             refPtr->refCount++;
135             Tcl_MutexUnlock(&preserveMutex);
136             return;
137         }
138     }
139
140     /*
141      * Make a reference array if it doesn't already exist, or make it bigger
142      * if it is full.
143      */
144
145     if (inUse == spaceAvl) {
146         spaceAvl = spaceAvl ? 2*spaceAvl : INITIAL_SIZE;
147         refArray = ckrealloc(refArray, spaceAvl * sizeof(Reference));
148     }
149
150     /*
151      * Make a new entry for the new reference.
152      */
153
154     refPtr = &refArray[inUse];
155     refPtr->clientData = clientData;
156     refPtr->refCount = 1;
157     refPtr->mustFree = 0;
158     refPtr->freeProc = TCL_STATIC;
159     inUse += 1;
160     Tcl_MutexUnlock(&preserveMutex);
161 }
162 \f
163 /*
164  *----------------------------------------------------------------------
165  *
166  * Tcl_Release --
167  *
168  *      This function is called to cancel a previous call to Tcl_Preserve,
169  *      thereby allowing a block of memory to be freed (if no one else cares
170  *      about it).
171  *
172  * Results:
173  *      None.
174  *
175  * Side effects:
176  *      If Tcl_EventuallyFree has been called for clientData, and if no other
177  *      call to Tcl_Preserve is still in effect, the block of memory is freed.
178  *
179  *----------------------------------------------------------------------
180  */
181
182 void
183 Tcl_Release(
184     ClientData clientData)      /* Pointer to malloc'ed block of memory. */
185 {
186     Reference *refPtr;
187     int i;
188
189     Tcl_MutexLock(&preserveMutex);
190     for (i=0, refPtr=refArray ; i<inUse ; i++, refPtr++) {
191         int mustFree;
192         Tcl_FreeProc *freeProc;
193
194         if (refPtr->clientData != clientData) {
195             continue;
196         }
197
198         if (--refPtr->refCount != 0) {
199             Tcl_MutexUnlock(&preserveMutex);
200             return;
201         }
202
203         /*
204          * Must remove information from the slot before calling freeProc to
205          * avoid reentrancy problems if the freeProc calls Tcl_Preserve on the
206          * same clientData. Copy down the last reference in the array to
207          * overwrite the current slot.
208          */
209
210         freeProc = refPtr->freeProc;
211         mustFree = refPtr->mustFree;
212         inUse--;
213         if (i < inUse) {
214             refArray[i] = refArray[inUse];
215         }
216
217         /*
218          * Now committed to disposing the data. But first, we've patched up
219          * all the global data structures so we should release the mutex now.
220          * Only then should we dabble around with potentially-slow memory
221          * managers...
222          */
223
224         Tcl_MutexUnlock(&preserveMutex);
225         if (mustFree) {
226             if (freeProc == TCL_DYNAMIC) {
227                 ckfree(clientData);
228             } else {
229                 freeProc(clientData);
230             }
231         }
232         return;
233     }
234     Tcl_MutexUnlock(&preserveMutex);
235
236     /*
237      * Reference not found. This is a bug in the caller.
238      */
239
240     Tcl_Panic("Tcl_Release couldn't find reference for %p", clientData);
241 }
242 \f
243 /*
244  *----------------------------------------------------------------------
245  *
246  * Tcl_EventuallyFree --
247  *
248  *      Free up a block of memory, unless a call to Tcl_Preserve is in effect
249  *      for that block. In this case, defer the free until all calls to
250  *      Tcl_Preserve have been undone by matching calls to Tcl_Release.
251  *
252  * Results:
253  *      None.
254  *
255  * Side effects:
256  *      Ptr may be released by calling free().
257  *
258  *----------------------------------------------------------------------
259  */
260
261 void
262 Tcl_EventuallyFree(
263     ClientData clientData,      /* Pointer to malloc'ed block of memory. */
264     Tcl_FreeProc *freeProc)     /* Function to actually do free. */
265 {
266     Reference *refPtr;
267     int i;
268
269     /*
270      * See if there is a reference for this pointer. If so, set its "mustFree"
271      * flag (the flag had better not be set already!).
272      */
273
274     Tcl_MutexLock(&preserveMutex);
275     for (i = 0, refPtr = refArray; i < inUse; i++, refPtr++) {
276         if (refPtr->clientData != clientData) {
277             continue;
278         }
279         if (refPtr->mustFree) {
280             Tcl_Panic("Tcl_EventuallyFree called twice for %p", clientData);
281         }
282         refPtr->mustFree = 1;
283         refPtr->freeProc = freeProc;
284         Tcl_MutexUnlock(&preserveMutex);
285         return;
286     }
287     Tcl_MutexUnlock(&preserveMutex);
288
289     /*
290      * No reference for this block.  Free it now.
291      */
292
293     if (freeProc == TCL_DYNAMIC) {
294         ckfree(clientData);
295     } else {
296         freeProc(clientData);
297     }
298 }
299 \f
300 /*
301  *---------------------------------------------------------------------------
302  *
303  * TclHandleCreate --
304  *
305  *      Allocate a handle that contains enough information to determine if an
306  *      arbitrary malloc'd block has been deleted. This is used to avoid the
307  *      more time-expensive algorithm of Tcl_Preserve().
308  *
309  * Results:
310  *      The return value is a TclHandle that refers to the given malloc'd
311  *      block. Doubly dereferencing the returned handle will give back the
312  *      pointer to the block, or will give NULL if the block has been deleted.
313  *
314  * Side effects:
315  *      The caller must keep track of this handle (generally by storing it in
316  *      a field in the malloc'd block) and call TclHandleFree() on this handle
317  *      when the block is deleted. Everything else that wishes to keep track
318  *      of whether the malloc'd block has been deleted should use calls to
319  *      TclHandlePreserve() and TclHandleRelease() on the associated handle.
320  *
321  *---------------------------------------------------------------------------
322  */
323
324 TclHandle
325 TclHandleCreate(
326     void *ptr)                  /* Pointer to an arbitrary block of memory to
327                                  * be tracked for deletion. Must not be
328                                  * NULL. */
329 {
330     HandleStruct *handlePtr = ckalloc(sizeof(HandleStruct));
331
332     handlePtr->ptr = ptr;
333 #ifdef TCL_MEM_DEBUG
334     handlePtr->ptr2 = ptr;
335 #endif
336     handlePtr->refCount = 0;
337     return (TclHandle) handlePtr;
338 }
339 \f
340 /*
341  *---------------------------------------------------------------------------
342  *
343  * TclHandleFree --
344  *
345  *      Called when the arbitrary malloc'd block associated with the handle is
346  *      being deleted. Modifies the handle so that doubly dereferencing it
347  *      will give NULL. This informs any user of the handle that the block of
348  *      memory formerly referenced by the handle has been freed.
349  *
350  * Results:
351  *      None.
352  *
353  * Side effects:
354  *      If nothing is referring to the handle, the handle will be reclaimed.
355  *
356  *---------------------------------------------------------------------------
357  */
358
359 void
360 TclHandleFree(
361     TclHandle handle)           /* Previously created handle associated with a
362                                  * malloc'd block that is being deleted. The
363                                  * handle is modified so that doubly
364                                  * dereferencing it will give NULL. */
365 {
366     HandleStruct *handlePtr;
367
368     handlePtr = (HandleStruct *) handle;
369 #ifdef TCL_MEM_DEBUG
370     if (handlePtr->refCount == 0x61616161) {
371         Tcl_Panic("using previously disposed TclHandle %p", handlePtr);
372     }
373     if (handlePtr->ptr2 != handlePtr->ptr) {
374         Tcl_Panic("someone has changed the block referenced by the handle %p\nfrom %p to %p",
375                 handlePtr, handlePtr->ptr2, handlePtr->ptr);
376     }
377 #endif
378     handlePtr->ptr = NULL;
379     if (handlePtr->refCount == 0) {
380         ckfree(handlePtr);
381     }
382 }
383 \f
384 /*
385  *---------------------------------------------------------------------------
386  *
387  * TclHandlePreserve --
388  *
389  *      Declare an interest in the arbitrary malloc'd block associated with
390  *      the handle.
391  *
392  * Results:
393  *      The return value is the handle argument, with its ref count
394  *      incremented.
395  *
396  * Side effects:
397  *      For each call to TclHandlePreserve(), there should be a matching call
398  *      to TclHandleRelease() when the caller is no longer interested in the
399  *      malloc'd block associated with the handle.
400  *
401  *---------------------------------------------------------------------------
402  */
403
404 TclHandle
405 TclHandlePreserve(
406     TclHandle handle)           /* Declare an interest in the block of memory
407                                  * referenced by this handle. */
408 {
409     HandleStruct *handlePtr;
410
411     handlePtr = (HandleStruct *) handle;
412 #ifdef TCL_MEM_DEBUG
413     if (handlePtr->refCount == 0x61616161) {
414         Tcl_Panic("using previously disposed TclHandle %p", handlePtr);
415     }
416     if ((handlePtr->ptr != NULL) && (handlePtr->ptr != handlePtr->ptr2)) {
417         Tcl_Panic("someone has changed the block referenced by the handle %p\nfrom %p to %p",
418                 handlePtr, handlePtr->ptr2, handlePtr->ptr);
419     }
420 #endif
421     handlePtr->refCount++;
422
423     return handle;
424 }
425 \f
426 /*
427  *---------------------------------------------------------------------------
428  *
429  * TclHandleRelease --
430  *
431  *      This function is called to release an interest in the malloc'd block
432  *      associated with the handle.
433  *
434  * Results:
435  *      None.
436  *
437  * Side effects:
438  *      The ref count of the handle is decremented. If the malloc'd block has
439  *      been freed and if no one is using the handle any more, the handle will
440  *      be reclaimed.
441  *
442  *---------------------------------------------------------------------------
443  */
444
445 void
446 TclHandleRelease(
447     TclHandle handle)           /* Unregister interest in the block of memory
448                                  * referenced by this handle. */
449 {
450     HandleStruct *handlePtr;
451
452     handlePtr = (HandleStruct *) handle;
453 #ifdef TCL_MEM_DEBUG
454     if (handlePtr->refCount == 0x61616161) {
455         Tcl_Panic("using previously disposed TclHandle %p", handlePtr);
456     }
457     if ((handlePtr->ptr != NULL) && (handlePtr->ptr != handlePtr->ptr2)) {
458         Tcl_Panic("someone has changed the block referenced by the handle %p\nfrom %p to %p",
459                 handlePtr, handlePtr->ptr2, handlePtr->ptr);
460     }
461 #endif
462     if ((--handlePtr->refCount == 0) && (handlePtr->ptr == NULL)) {
463         ckfree(handlePtr);
464     }
465 }
466 \f
467 /*
468  * Local Variables:
469  * mode: c
470  * c-basic-offset: 4
471  * fill-column: 78
472  * End:
473  */