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