]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * tkPreserve.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 1991 Regents of the University of California | |
10 | * Permission to use, copy, modify, and distribute this | |
11 | * software and its documentation for any purpose and without | |
12 | * fee is hereby granted, provided that this copyright | |
13 | * notice appears in all copies. The University of California | |
14 | * makes no representations about the suitability of this | |
15 | * software for any purpose. It is provided "as is" without | |
16 | * express or implied warranty. | |
17 | */ | |
18 | ||
19 | #ifndef lint | |
20 | static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkPreserve.c,v 1.6 92/06/15 13:44:51 ouster Exp $ SPRITE (Berkeley)"; | |
21 | #endif /* not lint */ | |
22 | ||
23 | #include "tkconfig.h" | |
24 | #include "tk.h" | |
25 | ||
26 | /* | |
27 | * The following data structure is used to keep track of all the | |
28 | * Tk_Preserve calls that are still in effect. It grows as needed | |
29 | * to accommodate any number of calls in effect. | |
30 | */ | |
31 | ||
32 | typedef struct { | |
33 | ClientData clientData; /* Address of preserved block. */ | |
34 | int refCount; /* Number of Tk_Preserve calls in effect | |
35 | * for block. */ | |
36 | int mustFree; /* Non-zero means Tk_EventuallyFree was | |
37 | * called while a Tk_Preserve call was in | |
38 | * effect, so the structure must be freed | |
39 | * when refCount becomes zero. */ | |
40 | Tk_FreeProc *freeProc; /* Procedure to call to free. */ | |
41 | } Reference; | |
42 | ||
43 | static Reference *refArray; /* First in array of references. */ | |
44 | static int spaceAvl = 0; /* Total number of structures available | |
45 | * at *firstRefPtr. */ | |
46 | static int inUse = 0; /* Count of structures currently in use | |
47 | * in refArray. */ | |
48 | #define INITIAL_SIZE 2 | |
49 | \f | |
50 | /* | |
51 | *---------------------------------------------------------------------- | |
52 | * | |
53 | * Tk_Preserve -- | |
54 | * | |
55 | * This procedure is used by a procedure to declare its interest | |
56 | * in a particular block of memory, so that the block will not be | |
57 | * reallocated until a matching call to Tk_Release has been made. | |
58 | * | |
59 | * Results: | |
60 | * None. | |
61 | * | |
62 | * Side effects: | |
63 | * Information is retained so that the block of memory will | |
64 | * not be freed until at least the matching call to Tk_Release. | |
65 | * | |
66 | *---------------------------------------------------------------------- | |
67 | */ | |
68 | ||
69 | void | |
70 | Tk_Preserve(clientData) | |
71 | ClientData clientData; /* Pointer to malloc'ed block of memory. */ | |
72 | { | |
73 | register Reference *refPtr; | |
74 | int i; | |
75 | ||
76 | /* | |
77 | * See if there is already a reference for this pointer. If so, | |
78 | * just increment its reference count. | |
79 | */ | |
80 | ||
81 | for (i = 0, refPtr = refArray; i < inUse; i++, refPtr++) { | |
82 | if (refPtr->clientData == clientData) { | |
83 | refPtr->refCount++; | |
84 | return; | |
85 | } | |
86 | } | |
87 | ||
88 | /* | |
89 | * Make a reference array if it doesn't already exist, or make it | |
90 | * bigger if it is full. | |
91 | */ | |
92 | ||
93 | if (inUse == spaceAvl) { | |
94 | if (spaceAvl == 0) { | |
95 | refArray = (Reference *) ckalloc((unsigned) | |
96 | (INITIAL_SIZE*sizeof(Reference))); | |
97 | spaceAvl = INITIAL_SIZE; | |
98 | } else { | |
99 | Reference *new; | |
100 | ||
101 | new = (Reference *) ckalloc((unsigned) | |
102 | (2*spaceAvl*sizeof(Reference))); | |
103 | memcpy((VOID *) new, (VOID *) refArray, spaceAvl*sizeof(Reference)); | |
104 | ckfree((char *) refArray); | |
105 | refArray = new; | |
106 | spaceAvl *= 2; | |
107 | } | |
108 | } | |
109 | ||
110 | /* | |
111 | * Make a new entry for the new reference. | |
112 | */ | |
113 | ||
114 | refPtr = &refArray[inUse]; | |
115 | refPtr->clientData = clientData; | |
116 | refPtr->refCount = 1; | |
117 | refPtr->mustFree = 0; | |
118 | inUse += 1; | |
119 | } | |
120 | \f | |
121 | /* | |
122 | *---------------------------------------------------------------------- | |
123 | * | |
124 | * Tk_Release -- | |
125 | * | |
126 | * This procedure is called to cancel a previous call to | |
127 | * Tk_Preserve, thereby allowing a block of memory to be | |
128 | * freed (if no one else cares about it). | |
129 | * | |
130 | * Results: | |
131 | * None. | |
132 | * | |
133 | * Side effects: | |
134 | * If Tk_EventuallyFree has been called for clientData, and if | |
135 | * no other call to Tk_Preserve is still in effect, the block of | |
136 | * memory is freed. | |
137 | * | |
138 | *---------------------------------------------------------------------- | |
139 | */ | |
140 | ||
141 | void | |
142 | Tk_Release(clientData) | |
143 | ClientData clientData; /* Pointer to malloc'ed block of memory. */ | |
144 | { | |
145 | register Reference *refPtr; | |
146 | int i; | |
147 | ||
148 | for (i = 0, refPtr = refArray; i < inUse; i++, refPtr++) { | |
149 | if (refPtr->clientData != clientData) { | |
150 | continue; | |
151 | } | |
152 | refPtr->refCount--; | |
153 | if (refPtr->refCount == 0) { | |
154 | if (refPtr->mustFree) { | |
155 | if (refPtr->freeProc == (Tk_FreeProc *) free) { | |
156 | ckfree((char *) refPtr->clientData); | |
157 | } else { | |
158 | (*refPtr->freeProc)(refPtr->clientData); | |
159 | } | |
160 | } | |
161 | ||
162 | /* | |
163 | * Copy down all of the trailing reference in the array | |
164 | * to cover up the hole left by the unused reference. | |
165 | */ | |
166 | ||
167 | inUse--; | |
168 | if (i != inUse) { | |
169 | memcpy((VOID *) &refArray[i], (VOID *) &refArray[i+1], | |
170 | (inUse-i)*sizeof(Reference)); | |
171 | } | |
172 | } | |
173 | return; | |
174 | } | |
175 | ||
176 | /* | |
177 | * Reference not found. This is a bug in the caller. | |
178 | */ | |
179 | ||
180 | panic("Tk_Release couldn't find reference for 0x%x", clientData); | |
181 | } | |
182 | \f | |
183 | /* | |
184 | *---------------------------------------------------------------------- | |
185 | * | |
186 | * Tk_EventuallyFree -- | |
187 | * | |
188 | * Free up a block of memory, unless a call to Tk_Preserve is in | |
189 | * effect for that block. In this case, defer the free until all | |
190 | * calls to Tk_Preserve have been undone by matching calls to | |
191 | * Tk_Release. | |
192 | * | |
193 | * Results: | |
194 | * None. | |
195 | * | |
196 | * Side effects: | |
197 | * Ptr may be released by calling free(). | |
198 | * | |
199 | *---------------------------------------------------------------------- | |
200 | */ | |
201 | ||
202 | void | |
203 | Tk_EventuallyFree(clientData, freeProc) | |
204 | ClientData clientData; /* Pointer to malloc'ed block of memory. */ | |
205 | Tk_FreeProc *freeProc; /* Procedure to actually do free. */ | |
206 | { | |
207 | register Reference *refPtr; | |
208 | int i; | |
209 | ||
210 | /* | |
211 | * See if there is a reference for this pointer. If so, set its | |
212 | * "mustFree" flag (the flag had better not be set already!). | |
213 | */ | |
214 | ||
215 | for (i = 0, refPtr = refArray; i < inUse; i++, refPtr++) { | |
216 | if (refPtr->clientData != clientData) { | |
217 | continue; | |
218 | } | |
219 | if (refPtr->mustFree) { | |
220 | panic("Tk_EventuallyFree called twice for 0x%x\n", clientData); | |
221 | } | |
222 | refPtr->mustFree = 1; | |
223 | refPtr->freeProc = freeProc; | |
224 | return; | |
225 | } | |
226 | ||
227 | /* | |
228 | * No reference for this block. Free it now. | |
229 | */ | |
230 | ||
231 | if (freeProc == (Tk_FreeProc *) free) { | |
232 | ckfree((char *) clientData); | |
233 | } else { | |
234 | (*freeProc)(clientData); | |
235 | } | |
236 | } |