tclPreserve.cGo to the documentation of this file.00001 /* 00002 * tclPreserve.c -- 00003 * 00004 * This file contains a collection of functions that are used to make 00005 * sure that widget records and other data structures aren't reallocated 00006 * when there are nested functions that depend on their existence. 00007 * 00008 * Copyright (c) 1991-1994 The Regents of the University of California. 00009 * Copyright (c) 1994-1998 Sun Microsystems, Inc. 00010 * 00011 * See the file "license.terms" for information on usage and redistribution of 00012 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 00013 * 00014 * RCS: @(#) $Id: tclPreserve.c,v 1.10 2007/03/21 18:02:51 dgp Exp $ 00015 */ 00016 00017 #include "tclInt.h" 00018 00019 /* 00020 * The following data structure is used to keep track of all the Tcl_Preserve 00021 * calls that are still in effect. It grows as needed to accommodate any 00022 * number of calls in effect. 00023 */ 00024 00025 typedef struct { 00026 ClientData clientData; /* Address of preserved block. */ 00027 int refCount; /* Number of Tcl_Preserve calls in effect for 00028 * block. */ 00029 int mustFree; /* Non-zero means Tcl_EventuallyFree was 00030 * called while a Tcl_Preserve call was in 00031 * effect, so the structure must be freed when 00032 * refCount becomes zero. */ 00033 Tcl_FreeProc *freeProc; /* Function to call to free. */ 00034 } Reference; 00035 00036 /* 00037 * Global data structures used to hold the list of preserved data references. 00038 * These variables are protected by "preserveMutex". 00039 */ 00040 00041 static Reference *refArray = NULL; /* First in array of references. */ 00042 static int spaceAvl = 0; /* Total number of structures available at 00043 * *firstRefPtr. */ 00044 static int inUse = 0; /* Count of structures currently in use in 00045 * refArray. */ 00046 TCL_DECLARE_MUTEX(preserveMutex)/* To protect the above statics */ 00047 00048 #define INITIAL_SIZE 2 /* Initial number of reference slots to make */ 00049 00050 /* 00051 * The following data structure is used to keep track of whether an arbitrary 00052 * block of memory has been deleted. This is used by the TclHandle code to 00053 * avoid the more time-expensive algorithm of Tcl_Preserve(). This mechanism 00054 * is mainly used when we have lots of references to a few big, expensive 00055 * objects that we don't want to live any longer than necessary. 00056 */ 00057 00058 typedef struct HandleStruct { 00059 void *ptr; /* Pointer to the memory block being tracked. 00060 * This field will become NULL when the memory 00061 * block is deleted. This field must be the 00062 * first in the structure. */ 00063 #ifdef TCL_MEM_DEBUG 00064 void *ptr2; /* Backup copy of the above pointer used to 00065 * ensure that the contents of the handle are 00066 * not changed by anyone else. */ 00067 #endif 00068 int refCount; /* Number of TclHandlePreserve() calls in 00069 * effect on this handle. */ 00070 } HandleStruct; 00071 00072 /* 00073 *---------------------------------------------------------------------- 00074 * 00075 * TclFinalizePreserve -- 00076 * 00077 * Called during exit processing to clean up the reference array. 00078 * 00079 * Results: 00080 * None. 00081 * 00082 * Side effects: 00083 * Frees the storage of the reference array. 00084 * 00085 *---------------------------------------------------------------------- 00086 */ 00087 00088 /* ARGSUSED */ 00089 void 00090 TclFinalizePreserve(void) 00091 { 00092 Tcl_MutexLock(&preserveMutex); 00093 if (spaceAvl != 0) { 00094 ckfree((char *) refArray); 00095 refArray = NULL; 00096 inUse = 0; 00097 spaceAvl = 0; 00098 } 00099 Tcl_MutexUnlock(&preserveMutex); 00100 } 00101 00102 /* 00103 *---------------------------------------------------------------------- 00104 * 00105 * Tcl_Preserve -- 00106 * 00107 * This function is used by a function to declare its interest in a 00108 * particular block of memory, so that the block will not be reallocated 00109 * until a matching call to Tcl_Release has been made. 00110 * 00111 * Results: 00112 * None. 00113 * 00114 * Side effects: 00115 * Information is retained so that the block of memory will not be freed 00116 * until at least the matching call to Tcl_Release. 00117 * 00118 *---------------------------------------------------------------------- 00119 */ 00120 00121 void 00122 Tcl_Preserve( 00123 ClientData clientData) /* Pointer to malloc'ed block of memory. */ 00124 { 00125 Reference *refPtr; 00126 int i; 00127 00128 /* 00129 * See if there is already a reference for this pointer. If so, just 00130 * increment its reference count. 00131 */ 00132 00133 Tcl_MutexLock(&preserveMutex); 00134 for (i=0, refPtr=refArray ; i<inUse ; i++, refPtr++) { 00135 if (refPtr->clientData == clientData) { 00136 refPtr->refCount++; 00137 Tcl_MutexUnlock(&preserveMutex); 00138 return; 00139 } 00140 } 00141 00142 /* 00143 * Make a reference array if it doesn't already exist, or make it bigger 00144 * if it is full. 00145 */ 00146 00147 if (inUse == spaceAvl) { 00148 spaceAvl = spaceAvl ? 2*spaceAvl : INITIAL_SIZE; 00149 refArray = (Reference *) ckrealloc((char *) refArray, 00150 spaceAvl * sizeof(Reference)); 00151 } 00152 00153 /* 00154 * Make a new entry for the new reference. 00155 */ 00156 00157 refPtr = &refArray[inUse]; 00158 refPtr->clientData = clientData; 00159 refPtr->refCount = 1; 00160 refPtr->mustFree = 0; 00161 refPtr->freeProc = TCL_STATIC; 00162 inUse += 1; 00163 Tcl_MutexUnlock(&preserveMutex); 00164 } 00165 00166 /* 00167 *---------------------------------------------------------------------- 00168 * 00169 * Tcl_Release -- 00170 * 00171 * This function is called to cancel a previous call to Tcl_Preserve, 00172 * thereby allowing a block of memory to be freed (if no one else cares 00173 * about it). 00174 * 00175 * Results: 00176 * None. 00177 * 00178 * Side effects: 00179 * If Tcl_EventuallyFree has been called for clientData, and if no other 00180 * call to Tcl_Preserve is still in effect, the block of memory is freed. 00181 * 00182 *---------------------------------------------------------------------- 00183 */ 00184 00185 void 00186 Tcl_Release( 00187 ClientData clientData) /* Pointer to malloc'ed block of memory. */ 00188 { 00189 Reference *refPtr; 00190 int i; 00191 00192 Tcl_MutexLock(&preserveMutex); 00193 for (i=0, refPtr=refArray ; i<inUse ; i++, refPtr++) { 00194 int mustFree; 00195 Tcl_FreeProc *freeProc; 00196 00197 if (refPtr->clientData != clientData) { 00198 continue; 00199 } 00200 00201 if (--refPtr->refCount != 0) { 00202 Tcl_MutexUnlock(&preserveMutex); 00203 return; 00204 } 00205 00206 /* 00207 * Must remove information from the slot before calling freeProc to 00208 * avoid reentrancy problems if the freeProc calls Tcl_Preserve on the 00209 * same clientData. Copy down the last reference in the array to 00210 * overwrite the current slot. 00211 */ 00212 00213 freeProc = refPtr->freeProc; 00214 mustFree = refPtr->mustFree; 00215 inUse--; 00216 if (i < inUse) { 00217 refArray[i] = refArray[inUse]; 00218 } 00219 00220 /* 00221 * Now committed to disposing the data. But first, we've patched up 00222 * all the global data structures so we should release the mutex now. 00223 * Only then should we dabble around with potentially-slow memory 00224 * managers... 00225 */ 00226 00227 Tcl_MutexUnlock(&preserveMutex); 00228 if (mustFree) { 00229 if (freeProc == TCL_DYNAMIC) { 00230 ckfree((char *) clientData); 00231 } else { 00232 (*freeProc)((char *) clientData); 00233 } 00234 } 00235 return; 00236 } 00237 Tcl_MutexUnlock(&preserveMutex); 00238 00239 /* 00240 * Reference not found. This is a bug in the caller. 00241 */ 00242 00243 Tcl_Panic("Tcl_Release couldn't find reference for 0x%x", clientData); 00244 } 00245 00246 /* 00247 *---------------------------------------------------------------------- 00248 * 00249 * Tcl_EventuallyFree -- 00250 * 00251 * Free up a block of memory, unless a call to Tcl_Preserve is in effect 00252 * for that block. In this case, defer the free until all calls to 00253 * Tcl_Preserve have been undone by matching calls to Tcl_Release. 00254 * 00255 * Results: 00256 * None. 00257 * 00258 * Side effects: 00259 * Ptr may be released by calling free(). 00260 * 00261 *---------------------------------------------------------------------- 00262 */ 00263 00264 void 00265 Tcl_EventuallyFree( 00266 ClientData clientData, /* Pointer to malloc'ed block of memory. */ 00267 Tcl_FreeProc *freeProc) /* Function to actually do free. */ 00268 { 00269 Reference *refPtr; 00270 int i; 00271 00272 /* 00273 * See if there is a reference for this pointer. If so, set its "mustFree" 00274 * flag (the flag had better not be set already!). 00275 */ 00276 00277 Tcl_MutexLock(&preserveMutex); 00278 for (i = 0, refPtr = refArray; i < inUse; i++, refPtr++) { 00279 if (refPtr->clientData != clientData) { 00280 continue; 00281 } 00282 if (refPtr->mustFree) { 00283 Tcl_Panic("Tcl_EventuallyFree called twice for 0x%x", 00284 clientData); 00285 } 00286 refPtr->mustFree = 1; 00287 refPtr->freeProc = freeProc; 00288 Tcl_MutexUnlock(&preserveMutex); 00289 return; 00290 } 00291 Tcl_MutexUnlock(&preserveMutex); 00292 00293 /* 00294 * No reference for this block. Free it now. 00295 */ 00296 00297 if (freeProc == TCL_DYNAMIC) { 00298 ckfree((char *) clientData); 00299 } else { 00300 (*freeProc)((char *)clientData); 00301 } 00302 } 00303 00304 /* 00305 *--------------------------------------------------------------------------- 00306 * 00307 * TclHandleCreate -- 00308 * 00309 * Allocate a handle that contains enough information to determine if an 00310 * arbitrary malloc'd block has been deleted. This is used to avoid the 00311 * more time-expensive algorithm of Tcl_Preserve(). 00312 * 00313 * Results: 00314 * The return value is a TclHandle that refers to the given malloc'd 00315 * block. Doubly dereferencing the returned handle will give back the 00316 * pointer to the block, or will give NULL if the block has been deleted. 00317 * 00318 * Side effects: 00319 * The caller must keep track of this handle (generally by storing it in 00320 * a field in the malloc'd block) and call TclHandleFree() on this handle 00321 * when the block is deleted. Everything else that wishes to keep track 00322 * of whether the malloc'd block has been deleted should use calls to 00323 * TclHandlePreserve() and TclHandleRelease() on the associated handle. 00324 * 00325 *--------------------------------------------------------------------------- 00326 */ 00327 00328 TclHandle 00329 TclHandleCreate( 00330 void *ptr) /* Pointer to an arbitrary block of memory to 00331 * be tracked for deletion. Must not be 00332 * NULL. */ 00333 { 00334 HandleStruct *handlePtr; 00335 00336 handlePtr = (HandleStruct *) ckalloc(sizeof(HandleStruct)); 00337 handlePtr->ptr = ptr; 00338 #ifdef TCL_MEM_DEBUG 00339 handlePtr->ptr2 = ptr; 00340 #endif 00341 handlePtr->refCount = 0; 00342 return (TclHandle) handlePtr; 00343 } 00344 00345 /* 00346 *--------------------------------------------------------------------------- 00347 * 00348 * TclHandleFree -- 00349 * 00350 * Called when the arbitrary malloc'd block associated with the handle is 00351 * being deleted. Modifies the handle so that doubly dereferencing it 00352 * will give NULL. This informs any user of the handle that the block of 00353 * memory formerly referenced by the handle has been freed. 00354 * 00355 * Results: 00356 * None. 00357 * 00358 * Side effects: 00359 * If nothing is referring to the handle, the handle will be reclaimed. 00360 * 00361 *--------------------------------------------------------------------------- 00362 */ 00363 00364 void 00365 TclHandleFree( 00366 TclHandle handle) /* Previously created handle associated with a 00367 * malloc'd block that is being deleted. The 00368 * handle is modified so that doubly 00369 * dereferencing it will give NULL. */ 00370 { 00371 HandleStruct *handlePtr; 00372 00373 handlePtr = (HandleStruct *) handle; 00374 #ifdef TCL_MEM_DEBUG 00375 if (handlePtr->refCount == 0x61616161) { 00376 Tcl_Panic("using previously disposed TclHandle %x", handlePtr); 00377 } 00378 if (handlePtr->ptr2 != handlePtr->ptr) { 00379 Tcl_Panic("someone has changed the block referenced by the handle %x\nfrom %x to %x", 00380 handlePtr, handlePtr->ptr2, handlePtr->ptr); 00381 } 00382 #endif 00383 handlePtr->ptr = NULL; 00384 if (handlePtr->refCount == 0) { 00385 ckfree((char *) handlePtr); 00386 } 00387 } 00388 00389 /* 00390 *--------------------------------------------------------------------------- 00391 * 00392 * TclHandlePreserve -- 00393 * 00394 * Declare an interest in the arbitrary malloc'd block associated with 00395 * the handle. 00396 * 00397 * Results: 00398 * The return value is the handle argument, with its ref count 00399 * incremented. 00400 * 00401 * Side effects: 00402 * For each call to TclHandlePreserve(), there should be a matching call 00403 * to TclHandleRelease() when the caller is no longer interested in the 00404 * malloc'd block associated with the handle. 00405 * 00406 *--------------------------------------------------------------------------- 00407 */ 00408 00409 TclHandle 00410 TclHandlePreserve( 00411 TclHandle handle) /* Declare an interest in the block of memory 00412 * referenced by this handle. */ 00413 { 00414 HandleStruct *handlePtr; 00415 00416 handlePtr = (HandleStruct *) handle; 00417 #ifdef TCL_MEM_DEBUG 00418 if (handlePtr->refCount == 0x61616161) { 00419 Tcl_Panic("using previously disposed TclHandle %x", handlePtr); 00420 } 00421 if ((handlePtr->ptr != NULL) && (handlePtr->ptr != handlePtr->ptr2)) { 00422 Tcl_Panic("someone has changed the block referenced by the handle %x\nfrom %x to %x", 00423 handlePtr, handlePtr->ptr2, handlePtr->ptr); 00424 } 00425 #endif 00426 handlePtr->refCount++; 00427 00428 return handle; 00429 } 00430 00431 /* 00432 *--------------------------------------------------------------------------- 00433 * 00434 * TclHandleRelease -- 00435 * 00436 * This function is called to release an interest in the malloc'd block 00437 * associated with the handle. 00438 * 00439 * Results: 00440 * None. 00441 * 00442 * Side effects: 00443 * The ref count of the handle is decremented. If the malloc'd block has 00444 * been freed and if no one is using the handle any more, the handle will 00445 * be reclaimed. 00446 * 00447 *--------------------------------------------------------------------------- 00448 */ 00449 00450 void 00451 TclHandleRelease( 00452 TclHandle handle) /* Unregister interest in the block of memory 00453 * referenced by this handle. */ 00454 { 00455 HandleStruct *handlePtr; 00456 00457 handlePtr = (HandleStruct *) handle; 00458 #ifdef TCL_MEM_DEBUG 00459 if (handlePtr->refCount == 0x61616161) { 00460 Tcl_Panic("using previously disposed TclHandle %x", handlePtr); 00461 } 00462 if ((handlePtr->ptr != NULL) && (handlePtr->ptr != handlePtr->ptr2)) { 00463 Tcl_Panic("someone has changed the block referenced by the handle %x\nfrom %x to %x", 00464 handlePtr, handlePtr->ptr2, handlePtr->ptr); 00465 } 00466 #endif 00467 handlePtr->refCount--; 00468 if ((handlePtr->refCount == 0) && (handlePtr->ptr == NULL)) { 00469 ckfree((char *) handlePtr); 00470 } 00471 } 00472 00473 /* 00474 * Local Variables: 00475 * mode: c 00476 * c-basic-offset: 4 00477 * fill-column: 78 00478 * End: 00479 */
Generated on Wed Mar 12 12:18:20 2008 by 1.5.1 |