tclPreserve.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:13k
源码类别:

通讯编程

开发平台:

Visual C++

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