1 /* Copyright (C) 1992-1998 The Geometry Center
2 * Copyright (C) 1998-2000 Stuart Levy, Tamara Munzner, Mark Phillips
3 *
4 * This file is part of Geomview.
5 *
6 * Geomview is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published
8 * by the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * Geomview is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with Geomview; see the file COPYING. If not, write
18 * to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
19 * USA, or visit http://www.gnu.org.
20 */
21
22 #if HAVE_CONFIG_H
23 # include "config.h"
24 #endif
25
26 #if 0
27 static char copyright[] = "Copyright (C) 1992-1998 The Geometry Center\n\
28 Copyright (C) 1998-2000 Stuart Levy, Tamara Munzner, Mark Phillips";
29 #endif
30
31
32 /* Authors: Charlie Gunn, Stuart Levy, Tamara Munzner, Mark Phillips */
33
34 #include <string.h>
35 #include "handleP.h"
36 #include "freelist.h"
37
38 static DBLLIST(AllHandles);
39 static DEF_FREELIST(HRef);
40 static DEF_FREELIST(Handle);
41
HRefFreeListPrune(void)42 void HRefFreeListPrune(void)
43 {
44 FreeListNode *old;
45 size_t size = 0;
46
47 while (HRefFreeList) {
48 old = HRefFreeList;
49 HRefFreeList = old->next;
50 OOGLFree(old);
51 size += sizeof(HRef);
52 }
53 OOGLWarn("Freed %ld bytes.\n", size);
54 }
55
HandleFreeListPrune(void)56 void HandleFreeListPrune(void)
57 {
58 FreeListNode *old;
59 size_t size = 0;
60
61 while (HandleFreeList) {
62 old = HandleFreeList;
63 HandleFreeList = old->next;
64 OOGLFree(old);
65 size += sizeof(Handle);
66 }
67 OOGLWarn("Freed %ld bytes.\n", size);
68 }
69
70 #define HANDLE_DEBUG 0
71
72 void handle_dump(void);
73
74 static HandleOps NullOps = {
75 "null",
76 };
77
HandlesSetObjSaved(bool obj_saved)78 void HandlesSetObjSaved(bool obj_saved)
79 {
80 HandleOps *ops;
81 Handle *h;
82
83 DblListIterateNoDelete(&AllHandles, HandleOps, node, ops) {
84 DblListIterateNoDelete(&ops->handles, Handle, opsnode, h) {
85 h->obj_saved = obj_saved;
86 }
87 }
88 }
89
90 /* Iterate over Ref->handles.
91 *
92 * This function increases the ref-count of the handle, only this way
93 * the caller may safely delete the returned handle; if pos != NULL,
94 * then we first iterate to the next element in the list, and then
95 * call HandleDelete() for pos to undo the REFGET from the previous
96 * call. The caller should always iterate until we return NULL,
97 * otherwise spurious ref-counts will be the result.
98 */
HandleRefIterate(Ref * r,Handle * pos)99 Handle *HandleRefIterate(Ref *r, Handle *pos)
100 {
101 if (pos == NULL) {
102 return DblListEmpty(&r->handles)
103 ? NULL
104 : REFGET(Handle, DblListContainer(r->handles.next, Handle, objnode));
105 } else {
106 DblListNode *next = pos->objnode.next;
107
108 HandleDelete(pos); /* undo REFGET(), possibly really delete the handle */
109 return next == &r->handles
110 ? NULL
111 : REFGET(Handle, DblListContainer(next, Handle, objnode));
112 }
113 }
114
115 /* iterate over Pool->handles */
HandlePoolIterate(Pool * pool,Handle * pos)116 Handle *HandlePoolIterate(Pool *pool, Handle *pos)
117 {
118 if (pos == NULL) {
119 return DblListEmpty(&pool->handles)
120 ? NULL
121 : REFGET(Handle, DblListContainer(pool->handles.next, Handle, poolnode));
122 } else {
123 DblListNode *next = pos->objnode.next;
124
125 HandleDelete(pos);
126 return next == &pool->handles
127 ? NULL
128 : REFGET(Handle, DblListContainer(next, Handle, poolnode));
129 }
130 }
131
132 void
HandleUpdRef(Handle ** hp,Ref * parent,Ref ** objp)133 HandleUpdRef(Handle **hp, Ref *parent, Ref **objp)
134 {
135 Handle *h;
136
137 (void)parent;
138
139 if((h = *hp) != NULL && objp != NULL && h->object != *objp) {
140 if (*objp) {
141 if (h->ops->delete) {
142 (*h->ops->delete)(*objp);
143 } else {
144 REFPUT(*objp);
145 }
146 }
147 *objp = REFGET(Ref, h->object);
148 }
149 }
150
HandleUnregister(Handle ** hp)151 void HandleUnregister(Handle **hp)
152 {
153 Handle *h;
154 HRef *r, *rn;
155
156 if (hp == NULL || (h = *hp) == NULL) {
157 return;
158 }
159
160 DblListIterate(&h->refs, HRef, node, r, rn) {
161 if(r->hp == hp) {
162 DblListDelete(&r->node);
163 memset(r, 0, sizeof(*r));
164 FREELIST_FREE(HRef, r);
165 REFPUT(h);
166 }
167 }
168 }
169
HandleUnregisterJust(Handle ** hp,Ref * obj,void * info,void (* update)P ((Handle **,Ref *,void *)))170 void HandleUnregisterJust(Handle **hp, Ref *obj, void *info,
171 void (*update) P((Handle **, Ref *, void *)))
172 {
173 Handle *h;
174 HRef *rp, *rnext;
175
176 if(hp == NULL || (h = *hp) == NULL) {
177 return;
178 }
179
180 DblListIterate(&h->refs, HRef, node, rp, rnext) {
181 if(rp->hp == hp &&
182 (obj == NULL || rp->parentobj == obj) &&
183 (info == NULL || rp->info == info) &&
184 (update == NULL || rp->update == update)) {
185 DblListDelete(&rp->node);
186 memset(rp, 0, sizeof(*rp));
187 FREELIST_FREE(HRef, rp);
188 REFPUT(h);
189 }
190 }
191 }
192
193 /*
194 * Remove all callbacks on any handle with the given properties.
195 */
HandleUnregisterAll(Ref * obj,void * info,void (* update)P ((Handle **,Ref *,void *)))196 void HandleUnregisterAll(Ref *obj,
197 void *info,
198 void (*update) P((Handle **, Ref *, void *)))
199 {
200 HandleOps *ops;
201 Handle *h;
202 HRef *r, *rn;
203
204 DblListIterateNoDelete(&AllHandles, HandleOps, node, ops) {
205 DblListIterateNoDelete(&ops->handles, Handle, opsnode, h) {
206 DblListIterate(&h->refs, HRef, node, r, rn) {
207 if((obj == NULL || r->parentobj == obj) &&
208 (info == NULL || r->info == info) &&
209 (update == NULL || r->update == update)) {
210 DblListDelete(&r->node);
211 memset(r, 0, sizeof(*r));
212 FREELIST_FREE(HRef, r);
213 REFPUT(h);
214 }
215 }
216 }
217 }
218 }
219
220 static
handleupdate(Handle * h,HRef * rp)221 void handleupdate(Handle *h, HRef *rp)
222 {
223 if(rp->update && h == *rp->hp) {
224 (*rp->update)(rp->hp, rp->parentobj, rp->info);
225 } else { /* BUG */
226 OOGLError(1, "handleupdate mismatch: h %x %s, rp->hp %x, *rp->hp %x, rp->parentobj %x, rp->update %x",
227 h, h->name, rp->hp, *rp->hp, rp->parentobj, rp->update);
228 if(*rp->hp) {
229 OOGLError(1, "... *rp->hp->name %s", (*rp->hp)->name);
230 }
231 }
232 }
233
HandleRegister(Handle ** hp,Ref * parentobj,void * info,void (* update)P ((Handle **,Ref *,void *)))234 int HandleRegister(Handle **hp,
235 Ref *parentobj,
236 void *info,
237 void (*update) P((Handle **, Ref *, void *)))
238 {
239 Handle *h;
240 HRef *rp;
241
242 if (hp == NULL || (h = *hp) == NULL) {
243 return false;
244 }
245
246 DblListIterateNoDelete(&h->refs, HRef, node, rp) {
247 if (rp->hp == hp && rp->parentobj == parentobj && rp->info == info) {
248 goto update_out;
249 }
250 }
251
252 FREELIST_NEW(HRef, rp);
253
254 REFINCR(h);
255
256 rp->hp = hp;
257 rp->parentobj = parentobj;
258 rp->info = info;
259
260 DblListAdd(&h->refs, &rp->node);
261
262 update_out:
263 rp->update = update;
264 handleupdate(h, rp);
265 return true;
266 }
267
268 char *
HandleName(Handle * h)269 HandleName(Handle *h)
270 {
271 return h ? h->name : NULL;
272 }
273
HandleOpsByName(char * name)274 HandleOps *HandleOpsByName(char *name)
275 {
276 HandleOps *ops;
277
278 DblListIterateNoDelete(&AllHandles, HandleOps, node, ops) {
279 if (strcmp(name, ops->prefix) == 0) {
280 return ops;
281 }
282 }
283 return NULL;
284 }
285
286 Handle *
HandleByName(char * name,HandleOps * ops)287 HandleByName(char *name, HandleOps *ops)
288 {
289 Handle *h;
290
291 if (ops == NULL) {
292 DblListIterateNoDelete(&AllHandles, HandleOps, node, ops) {
293 DblListIterateNoDelete(&ops->handles, Handle, opsnode, h) {
294 if (strcmp(h->name, name) == 0) {
295 return REFGET(Handle, h);
296 }
297 }
298 }
299 } else {
300 if (ops->handles.next == NULL) {
301 DblListInit(&ops->handles);
302 DblListAdd(&AllHandles, &ops->node);
303 }
304 DblListIterateNoDelete(&ops->handles, Handle, opsnode, h) {
305 if (strcmp(h->name, name) == 0) {
306 return REFGET(Handle, h);
307 }
308 }
309 }
310 return NULL;
311 }
312
313 static Handle *
handlecreate(char * name,HandleOps * ops)314 handlecreate(char *name, HandleOps *ops)
315 {
316 Handle *h;
317
318 #if HANDLE_DEBUG
319 OOGLWarn("Creating handle with name \"%s\"", name);
320 #endif
321
322 FREELIST_NEW(Handle, h);
323
324 RefInit((Ref *)h, HANDLEMAGIC);
325 h->ops = ops = ops ? ops : &NullOps;
326 h->name = strdup(name);
327 h->object = NULL;
328 h->whence = NULL;
329 h->permanent = false;
330
331 DblListInit(&h->refs);
332
333 /* The following two are nodes, not list-heads, but this way we can
334 * safely call DblListDelete(), even if there is no associated list.
335 */
336 DblListInit(&h->objnode);
337 DblListInit(&h->poolnode);
338
339 if (ops->handles.next == NULL) {
340 DblListInit(&ops->handles);
341 DblListAdd(&AllHandles, &ops->node);
342 }
343 DblListAddTail(&ops->handles, &h->opsnode);
344
345 /* handle_dump();*/
346
347 return h;
348 }
349
350 /*
351 * Assign a new object to a Handle.
352 */
353 int
HandleSetObject(Handle * h,Ref * object)354 HandleSetObject(Handle *h, Ref *object)
355 {
356 HRef *ref;
357
358 if(h == NULL) {
359 return false;
360 }
361
362 if(h->object == object) {
363 return true;
364 }
365
366 DblListDelete(&h->objnode);
367
368 if (h->object) {
369 if (h->ops->delete != NULL) {
370 (*h->ops->delete)(h->object);
371 } else {
372 REFPUT(h->object);
373 }
374 }
375 h->object = REFGET(Ref, object);
376 if (object) {
377 DblListAddTail(&object->handles, &h->objnode);
378 }
379
380 DblListIterateNoDelete(&h->refs, HRef, node, ref) {
381 handleupdate(h, ref);
382 }
383
384 return true;
385 }
386
387 /*
388 * Unconditionally create a new handle.
389 */
390 Handle *
HandleDoCreate(char * name,HandleOps * ops)391 HandleDoCreate(char *name, HandleOps *ops)
392 {
393 Handle *h;
394
395 h = handlecreate(name, ops);
396
397 return h;
398 }
399
400 /*
401 * Create a Handle, don't reassign its value.
402 */
403 Handle *
HandleCreate(char * name,HandleOps * ops)404 HandleCreate(char *name, HandleOps *ops)
405 {
406 Handle *h;
407
408 h = HandleByName(name, ops);
409 if (h == NULL) {
410 h = handlecreate(name, ops);
411 }
412 return h;
413 }
414
415 /*
416 * Global handle: increase the ref-count twice.
417 */
418 Handle *
HandleCreateGlobal(char * name,HandleOps * ops)419 HandleCreateGlobal(char *name, HandleOps *ops)
420 {
421 Handle *h;
422
423 h = HandleCreate(name, ops);
424 if (!h->permanent) {
425 h->permanent = true;
426 return REFGET(Handle, h);
427 } else {
428 return h;
429 }
430 }
431
432 /*
433 * Create a Handle (or reassign its value if it already exists)
434 */
435 Handle *
HandleAssign(char * name,HandleOps * ops,Ref * object)436 HandleAssign(char *name, HandleOps *ops, Ref *object)
437 {
438 Handle *h;
439
440 h = HandleCreate(name, ops);
441 HandleSetObject(h, object);
442 return h;
443 }
444
445 /*
446 * Return the current object held by a Handle.
447 */
448 Ref *
HandleObject(Handle * h)449 HandleObject(Handle *h)
450 {
451 return h->object;
452 }
453
454 Pool *
HandlePool(Handle * h)455 HandlePool(Handle *h)
456 {
457 return h->whence;
458 }
459
HandlePDelete(Handle ** hp)460 void HandlePDelete(Handle **hp)
461 {
462 if(hp && *hp) {
463 HandleUnregister(hp);
464 HandleDelete(*hp);
465 *hp = NULL;
466 }
467 }
468
469 /* Simple debugging aid: dump the list of currently defined handles
470 * and their ref-counts, and the ref. counts of the attached object
471 * (if any).
472 */
handle_dump(void)473 void handle_dump(void)
474 {
475 HandleOps *ops;
476 Handle *h;
477
478 OOGLWarn("Active handles:");
479
480 DblListIterateNoDelete(&AllHandles, HandleOps, node, ops) {
481 DblListIterateNoDelete(&ops->handles, Handle, opsnode, h) {
482 OOGLWarn(" %s[%s]@%p (%s: #%d, o: #%d )",
483 ops->prefix, h->name, (void *)h,
484 h->permanent ? "H" : "h",
485 RefCount((Ref *)h),
486 h->object ? RefCount((Ref *)h->object) : -1);
487 }
488 }
489 }
490
491 /* In principle, it is not necessary to distinguish between permanent
492 * and non-permanent handles IF the ref-counts are kept up to date.
493 */
HandleDelete(Handle * h)494 void HandleDelete(Handle *h)
495 {
496 if (h == NULL) {
497 return;
498 }
499
500 if (h->magic != HANDLEMAGIC) {
501 OOGLWarn("Internal warning: HandleDelete of non-Handle %x (%x != %x)",
502 h, h->magic, HANDLEMAGIC);
503 return;
504 }
505
506 if (REFPUT(h) > 0) {
507 return;
508 }
509
510 #if HANDLE_DEBUG
511 OOGLWarn("Deleting handle with name \"%s\"", h->name);
512 if (!DblListEmpty(&h->refs)) {
513 OOGLWarn("Deleting handle with active back references.");
514 abort();
515 }
516 #endif
517
518 /* remove ourselves from various lists */
519 DblListDelete(&h->objnode);
520 DblListDelete(&h->opsnode);
521 DblListDelete(&h->poolnode);
522
523 if (h->object) {
524 /* undo RefIncr() from HandleSetObject() */
525 if (h->ops->delete) {
526 (*h->ops->delete)(h->object);
527 } else {
528 REFPUT(h->object);
529 }
530 }
531
532 /* Don't let Pool code think we have something cached in this handle! */
533 if (h->whence && h->whence->seekable) {
534 h->whence->flags &= ~PF_ANY;
535 if (!h->permanent) {
536 #if 1
537 /* Should we do this? If we are not permanent and attached to a
538 * pool, then we are a dummy pool-handle assigned to an object
539 * just read from the pool. Still the pool could belong to a
540 * pipe, and maybe we should not close and delete the pool.
541 *
542 * I think closing and deleting is ok here ...
543 *
544 * If we are permanent, then we should not close whence.
545 */
546 PoolClose(h->whence);
547 PoolDelete(h->whence);
548 #endif
549 }
550 }
551
552 if(h->name) {
553 free(h->name);
554 }
555
556 memset(h, 0, sizeof(*h));
557 FREELIST_FREE(Handle, h);
558
559 /* handle_dump();*/
560 }
561
562 /*
563 * Local Variables: ***
564 * mode: c ***
565 * c-basic-offset: 2 ***
566 * End: ***
567 */
568