/[dtapublic]/projs/trunk/shared_source/c_tcl_base_7_5_w_mods/tclpreserve.c
ViewVC logotype

Annotation of /projs/trunk/shared_source/c_tcl_base_7_5_w_mods/tclpreserve.c

Parent Directory Parent Directory | Revision Log Revision Log


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

Properties

Name Value
svn:eol-style native
svn:keywords Header

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25