diff options
Diffstat (limited to 'generic/tclPreserve.c')
| -rw-r--r-- | generic/tclPreserve.c | 371 | 
1 files changed, 178 insertions, 193 deletions
| diff --git a/generic/tclPreserve.c b/generic/tclPreserve.c index 04615b7..5c6097f 100644 --- a/generic/tclPreserve.c +++ b/generic/tclPreserve.c @@ -1,82 +1,76 @@ -/*  +/*   * tclPreserve.c --   * - *	This file contains a collection of procedures that are used - *	to make sure that widget records and other data structures - *	aren't reallocated when there are nested procedures that - *	depend on their existence. + *	This file contains a collection of functions that are used to make + *	sure that widget records and other data structures aren't reallocated + *	when there are nested functions that depend on their existence.   *   * Copyright (c) 1991-1994 The Regents of the University of California.   * Copyright (c) 1994-1998 Sun Microsystems, Inc.   * - * See the file "license.terms" for information on usage and redistribution - * of this file, and for a DISCLAIMER OF ALL WARRANTIES. - * - * RCS: @(#) $Id: tclPreserve.c,v 1.3.34.1 2003/07/16 21:25:07 hobbs Exp $ + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES.   */  #include "tclInt.h"  /* - * The following data structure is used to keep track of all the - * Tcl_Preserve calls that are still in effect.  It grows as needed - * to accommodate any number of calls in effect. + * The following data structure is used to keep track of all the Tcl_Preserve + * calls that are still in effect. It grows as needed to accommodate any + * number of calls in effect.   */  typedef struct {      ClientData clientData;	/* Address of preserved block. */ -    int refCount;		/* Number of Tcl_Preserve calls in effect -				 * for block. */ +    size_t refCount;		/* Number of Tcl_Preserve calls in effect for +				 * block. */      int mustFree;		/* Non-zero means Tcl_EventuallyFree was  				 * called while a Tcl_Preserve call was in -				 * effect, so the structure must be freed -				 * when refCount becomes zero. */ -    Tcl_FreeProc *freeProc;	/* Procedure to call to free. */ +				 * effect, so the structure must be freed when +				 * refCount becomes zero. */ +    Tcl_FreeProc *freeProc;	/* Function to call to free. */  } Reference; -static Reference *refArray;	/* First in array of references. */ -static int spaceAvl = 0;	/* Total number of structures available -				 * at *firstRefPtr. */ -static int inUse = 0;		/* Count of structures currently in use -				 * in refArray. */ -#define INITIAL_SIZE 2 +/* + * Global data structures used to hold the list of preserved data references. + * These variables are protected by "preserveMutex". + */ + +static Reference *refArray = NULL;	/* First in array of references. */ +static int spaceAvl = 0;	/* Total number of structures available at +				 * *firstRefPtr. */ +static int inUse = 0;		/* Count of structures currently in use in +				 * refArray. */  TCL_DECLARE_MUTEX(preserveMutex)/* To protect the above statics */ +#define INITIAL_SIZE	2	/* Initial number of reference slots to make */ +  /* - * The following data structure is used to keep track of whether an - * arbitrary block of memory has been deleted.  This is used by the - * TclHandle code to avoid the more time-expensive algorithm of - * Tcl_Preserve().  This mechanism is mainly used when we have lots of - * references to a few big, expensive objects that we don't want to live - * any longer than necessary. + * The following data structure is used to keep track of whether an arbitrary + * block of memory has been deleted. This is used by the TclHandle code to + * avoid the more time-expensive algorithm of Tcl_Preserve(). This mechanism + * is mainly used when we have lots of references to a few big, expensive + * objects that we don't want to live any longer than necessary.   */  typedef struct HandleStruct { -    VOID *ptr;			/* Pointer to the memory block being -				 * tracked.  This field will become NULL when -				 * the memory block is deleted.  This field -				 * must be the first in the structure. */ +    void *ptr;			/* Pointer to the memory block being tracked. +				 * This field will become NULL when the memory +				 * block is deleted. This field must be the +				 * first in the structure. */  #ifdef TCL_MEM_DEBUG -    VOID *ptr2;			/* Backup copy of the abpve pointer used to +    void *ptr2;			/* Backup copy of the above pointer used to  				 * ensure that the contents of the handle are  				 * not changed by anyone else. */  #endif -    int refCount;		/* Number of TclHandlePreserve() calls in +    size_t refCount;		/* Number of TclHandlePreserve() calls in  				 * effect on this handle. */  } HandleStruct; - - -/* - * Static routines in this file: - */ - -static void	PreserveExitProc _ANSI_ARGS_((ClientData clientData)); -  /*   *----------------------------------------------------------------------   * - * PreserveExitProc -- + * TclFinalizePreserve --   *   *	Called during exit processing to clean up the reference array.   * @@ -90,16 +84,15 @@ static void	PreserveExitProc _ANSI_ARGS_((ClientData clientData));   */  	/* ARGSUSED */ -static void -PreserveExitProc(clientData) -    ClientData clientData;		/* NULL -Unused. */ +void +TclFinalizePreserve(void)  {      Tcl_MutexLock(&preserveMutex);      if (spaceAvl != 0) { -        ckfree((char *) refArray); -        refArray = (Reference *) NULL; -        inUse = 0; -        spaceAvl = 0; +	ckfree(refArray); +	refArray = NULL; +	inUse = 0; +	spaceAvl = 0;      }      Tcl_MutexUnlock(&preserveMutex);  } @@ -109,34 +102,34 @@ PreserveExitProc(clientData)   *   * Tcl_Preserve --   * - *	This procedure is used by a procedure to declare its interest - *	in a particular block of memory, so that the block will not be - *	reallocated until a matching call to Tcl_Release has been made. + *	This function is used by a function to declare its interest in a + *	particular block of memory, so that the block will not be reallocated + *	until a matching call to Tcl_Release has been made.   *   * Results:   *	None.   *   * Side effects: - *	Information is retained so that the block of memory will - *	not be freed until at least the matching call to Tcl_Release. + *	Information is retained so that the block of memory will not be freed + *	until at least the matching call to Tcl_Release.   *   *----------------------------------------------------------------------   */  void -Tcl_Preserve(clientData) -    ClientData clientData;	/* Pointer to malloc'ed block of memory. */ +Tcl_Preserve( +    ClientData clientData)	/* Pointer to malloc'ed block of memory. */  {      Reference *refPtr;      int i;      /* -     * See if there is already a reference for this pointer.  If so, -     * just increment its reference count. +     * See if there is already a reference for this pointer. If so, just +     * increment its reference count.       */      Tcl_MutexLock(&preserveMutex); -    for (i = 0, refPtr = refArray; i < inUse; i++, refPtr++) { +    for (i=0, refPtr=refArray ; i<inUse ; i++, refPtr++) {  	if (refPtr->clientData == clientData) {  	    refPtr->refCount++;  	    Tcl_MutexUnlock(&preserveMutex); @@ -145,28 +138,13 @@ Tcl_Preserve(clientData)      }      /* -     * Make a reference array if it doesn't already exist, or make it -     * bigger if it is full. +     * Make a reference array if it doesn't already exist, or make it bigger +     * if it is full.       */      if (inUse == spaceAvl) { -	if (spaceAvl == 0) { -            Tcl_CreateExitHandler((Tcl_ExitProc *) PreserveExitProc, -                    (ClientData) NULL); -	    refArray = (Reference *) ckalloc((unsigned) -		    (INITIAL_SIZE*sizeof(Reference))); -	    spaceAvl = INITIAL_SIZE; -	} else { -	    Reference *new; - -	    new = (Reference *) ckalloc((unsigned) -		    (2*spaceAvl*sizeof(Reference))); -	    memcpy((VOID *) new, (VOID *) refArray, -                    spaceAvl*sizeof(Reference)); -	    ckfree((char *) refArray); -	    refArray = new; -	    spaceAvl *= 2; -	} +	spaceAvl = spaceAvl ? 2*spaceAvl : INITIAL_SIZE; +	refArray = ckrealloc(refArray, spaceAvl * sizeof(Reference));      }      /* @@ -177,7 +155,7 @@ Tcl_Preserve(clientData)      refPtr->clientData = clientData;      refPtr->refCount = 1;      refPtr->mustFree = 0; -    refPtr->freeProc = TCL_STATIC; +    refPtr->freeProc = 0;      inUse += 1;      Tcl_MutexUnlock(&preserveMutex);  } @@ -187,71 +165,79 @@ Tcl_Preserve(clientData)   *   * Tcl_Release --   * - *	This procedure is called to cancel a previous call to - *	Tcl_Preserve, thereby allowing a block of memory to be - *	freed (if no one else cares about it). + *	This function is called to cancel a previous call to Tcl_Preserve, + *	thereby allowing a block of memory to be freed (if no one else cares + *	about it).   *   * Results:   *	None.   *   * Side effects: - *	If Tcl_EventuallyFree has been called for clientData, and if - *	no other call to Tcl_Preserve is still in effect, the block of - *	memory is freed. + *	If Tcl_EventuallyFree has been called for clientData, and if no other + *	call to Tcl_Preserve is still in effect, the block of memory is freed.   *   *----------------------------------------------------------------------   */  void -Tcl_Release(clientData) -    ClientData clientData;	/* Pointer to malloc'ed block of memory. */ +Tcl_Release( +    ClientData clientData)	/* Pointer to malloc'ed block of memory. */  {      Reference *refPtr; -    int mustFree; -    Tcl_FreeProc *freeProc;      int i;      Tcl_MutexLock(&preserveMutex); -    for (i = 0, refPtr = refArray; i < inUse; i++, refPtr++) { +    for (i=0, refPtr=refArray ; i<inUse ; i++, refPtr++) { +	int mustFree; +	Tcl_FreeProc *freeProc; +  	if (refPtr->clientData != clientData) {  	    continue;  	} -	refPtr->refCount--; -	if (refPtr->refCount == 0) { - -            /* -             * Must remove information from the slot before calling freeProc -             * to avoid reentrancy problems if the freeProc calls Tcl_Preserve -             * on the same clientData. Copy down the last reference in the -             * array to overwrite the current slot. -             */ - -            freeProc = refPtr->freeProc; -            mustFree = refPtr->mustFree; -	    inUse--; -	    if (i < inUse) { -		refArray[i] = refArray[inUse]; -	    } -	    if (mustFree) { -		if (freeProc == TCL_DYNAMIC) { -		    ckfree((char *) clientData); -		} else { -		    Tcl_MutexUnlock(&preserveMutex); -		    (*freeProc)((char *) clientData); -		    return; -		} -	    } + +	if (refPtr->refCount-- > 1) { +	    Tcl_MutexUnlock(&preserveMutex); +	    return;  	} + +	/* +	 * Must remove information from the slot before calling freeProc to +	 * avoid reentrancy problems if the freeProc calls Tcl_Preserve on the +	 * same clientData. Copy down the last reference in the array to +	 * overwrite the current slot. +	 */ + +	freeProc = refPtr->freeProc; +	mustFree = refPtr->mustFree; +	inUse--; +	if (i < inUse) { +	    refArray[i] = refArray[inUse]; +	} + +	/* +	 * Now committed to disposing the data. But first, we've patched up +	 * all the global data structures so we should release the mutex now. +	 * Only then should we dabble around with potentially-slow memory +	 * managers... +	 */ +  	Tcl_MutexUnlock(&preserveMutex); +	if (mustFree) { +	    if (freeProc == TCL_DYNAMIC) { +		ckfree(clientData); +	    } else { +		freeProc(clientData); +	    } +	}  	return;      }      Tcl_MutexUnlock(&preserveMutex);      /* -     * Reference not found.  This is a bug in the caller. +     * Reference not found. This is a bug in the caller.       */ -    panic("Tcl_Release couldn't find reference for 0x%x", clientData); +    Tcl_Panic("Tcl_Release couldn't find reference for %p", clientData);  }  /* @@ -259,10 +245,9 @@ Tcl_Release(clientData)   *   * Tcl_EventuallyFree --   * - *	Free up a block of memory, unless a call to Tcl_Preserve is in - *	effect for that block.  In this case, defer the free until all - *	calls to Tcl_Preserve have been undone by matching calls to - *	Tcl_Release. + *	Free up a block of memory, unless a call to Tcl_Preserve is in effect + *	for that block. In this case, defer the free until all calls to + *	Tcl_Preserve have been undone by matching calls to Tcl_Release.   *   * Results:   *	None. @@ -274,16 +259,16 @@ Tcl_Release(clientData)   */  void -Tcl_EventuallyFree(clientData, freeProc) -    ClientData clientData;	/* Pointer to malloc'ed block of memory. */ -    Tcl_FreeProc *freeProc;	/* Procedure to actually do free. */ +Tcl_EventuallyFree( +    ClientData clientData,	/* Pointer to malloc'ed block of memory. */ +    Tcl_FreeProc *freeProc)	/* Function to actually do free. */  {      Reference *refPtr;      int i;      /* -     * See if there is a reference for this pointer.  If so, set its -     * "mustFree" flag (the flag had better not be set already!). +     * See if there is a reference for this pointer. If so, set its "mustFree" +     * flag (the flag had better not be set already!).       */      Tcl_MutexLock(&preserveMutex); @@ -292,12 +277,12 @@ Tcl_EventuallyFree(clientData, freeProc)  	    continue;  	}  	if (refPtr->mustFree) { -	    panic("Tcl_EventuallyFree called twice for 0x%x\n", clientData); -        } -        refPtr->mustFree = 1; +	    Tcl_Panic("Tcl_EventuallyFree called twice for %p", clientData); +	} +	refPtr->mustFree = 1;  	refPtr->freeProc = freeProc;  	Tcl_MutexUnlock(&preserveMutex); -        return; +	return;      }      Tcl_MutexUnlock(&preserveMutex); @@ -306,9 +291,9 @@ Tcl_EventuallyFree(clientData, freeProc)       */      if (freeProc == TCL_DYNAMIC) { -	ckfree((char *) clientData); +	ckfree(clientData);      } else { -	(*freeProc)((char *)clientData); +	freeProc(clientData);      }  } @@ -317,36 +302,33 @@ Tcl_EventuallyFree(clientData, freeProc)   *   * TclHandleCreate --   * - *	Allocate a handle that contains enough information to determine - *	if an arbitrary malloc'd block has been deleted.  This is  - *	used to avoid the more time-expensive algorithm of Tcl_Preserve(). + *	Allocate a handle that contains enough information to determine if an + *	arbitrary malloc'd block has been deleted. This is used to avoid the + *	more time-expensive algorithm of Tcl_Preserve().   *   * Results:   *	The return value is a TclHandle that refers to the given malloc'd - *	block.  Doubly dereferencing the returned handle will give - *	back the pointer to the block, or will give NULL if the block has - *	been deleted. + *	block. Doubly dereferencing the returned handle will give back the + *	pointer to the block, or will give NULL if the block has been deleted.   *   * Side effects: - *	The caller must keep track of this handle (generally by storing - *	it in a field in the malloc'd block) and call TclHandleFree() - *	on this handle when the block is deleted.  Everything else that - *	wishes to keep track of whether the malloc'd block has been deleted - *	should use calls to TclHandlePreserve() and TclHandleRelease() - *	on the associated handle. + *	The caller must keep track of this handle (generally by storing it in + *	a field in the malloc'd block) and call TclHandleFree() on this handle + *	when the block is deleted. Everything else that wishes to keep track + *	of whether the malloc'd block has been deleted should use calls to + *	TclHandlePreserve() and TclHandleRelease() on the associated handle.   *   *---------------------------------------------------------------------------   */  TclHandle -TclHandleCreate(ptr) -    VOID *ptr;			/* Pointer to an arbitrary block of memory -				 * to be tracked for deletion.  Must not be +TclHandleCreate( +    void *ptr)			/* Pointer to an arbitrary block of memory to +				 * be tracked for deletion. Must not be  				 * NULL. */  { -    HandleStruct *handlePtr; +    HandleStruct *handlePtr = ckalloc(sizeof(HandleStruct)); -    handlePtr = (HandleStruct *) ckalloc(sizeof(HandleStruct));      handlePtr->ptr = ptr;  #ifdef TCL_MEM_DEBUG      handlePtr->ptr2 = ptr; @@ -360,11 +342,10 @@ TclHandleCreate(ptr)   *   * TclHandleFree --   * - *	Called when the arbitrary malloc'd block associated with the - *	handle is being deleted.  Modifies the handle so that doubly - *	dereferencing it will give NULL.  This informs any user of the - *	handle that the block of memory formerly referenced by the - *	handle has been freed. + *	Called when the arbitrary malloc'd block associated with the handle is + *	being deleted. Modifies the handle so that doubly dereferencing it + *	will give NULL. This informs any user of the handle that the block of + *	memory formerly referenced by the handle has been freed.   *   * Results:   *	None. @@ -376,27 +357,27 @@ TclHandleCreate(ptr)   */  void -TclHandleFree(handle) -    TclHandle handle;		/* Previously created handle associated -				 * with a malloc'd block that is being -				 * deleted.  The handle is modified so that -				 * doubly dereferencing it will give NULL. */ +TclHandleFree( +    TclHandle handle)		/* Previously created handle associated with a +				 * malloc'd block that is being deleted. The +				 * handle is modified so that doubly +				 * dereferencing it will give NULL. */  {      HandleStruct *handlePtr;      handlePtr = (HandleStruct *) handle;  #ifdef TCL_MEM_DEBUG      if (handlePtr->refCount == 0x61616161) { -	panic("using previously disposed TclHandle %x", handlePtr); +	Tcl_Panic("using previously disposed TclHandle %p", handlePtr);      }      if (handlePtr->ptr2 != handlePtr->ptr) { -	panic("someone has changed the block referenced by the handle %x\nfrom %x to %x", +	Tcl_Panic("someone has changed the block referenced by the handle %p\nfrom %p to %p",  		handlePtr, handlePtr->ptr2, handlePtr->ptr);      }  #endif      handlePtr->ptr = NULL;      if (handlePtr->refCount == 0) { -	ckfree((char *) handlePtr); +	ckfree(handlePtr);      }  } @@ -405,36 +386,35 @@ TclHandleFree(handle)   *   * TclHandlePreserve --   * - *	Declare an interest in the arbitrary malloc'd block associated - *	with the handle.   + *	Declare an interest in the arbitrary malloc'd block associated with + *	the handle.   *   * Results:   *	The return value is the handle argument, with its ref count   *	incremented.   *   * Side effects: - *	For each call to TclHandlePreserve(), there should be a matching - *	call to TclHandleRelease() when the caller is no longer interested - *	in the malloc'd block associated with the handle. + *	For each call to TclHandlePreserve(), there should be a matching call + *	to TclHandleRelease() when the caller is no longer interested in the + *	malloc'd block associated with the handle.   *   *---------------------------------------------------------------------------   */  TclHandle -TclHandlePreserve(handle) -    TclHandle handle;		/* Declare an interest in the block of -				 * memory referenced by this handle. */ +TclHandlePreserve( +    TclHandle handle)		/* Declare an interest in the block of memory +				 * referenced by this handle. */  {      HandleStruct *handlePtr;      handlePtr = (HandleStruct *) handle;  #ifdef TCL_MEM_DEBUG      if (handlePtr->refCount == 0x61616161) { -	panic("using previously disposed TclHandle %x", handlePtr); +	Tcl_Panic("using previously disposed TclHandle %p", handlePtr);      } -    if ((handlePtr->ptr != NULL) -	    && (handlePtr->ptr != handlePtr->ptr2)) { -	panic("someone has changed the block referenced by the handle %x\nfrom %x to %x", +    if ((handlePtr->ptr != NULL) && (handlePtr->ptr != handlePtr->ptr2)) { +	Tcl_Panic("someone has changed the block referenced by the handle %p\nfrom %p to %p",  		handlePtr, handlePtr->ptr2, handlePtr->ptr);      }  #endif @@ -448,41 +428,46 @@ TclHandlePreserve(handle)   *   * TclHandleRelease --   * - *	This procedure is called to release an interest in the malloc'd - *	block associated with the handle. + *	This function is called to release an interest in the malloc'd block + *	associated with the handle.   *   * Results:   *	None.   *   * Side effects: - *	The ref count of the handle is decremented.  If the malloc'd block - *	has been freed and if no one is using the handle any more, the - *	handle will be reclaimed. + *	The ref count of the handle is decremented. If the malloc'd block has + *	been freed and if no one is using the handle any more, the handle will + *	be reclaimed.   *   *---------------------------------------------------------------------------   */ -  +  void -TclHandleRelease(handle) -    TclHandle handle;		/* Unregister interest in the block of -				 * memory referenced by this handle. */ +TclHandleRelease( +    TclHandle handle)		/* Unregister interest in the block of memory +				 * referenced by this handle. */  {      HandleStruct *handlePtr;      handlePtr = (HandleStruct *) handle;  #ifdef TCL_MEM_DEBUG      if (handlePtr->refCount == 0x61616161) { -	panic("using previously disposed TclHandle %x", handlePtr); +	Tcl_Panic("using previously disposed TclHandle %p", handlePtr);      } -    if ((handlePtr->ptr != NULL) -	    && (handlePtr->ptr != handlePtr->ptr2)) { -	panic("someone has changed the block referenced by the handle %x\nfrom %x to %x", +    if ((handlePtr->ptr != NULL) && (handlePtr->ptr != handlePtr->ptr2)) { +	Tcl_Panic("someone has changed the block referenced by the handle %p\nfrom %p to %p",  		handlePtr, handlePtr->ptr2, handlePtr->ptr);      }  #endif -    handlePtr->refCount--; -    if ((handlePtr->refCount == 0) && (handlePtr->ptr == NULL)) { -	ckfree((char *) handlePtr); +    if ((handlePtr->refCount-- <= 1) && (handlePtr->ptr == NULL)) { +	ckfree(handlePtr);      }  } -     + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ | 
