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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 64 - (show annotations) (download)
Sun Oct 30 04:21:11 2016 UTC (7 years, 11 months ago) by dashley
File MIME type: text/plain
File size: 14334 byte(s)
Adjust line endings to Windows style.
Set properties to expand the "Header" keyword.
Change header and footer.
1 /*$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:keywords Header

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25