home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / tkisrc04.zip / tcl / os2 / tclPreserve.c < prev    next >
C/C++ Source or Header  |  1998-08-07  |  7KB  |  276 lines

  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-1995 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.  * SCCS: @(#) tclPreserve.c 1.14 96/03/20 08:24:37
  16.  */
  17.  
  18. #include "tclInt.h"
  19.  
  20. /*
  21.  * The following data structure is used to keep track of all the
  22.  * Tcl_Preserve calls that are still in effect.  It grows as needed
  23.  * to accommodate any number of calls in effect.
  24.  */
  25.  
  26. typedef struct {
  27.     ClientData clientData;    /* Address of preserved block. */
  28.     int refCount;        /* Number of Tcl_Preserve calls in effect
  29.                  * for block. */
  30.     int mustFree;        /* Non-zero means Tcl_EventuallyFree was
  31.                  * called while a Tcl_Preserve call was in
  32.                  * effect, so the structure must be freed
  33.                  * when refCount becomes zero. */
  34.     Tcl_FreeProc *freeProc;    /* Procedure to call to free. */
  35. } Reference;
  36.  
  37. static Reference *refArray;    /* First in array of references. */
  38. static int spaceAvl = 0;    /* Total number of structures available
  39.                  * at *firstRefPtr. */
  40. static int inUse = 0;        /* Count of structures currently in use
  41.                  * in refArray. */
  42. #define INITIAL_SIZE 2
  43.  
  44. /*
  45.  * Static routines in this file:
  46.  */
  47.  
  48. static void    PreserveExitProc _ANSI_ARGS_((ClientData clientData));
  49.  
  50.  
  51. /*
  52.  *----------------------------------------------------------------------
  53.  *
  54.  * PreserveExitProc --
  55.  *
  56.  *    Called during exit processing to clean up the reference array.
  57.  *
  58.  * Results:
  59.  *    None.
  60.  *
  61.  * Side effects:
  62.  *    Frees the storage of the reference array.
  63.  *
  64.  *----------------------------------------------------------------------
  65.  */
  66.  
  67.     /* ARGSUSED */
  68. static void
  69. PreserveExitProc(clientData)
  70.     ClientData clientData;        /* NULL -Unused. */
  71. {
  72.     if (spaceAvl != 0) {
  73.         ckfree((char *) refArray);
  74.         refArray = (Reference *) NULL;
  75.         inUse = 0;
  76.         spaceAvl = 0;
  77.     }
  78. }
  79.  
  80. /*
  81.  *----------------------------------------------------------------------
  82.  *
  83.  * Tcl_Preserve --
  84.  *
  85.  *    This procedure is used by a procedure to declare its interest
  86.  *    in a particular block of memory, so that the block will not be
  87.  *    reallocated until a matching call to Tcl_Release has been made.
  88.  *
  89.  * Results:
  90.  *    None.
  91.  *
  92.  * Side effects:
  93.  *    Information is retained so that the block of memory will
  94.  *    not be freed until at least the matching call to Tcl_Release.
  95.  *
  96.  *----------------------------------------------------------------------
  97.  */
  98.  
  99. void
  100. Tcl_Preserve(clientData)
  101.     ClientData clientData;    /* Pointer to malloc'ed block of memory. */
  102. {
  103.     Reference *refPtr;
  104.     int i;
  105.  
  106.     /*
  107.      * See if there is already a reference for this pointer.  If so,
  108.      * just increment its reference count.
  109.      */
  110.  
  111.     for (i = 0, refPtr = refArray; i < inUse; i++, refPtr++) {
  112.     if (refPtr->clientData == clientData) {
  113.         refPtr->refCount++;
  114.         return;
  115.     }
  116.     }
  117.  
  118.     /*
  119.      * Make a reference array if it doesn't already exist, or make it
  120.      * bigger if it is full.
  121.      */
  122.  
  123.     if (inUse == spaceAvl) {
  124.     if (spaceAvl == 0) {
  125.             Tcl_CreateExitHandler((Tcl_ExitProc *) PreserveExitProc,
  126.                     (ClientData) NULL);
  127.         refArray = (Reference *) ckalloc((unsigned)
  128.             (INITIAL_SIZE*sizeof(Reference)));
  129.         spaceAvl = INITIAL_SIZE;
  130.     } else {
  131.         Reference *new;
  132.  
  133.         new = (Reference *) ckalloc((unsigned)
  134.             (2*spaceAvl*sizeof(Reference)));
  135.         memcpy((VOID *) new, (VOID *) refArray,
  136.                     spaceAvl*sizeof(Reference));
  137.         ckfree((char *) refArray);
  138.         refArray = new;
  139.         spaceAvl *= 2;
  140.     }
  141.     }
  142.  
  143.     /*
  144.      * Make a new entry for the new reference.
  145.      */
  146.  
  147.     refPtr = &refArray[inUse];
  148.     refPtr->clientData = clientData;
  149.     refPtr->refCount = 1;
  150.     refPtr->mustFree = 0;
  151.     inUse += 1;
  152. }
  153.  
  154. /*
  155.  *----------------------------------------------------------------------
  156.  *
  157.  * Tcl_Release --
  158.  *
  159.  *    This procedure is called to cancel a previous call to
  160.  *    Tcl_Preserve, thereby allowing a block of memory to be
  161.  *    freed (if no one else cares about it).
  162.  *
  163.  * Results:
  164.  *    None.
  165.  *
  166.  * Side effects:
  167.  *    If Tcl_EventuallyFree has been called for clientData, and if
  168.  *    no other call to Tcl_Preserve is still in effect, the block of
  169.  *    memory is freed.
  170.  *
  171.  *----------------------------------------------------------------------
  172.  */
  173.  
  174. void
  175. Tcl_Release(clientData)
  176.     ClientData clientData;    /* Pointer to malloc'ed block of memory. */
  177. {
  178.     Reference *refPtr;
  179.     int mustFree;
  180.     Tcl_FreeProc *freeProc;
  181.     int i;
  182.  
  183.     for (i = 0, refPtr = refArray; i < inUse; i++, refPtr++) {
  184.     if (refPtr->clientData != clientData) {
  185.         continue;
  186.     }
  187.     refPtr->refCount--;
  188.     if (refPtr->refCount == 0) {
  189.  
  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.  
  197.             freeProc = refPtr->freeProc;
  198.             mustFree = refPtr->mustFree;
  199.         inUse--;
  200.         if (i < inUse) {
  201.         refArray[i] = refArray[inUse];
  202.         }
  203.         if (mustFree) {
  204.         if ((freeProc == TCL_DYNAMIC) ||
  205.                         (freeProc == (Tcl_FreeProc *) free)) {
  206.             ckfree((char *) clientData);
  207.         } else {
  208.             (*freeProc)((char *) clientData);
  209.         }
  210.         }
  211.     }
  212.     return;
  213.     }
  214.  
  215.     /*
  216.      * Reference not found.  This is a bug in the caller.
  217.      */
  218.  
  219.     panic("Tcl_Release couldn't find reference for 0x%x", clientData);
  220. }
  221.  
  222. /*
  223.  *----------------------------------------------------------------------
  224.  *
  225.  * Tcl_EventuallyFree --
  226.  *
  227.  *    Free up a block of memory, unless a call to Tcl_Preserve is in
  228.  *    effect for that block.  In this case, defer the free until all
  229.  *    calls to Tcl_Preserve have been undone by matching calls to
  230.  *    Tcl_Release.
  231.  *
  232.  * Results:
  233.  *    None.
  234.  *
  235.  * Side effects:
  236.  *    Ptr may be released by calling free().
  237.  *
  238.  *----------------------------------------------------------------------
  239.  */
  240.  
  241. void
  242. Tcl_EventuallyFree(clientData, freeProc)
  243.     ClientData clientData;    /* Pointer to malloc'ed block of memory. */
  244.     Tcl_FreeProc *freeProc;    /* Procedure to actually do free. */
  245. {
  246.     Reference *refPtr;
  247.     int i;
  248.  
  249.     /*
  250.      * See if there is a reference for this pointer.  If so, set its
  251.      * "mustFree" flag (the flag had better not be set already!).
  252.      */
  253.  
  254.     for (i = 0, refPtr = refArray; i < inUse; i++, refPtr++) {
  255.     if (refPtr->clientData != clientData) {
  256.         continue;
  257.     }
  258.     if (refPtr->mustFree) {
  259.         panic("Tcl_EventuallyFree called twice for 0x%x\n", clientData);
  260.         }
  261.         refPtr->mustFree = 1;
  262.     refPtr->freeProc = freeProc;
  263.         return;
  264.     }
  265.  
  266.     /*
  267.      * No reference for this block.  Free it now.
  268.      */
  269.  
  270.     if ((freeProc == TCL_DYNAMIC) || (freeProc == (Tcl_FreeProc *) free)) {
  271.     ckfree((char *) clientData);
  272.     } else {
  273.     (*freeProc)((char *)clientData);
  274.     }
  275. }
  276.