Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/tcl8.5.2/generic/tclPreserve.c @ 25

Last change on this file since 25 was 25, checked in by landauf, 16 years ago

added tcl to libs

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