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.
8 * Copyright (c) 1991-1994 The Regents of the University of California.
9 * Copyright (c) 1994-1998 Sun Microsystems, Inc.
11 * See the file "license.terms" for information on usage and redistribution of
12 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
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.
24 ClientData clientData; /* Address of preserved block. */
25 int refCount; /* Number of Tcl_Preserve calls in effect for
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. */
35 * Global data structures used to hold the list of preserved data references.
36 * These variables are protected by "preserveMutex".
39 static Reference *refArray = NULL; /* First in array of references. */
40 static int spaceAvl = 0; /* Total number of structures available at
42 static int inUse = 0; /* Count of structures currently in use in
44 TCL_DECLARE_MUTEX(preserveMutex)/* To protect the above statics */
46 #define INITIAL_SIZE 2 /* Initial number of reference slots to make */
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.
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. */
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. */
66 int refCount; /* Number of TclHandlePreserve() calls in
67 * effect on this handle. */
71 *----------------------------------------------------------------------
73 * TclFinalizePreserve --
75 * Called during exit processing to clean up the reference array.
81 * Frees the storage of the reference array.
83 *----------------------------------------------------------------------
88 TclFinalizePreserve(void)
90 Tcl_MutexLock(&preserveMutex);
97 Tcl_MutexUnlock(&preserveMutex);
101 *----------------------------------------------------------------------
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.
113 * Information is retained so that the block of memory will not be freed
114 * until at least the matching call to Tcl_Release.
116 *----------------------------------------------------------------------
121 ClientData clientData) /* Pointer to malloc'ed block of memory. */
127 * See if there is already a reference for this pointer. If so, just
128 * increment its reference count.
131 Tcl_MutexLock(&preserveMutex);
132 for (i=0, refPtr=refArray ; i<inUse ; i++, refPtr++) {
133 if (refPtr->clientData == clientData) {
135 Tcl_MutexUnlock(&preserveMutex);
141 * Make a reference array if it doesn't already exist, or make it bigger
145 if (inUse == spaceAvl) {
146 spaceAvl = spaceAvl ? 2*spaceAvl : INITIAL_SIZE;
147 refArray = ckrealloc(refArray, spaceAvl * sizeof(Reference));
151 * Make a new entry for the new reference.
154 refPtr = &refArray[inUse];
155 refPtr->clientData = clientData;
156 refPtr->refCount = 1;
157 refPtr->mustFree = 0;
158 refPtr->freeProc = TCL_STATIC;
160 Tcl_MutexUnlock(&preserveMutex);
164 *----------------------------------------------------------------------
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
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.
179 *----------------------------------------------------------------------
184 ClientData clientData) /* Pointer to malloc'ed block of memory. */
189 Tcl_MutexLock(&preserveMutex);
190 for (i=0, refPtr=refArray ; i<inUse ; i++, refPtr++) {
192 Tcl_FreeProc *freeProc;
194 if (refPtr->clientData != clientData) {
198 if (--refPtr->refCount != 0) {
199 Tcl_MutexUnlock(&preserveMutex);
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.
210 freeProc = refPtr->freeProc;
211 mustFree = refPtr->mustFree;
214 refArray[i] = refArray[inUse];
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
224 Tcl_MutexUnlock(&preserveMutex);
226 if (freeProc == TCL_DYNAMIC) {
229 freeProc(clientData);
234 Tcl_MutexUnlock(&preserveMutex);
237 * Reference not found. This is a bug in the caller.
240 Tcl_Panic("Tcl_Release couldn't find reference for %p", clientData);
244 *----------------------------------------------------------------------
246 * Tcl_EventuallyFree --
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.
256 * Ptr may be released by calling free().
258 *----------------------------------------------------------------------
263 ClientData clientData, /* Pointer to malloc'ed block of memory. */
264 Tcl_FreeProc *freeProc) /* Function to actually do free. */
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!).
274 Tcl_MutexLock(&preserveMutex);
275 for (i = 0, refPtr = refArray; i < inUse; i++, refPtr++) {
276 if (refPtr->clientData != clientData) {
279 if (refPtr->mustFree) {
280 Tcl_Panic("Tcl_EventuallyFree called twice for %p", clientData);
282 refPtr->mustFree = 1;
283 refPtr->freeProc = freeProc;
284 Tcl_MutexUnlock(&preserveMutex);
287 Tcl_MutexUnlock(&preserveMutex);
290 * No reference for this block. Free it now.
293 if (freeProc == TCL_DYNAMIC) {
296 freeProc(clientData);
301 *---------------------------------------------------------------------------
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().
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.
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.
321 *---------------------------------------------------------------------------
326 void *ptr) /* Pointer to an arbitrary block of memory to
327 * be tracked for deletion. Must not be
330 HandleStruct *handlePtr = ckalloc(sizeof(HandleStruct));
332 handlePtr->ptr = ptr;
334 handlePtr->ptr2 = ptr;
336 handlePtr->refCount = 0;
337 return (TclHandle) handlePtr;
341 *---------------------------------------------------------------------------
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.
354 * If nothing is referring to the handle, the handle will be reclaimed.
356 *---------------------------------------------------------------------------
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. */
366 HandleStruct *handlePtr;
368 handlePtr = (HandleStruct *) handle;
370 if (handlePtr->refCount == 0x61616161) {
371 Tcl_Panic("using previously disposed TclHandle %p", handlePtr);
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);
378 handlePtr->ptr = NULL;
379 if (handlePtr->refCount == 0) {
385 *---------------------------------------------------------------------------
387 * TclHandlePreserve --
389 * Declare an interest in the arbitrary malloc'd block associated with
393 * The return value is the handle argument, with its ref count
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.
401 *---------------------------------------------------------------------------
406 TclHandle handle) /* Declare an interest in the block of memory
407 * referenced by this handle. */
409 HandleStruct *handlePtr;
411 handlePtr = (HandleStruct *) handle;
413 if (handlePtr->refCount == 0x61616161) {
414 Tcl_Panic("using previously disposed TclHandle %p", handlePtr);
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);
421 handlePtr->refCount++;
427 *---------------------------------------------------------------------------
429 * TclHandleRelease --
431 * This function is called to release an interest in the malloc'd block
432 * associated with the handle.
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
442 *---------------------------------------------------------------------------
447 TclHandle handle) /* Unregister interest in the block of memory
448 * referenced by this handle. */
450 HandleStruct *handlePtr;
452 handlePtr = (HandleStruct *) handle;
454 if (handlePtr->refCount == 0x61616161) {
455 Tcl_Panic("using previously disposed TclHandle %p", handlePtr);
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);
462 if ((--handlePtr->refCount == 0) && (handlePtr->ptr == NULL)) {