2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these librararies and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
23 /* $XConsortium: tclPreserve.c /main/2 1996/08/08 14:46:12 cde-hp $ */
27 * This file contains a collection of procedures that are used
28 * to make sure that widget records and other data structures
29 * aren't reallocated when there are nested procedures that
30 * depend on their existence.
32 * Copyright (c) 1991-1994 The Regents of the University of California.
33 * Copyright (c) 1994-1995 Sun Microsystems, Inc.
35 * See the file "license.terms" for information on usage and redistribution
36 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
38 * SCCS: @(#) tclPreserve.c 1.14 96/03/20 08:24:37
44 * The following data structure is used to keep track of all the
45 * Tcl_Preserve calls that are still in effect. It grows as needed
46 * to accommodate any number of calls in effect.
50 ClientData clientData; /* Address of preserved block. */
51 int refCount; /* Number of Tcl_Preserve calls in effect
53 int mustFree; /* Non-zero means Tcl_EventuallyFree was
54 * called while a Tcl_Preserve call was in
55 * effect, so the structure must be freed
56 * when refCount becomes zero. */
57 Tcl_FreeProc *freeProc; /* Procedure to call to free. */
60 static Reference *refArray; /* First in array of references. */
61 static int spaceAvl = 0; /* Total number of structures available
63 static int inUse = 0; /* Count of structures currently in use
65 #define INITIAL_SIZE 2
68 * Static routines in this file:
71 static void PreserveExitProc _ANSI_ARGS_((ClientData clientData));
75 *----------------------------------------------------------------------
79 * Called during exit processing to clean up the reference array.
85 * Frees the storage of the reference array.
87 *----------------------------------------------------------------------
92 PreserveExitProc(clientData)
93 ClientData clientData; /* NULL -Unused. */
96 ckfree((char *) refArray);
97 refArray = (Reference *) NULL;
104 *----------------------------------------------------------------------
108 * This procedure is used by a procedure to declare its interest
109 * in a particular block of memory, so that the block will not be
110 * reallocated until a matching call to Tcl_Release has been made.
116 * Information is retained so that the block of memory will
117 * not be freed until at least the matching call to Tcl_Release.
119 *----------------------------------------------------------------------
123 Tcl_Preserve(clientData)
124 ClientData clientData; /* Pointer to malloc'ed block of memory. */
130 * See if there is already a reference for this pointer. If so,
131 * just increment its reference count.
134 for (i = 0, refPtr = refArray; i < inUse; i++, refPtr++) {
135 if (refPtr->clientData == clientData) {
142 * Make a reference array if it doesn't already exist, or make it
143 * bigger if it is full.
146 if (inUse == spaceAvl) {
148 Tcl_CreateExitHandler((Tcl_ExitProc *) PreserveExitProc,
150 refArray = (Reference *) ckalloc((unsigned)
151 (INITIAL_SIZE*sizeof(Reference)));
152 spaceAvl = INITIAL_SIZE;
156 new = (Reference *) ckalloc((unsigned)
157 (2*spaceAvl*sizeof(Reference)));
158 memcpy((VOID *) new, (VOID *) refArray,
159 spaceAvl*sizeof(Reference));
160 ckfree((char *) refArray);
167 * Make a new entry for the new reference.
170 refPtr = &refArray[inUse];
171 refPtr->clientData = clientData;
172 refPtr->refCount = 1;
173 refPtr->mustFree = 0;
178 *----------------------------------------------------------------------
182 * This procedure is called to cancel a previous call to
183 * Tcl_Preserve, thereby allowing a block of memory to be
184 * freed (if no one else cares about it).
190 * If Tcl_EventuallyFree has been called for clientData, and if
191 * no other call to Tcl_Preserve is still in effect, the block of
194 *----------------------------------------------------------------------
198 Tcl_Release(clientData)
199 ClientData clientData; /* Pointer to malloc'ed block of memory. */
203 Tcl_FreeProc *freeProc;
206 for (i = 0, refPtr = refArray; i < inUse; i++, refPtr++) {
207 if (refPtr->clientData != clientData) {
211 if (refPtr->refCount == 0) {
214 * Must remove information from the slot before calling freeProc
215 * to avoid reentrancy problems if the freeProc calls Tcl_Preserve
216 * on the same clientData. Copy down the last reference in the
217 * array to overwrite the current slot.
220 freeProc = refPtr->freeProc;
221 mustFree = refPtr->mustFree;
224 refArray[i] = refArray[inUse];
227 if ((freeProc == TCL_DYNAMIC) ||
228 (freeProc == (Tcl_FreeProc *) free)) {
229 ckfree((char *) clientData);
231 (*freeProc)((char *) clientData);
239 * Reference not found. This is a bug in the caller.
242 panic("Tcl_Release couldn't find reference for 0x%x", clientData);
246 *----------------------------------------------------------------------
248 * Tcl_EventuallyFree --
250 * Free up a block of memory, unless a call to Tcl_Preserve is in
251 * effect for that block. In this case, defer the free until all
252 * calls to Tcl_Preserve have been undone by matching calls to
259 * Ptr may be released by calling free().
261 *----------------------------------------------------------------------
265 Tcl_EventuallyFree(clientData, freeProc)
266 ClientData clientData; /* Pointer to malloc'ed block of memory. */
267 Tcl_FreeProc *freeProc; /* Procedure to actually do free. */
273 * See if there is a reference for this pointer. If so, set its
274 * "mustFree" flag (the flag had better not be set already!).
277 for (i = 0, refPtr = refArray; i < inUse; i++, refPtr++) {
278 if (refPtr->clientData != clientData) {
281 if (refPtr->mustFree) {
282 panic("Tcl_EventuallyFree called twice for 0x%x\n", clientData);
284 refPtr->mustFree = 1;
285 refPtr->freeProc = freeProc;
290 * No reference for this block. Free it now.
293 if ((freeProc == TCL_DYNAMIC) || (freeProc == (Tcl_FreeProc *) free)) {
294 ckfree((char *) clientData);
296 (*freeProc)((char *)clientData);