gc4.9 tarball import
[platform/upstream/libgc.git] / finalize.c
1 /*
2  * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
3  * Copyright (c) 1991-1996 by Xerox Corporation.  All rights reserved.
4
5  * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
6  * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
7  *
8  * Permission is hereby granted to use or copy this program
9  * for any purpose,  provided the above notices are retained on all copies.
10  * Permission to modify the code and to distribute modified code is granted,
11  * provided the above notices are retained, and a notice that the code was
12  * modified is included with the above copyright notice.
13  */
14 /* Boehm, February 1, 1996 1:19 pm PST */
15 # define I_HIDE_POINTERS
16 # include "gc_priv.h"
17 # include "gc_mark.h"
18
19 /* Type of mark procedure used for marking from finalizable object.     */
20 /* This procedure normally does not mark the object, only its           */
21 /* descendents.                                                         */
22 typedef void finalization_mark_proc(/* ptr_t finalizable_obj_ptr */); 
23
24 # define HASH3(addr,size,log_size) \
25     ((((word)(addr) >> 3) ^ ((word)(addr) >> (3+(log_size)))) \
26     & ((size) - 1))
27 #define HASH2(addr,log_size) HASH3(addr, 1 << log_size, log_size)
28
29 struct hash_chain_entry {
30     word hidden_key;
31     struct hash_chain_entry * next;
32 };
33
34 unsigned GC_finalization_failures = 0;
35         /* Number of finalization requests that failed for lack of memory. */
36
37 static struct disappearing_link {
38     struct hash_chain_entry prolog;
39 #   define dl_hidden_link prolog.hidden_key
40                                 /* Field to be cleared.         */
41 #   define dl_next(x) (struct disappearing_link *)((x) -> prolog.next)
42 #   define dl_set_next(x,y) (x) -> prolog.next = (struct hash_chain_entry *)(y)
43
44     word dl_hidden_obj;         /* Pointer to object base       */
45 } **dl_head = 0;
46
47 static signed_word log_dl_table_size = -1;
48                         /* Binary log of                                */
49                         /* current size of array pointed to by dl_head. */
50                         /* -1 ==> size is 0.                            */
51
52 word GC_dl_entries = 0; /* Number of entries currently in disappearing  */
53                         /* link table.                                  */
54
55 static struct finalizable_object {
56     struct hash_chain_entry prolog;
57 #   define fo_hidden_base prolog.hidden_key
58                                 /* Pointer to object base.      */
59                                 /* No longer hidden once object */
60                                 /* is on finalize_now queue.    */
61 #   define fo_next(x) (struct finalizable_object *)((x) -> prolog.next)
62 #   define fo_set_next(x,y) (x) -> prolog.next = (struct hash_chain_entry *)(y)
63     GC_finalization_proc fo_fn; /* Finalizer.                   */
64     ptr_t fo_client_data;
65     word fo_object_size;        /* In bytes.                    */
66     finalization_mark_proc * fo_mark_proc;      /* Mark-through procedure */
67 } **fo_head = 0;
68
69 struct finalizable_object * GC_finalize_now = 0;
70         /* LIst of objects that should be finalized now.        */
71
72 static signed_word log_fo_table_size = -1;
73
74 word GC_fo_entries = 0;
75
76 # ifdef SRC_M3
77 void GC_push_finalizer_structures()
78 {
79     GC_push_all((ptr_t)(&dl_head), (ptr_t)(&dl_head) + sizeof(word));
80     GC_push_all((ptr_t)(&fo_head), (ptr_t)(&fo_head) + sizeof(word));
81 }
82 # endif
83
84 /* Double the size of a hash table. *size_ptr is the log of its current */
85 /* size.  May be a noop.                                                */
86 /* *table is a pointer to an array of hash headers.  If we succeed, we  */
87 /* update both *table and *log_size_ptr.                                */
88 /* Lock is held.  Signals are disabled.                                 */
89 void GC_grow_table(table, log_size_ptr)
90 struct hash_chain_entry ***table;
91 signed_word * log_size_ptr;
92 {
93     register word i;
94     register struct hash_chain_entry *p;
95     int log_old_size = *log_size_ptr;
96     register int log_new_size = log_old_size + 1;
97     word old_size = ((log_old_size == -1)? 0: (1 << log_old_size));
98     register word new_size = 1 << log_new_size;
99     struct hash_chain_entry **new_table = (struct hash_chain_entry **)
100         GC_generic_malloc_inner_ignore_off_page(
101                 (size_t)new_size * sizeof(struct hash_chain_entry *), NORMAL);
102     
103     if (new_table == 0) {
104         if (table == 0) {
105             ABORT("Insufficient space for initial table allocation");
106         } else {
107             return;
108         }
109     }
110     for (i = 0; i < old_size; i++) {
111       p = (*table)[i];
112       while (p != 0) {
113         register ptr_t real_key = (ptr_t)REVEAL_POINTER(p -> hidden_key);
114         register struct hash_chain_entry *next = p -> next;
115         register int new_hash = HASH3(real_key, new_size, log_new_size);
116         
117         p -> next = new_table[new_hash];
118         new_table[new_hash] = p;
119         p = next;
120       }
121     }
122     *log_size_ptr = log_new_size;
123     *table = new_table;
124 }
125
126 # if defined(__STDC__) || defined(__cplusplus)
127     int GC_register_disappearing_link(GC_PTR * link)
128 # else
129     int GC_register_disappearing_link(link)
130     GC_PTR * link;
131 # endif
132 {
133     ptr_t base;
134     
135     base = (ptr_t)GC_base((GC_PTR)link);
136     if (base == 0)
137         ABORT("Bad arg to GC_register_disappearing_link");
138     return(GC_general_register_disappearing_link(link, base));
139 }
140
141 # if defined(__STDC__) || defined(__cplusplus)
142     int GC_general_register_disappearing_link(GC_PTR * link,
143                                               GC_PTR obj)
144 # else
145     int GC_general_register_disappearing_link(link, obj)
146     GC_PTR * link;
147     GC_PTR obj;
148 # endif
149
150 {
151     struct disappearing_link *curr_dl;
152     int index;
153     struct disappearing_link * new_dl;
154     DCL_LOCK_STATE;
155     
156     if ((word)link & (ALIGNMENT-1))
157         ABORT("Bad arg to GC_general_register_disappearing_link");
158 #   ifdef THREADS
159         DISABLE_SIGNALS();
160         LOCK();
161 #   endif
162     if (log_dl_table_size == -1
163         || GC_dl_entries > ((word)1 << log_dl_table_size)) {
164 #       ifndef THREADS
165             DISABLE_SIGNALS();
166 #       endif
167         GC_grow_table((struct hash_chain_entry ***)(&dl_head),
168                       &log_dl_table_size);
169 #       ifdef PRINTSTATS
170             GC_printf1("Grew dl table to %lu entries\n",
171                         (unsigned long)(1 << log_dl_table_size));
172 #       endif
173 #       ifndef THREADS
174             ENABLE_SIGNALS();
175 #       endif
176     }
177     index = HASH2(link, log_dl_table_size);
178     curr_dl = dl_head[index];
179     for (curr_dl = dl_head[index]; curr_dl != 0; curr_dl = dl_next(curr_dl)) {
180         if (curr_dl -> dl_hidden_link == HIDE_POINTER(link)) {
181             curr_dl -> dl_hidden_obj = HIDE_POINTER(obj);
182 #           ifdef THREADS
183                 UNLOCK();
184                 ENABLE_SIGNALS();
185 #           endif
186             return(1);
187         }
188     }
189 #   ifdef THREADS
190       new_dl = (struct disappearing_link *)
191         GC_generic_malloc_inner(sizeof(struct disappearing_link),NORMAL);
192 #   else
193       new_dl = (struct disappearing_link *)
194         GC_malloc(sizeof(struct disappearing_link));
195 #   endif
196     if (new_dl != 0) {
197         new_dl -> dl_hidden_obj = HIDE_POINTER(obj);
198         new_dl -> dl_hidden_link = HIDE_POINTER(link);
199         dl_set_next(new_dl, dl_head[index]);
200         dl_head[index] = new_dl;
201         GC_dl_entries++;
202     } else {
203         GC_finalization_failures++;
204     }
205 #   ifdef THREADS
206         UNLOCK();
207         ENABLE_SIGNALS();
208 #   endif
209     return(0);
210 }
211
212 # if defined(__STDC__) || defined(__cplusplus)
213     int GC_unregister_disappearing_link(GC_PTR * link)
214 # else
215     int GC_unregister_disappearing_link(link)
216     GC_PTR * link;
217 # endif
218 {
219     struct disappearing_link *curr_dl, *prev_dl;
220     int index;
221     DCL_LOCK_STATE;
222     
223     DISABLE_SIGNALS();
224     LOCK();
225     index = HASH2(link, log_dl_table_size);
226     if (((unsigned long)link & (ALIGNMENT-1))) goto out;
227     prev_dl = 0; curr_dl = dl_head[index];
228     while (curr_dl != 0) {
229         if (curr_dl -> dl_hidden_link == HIDE_POINTER(link)) {
230             if (prev_dl == 0) {
231                 dl_head[index] = dl_next(curr_dl);
232             } else {
233                 dl_set_next(prev_dl, dl_next(curr_dl));
234             }
235             GC_dl_entries--;
236             UNLOCK();
237             ENABLE_SIGNALS();
238             GC_free((GC_PTR)curr_dl);
239             return(1);
240         }
241         prev_dl = curr_dl;
242         curr_dl = dl_next(curr_dl);
243     }
244 out:
245     UNLOCK();
246     ENABLE_SIGNALS();
247     return(0);
248 }
249
250 /* Possible finalization_marker procedures.  Note that mark stack       */
251 /* overflow is handled by the caller, and is not a disaster.            */
252 void GC_normal_finalize_mark_proc(p)
253 ptr_t p;
254 {
255     hdr * hhdr = HDR(p);
256     
257     PUSH_OBJ((word *)p, hhdr, GC_mark_stack_top,
258              &(GC_mark_stack[GC_mark_stack_size]));
259 }
260
261 /* This only pays very partial attention to the mark descriptor.        */
262 /* It does the right thing for normal and atomic objects, and treats    */
263 /* most others as normal.                                               */
264 void GC_ignore_self_finalize_mark_proc(p)
265 ptr_t p;
266 {
267     hdr * hhdr = HDR(p);
268     word descr = hhdr -> hb_descr;
269     ptr_t q, r;
270     ptr_t scan_limit;
271     ptr_t target_limit = p + WORDS_TO_BYTES(hhdr -> hb_sz) - 1;
272     
273     if ((descr & DS_TAGS) == DS_LENGTH) {
274        scan_limit = p + descr - sizeof(word);
275     } else {
276        scan_limit = target_limit + 1 - sizeof(word);
277     }
278     for (q = p; q <= scan_limit; q += ALIGNMENT) {
279         r = *(ptr_t *)q;
280         if (r < p || r > target_limit) {
281             GC_PUSH_ONE_HEAP((word)r);
282         }
283     }
284 }
285
286 /*ARGSUSED*/
287 void GC_null_finalize_mark_proc(p)
288 ptr_t p;
289 {
290 }
291
292
293
294 /* Register a finalization function.  See gc.h for details.     */
295 /* in the nonthreads case, we try to avoid disabling signals,   */
296 /* since it can be expensive.  Threads packages typically       */
297 /* make it cheaper.                                             */
298 void GC_register_finalizer_inner(obj, fn, cd, ofn, ocd, mp)
299 GC_PTR obj;
300 GC_finalization_proc fn;
301 GC_PTR cd;
302 GC_finalization_proc * ofn;
303 GC_PTR * ocd;
304 finalization_mark_proc * mp;
305 {
306     ptr_t base;
307     struct finalizable_object * curr_fo, * prev_fo;
308     int index;
309     struct finalizable_object *new_fo;
310     DCL_LOCK_STATE;
311
312 #   ifdef THREADS
313         DISABLE_SIGNALS();
314         LOCK();
315 #   endif
316     if (log_fo_table_size == -1
317         || GC_fo_entries > ((word)1 << log_fo_table_size)) {
318 #       ifndef THREADS
319             DISABLE_SIGNALS();
320 #       endif
321         GC_grow_table((struct hash_chain_entry ***)(&fo_head),
322                       &log_fo_table_size);
323 #       ifdef PRINTSTATS
324             GC_printf1("Grew fo table to %lu entries\n",
325                         (unsigned long)(1 << log_fo_table_size));
326 #       endif
327 #       ifndef THREADS
328             ENABLE_SIGNALS();
329 #       endif
330     }
331     /* in the THREADS case signals are disabled and we hold allocation  */
332     /* lock; otherwise neither is true.  Proceed carefully.             */
333     base = (ptr_t)obj;
334     index = HASH2(base, log_fo_table_size);
335     prev_fo = 0; curr_fo = fo_head[index];
336     while (curr_fo != 0) {
337         if (curr_fo -> fo_hidden_base == HIDE_POINTER(base)) {
338             /* Interruption by a signal in the middle of this   */
339             /* should be safe.  The client may see only *ocd    */
340             /* updated, but we'll declare that to be his        */
341             /* problem.                                         */
342             if (ocd) *ocd = (GC_PTR) curr_fo -> fo_client_data;
343             if (ofn) *ofn = curr_fo -> fo_fn;
344             /* Delete the structure for base. */
345                 if (prev_fo == 0) {
346                   fo_head[index] = fo_next(curr_fo);
347                 } else {
348                   fo_set_next(prev_fo, fo_next(curr_fo));
349                 }
350             if (fn == 0) {
351                 GC_fo_entries--;
352                   /* May not happen if we get a signal.  But a high     */
353                   /* estimate will only make the table larger than      */
354                   /* necessary.                                         */
355 #               ifndef THREADS
356                   GC_free((GC_PTR)curr_fo);
357 #               endif
358             } else {
359                 curr_fo -> fo_fn = fn;
360                 curr_fo -> fo_client_data = (ptr_t)cd;
361                 curr_fo -> fo_mark_proc = mp;
362                 /* Reinsert it.  We deleted it first to maintain        */
363                 /* consistency in the event of a signal.                */
364                 if (prev_fo == 0) {
365                   fo_head[index] = curr_fo;
366                 } else {
367                   fo_set_next(prev_fo, curr_fo);
368                 }
369             }
370 #           ifdef THREADS
371                 UNLOCK();
372                 ENABLE_SIGNALS();
373 #           endif
374             return;
375         }
376         prev_fo = curr_fo;
377         curr_fo = fo_next(curr_fo);
378     }
379     if (ofn) *ofn = 0;
380     if (ocd) *ocd = 0;
381     if (fn == 0) {
382 #       ifdef THREADS
383             UNLOCK();
384             ENABLE_SIGNALS();
385 #       endif
386         return;
387     }
388 #   ifdef THREADS
389       new_fo = (struct finalizable_object *)
390         GC_generic_malloc_inner(sizeof(struct finalizable_object),NORMAL);
391 #   else
392       new_fo = (struct finalizable_object *)
393         GC_malloc(sizeof(struct finalizable_object));
394 #   endif
395     if (new_fo != 0) {
396         new_fo -> fo_hidden_base = (word)HIDE_POINTER(base);
397         new_fo -> fo_fn = fn;
398         new_fo -> fo_client_data = (ptr_t)cd;
399         new_fo -> fo_object_size = GC_size(base);
400         new_fo -> fo_mark_proc = mp;
401         fo_set_next(new_fo, fo_head[index]);
402         GC_fo_entries++;
403         fo_head[index] = new_fo;
404     } else {
405         GC_finalization_failures++;
406     }
407 #   ifdef THREADS
408         UNLOCK();
409         ENABLE_SIGNALS();
410 #   endif
411 }
412
413 # if defined(__STDC__)
414     void GC_register_finalizer(void * obj,
415                                GC_finalization_proc fn, void * cd,
416                                GC_finalization_proc *ofn, void ** ocd)
417 # else
418     void GC_register_finalizer(obj, fn, cd, ofn, ocd)
419     GC_PTR obj;
420     GC_finalization_proc fn;
421     GC_PTR cd;
422     GC_finalization_proc * ofn;
423     GC_PTR * ocd;
424 # endif
425 {
426     GC_register_finalizer_inner(obj, fn, cd, ofn,
427                                 ocd, GC_normal_finalize_mark_proc);
428 }
429
430 # if defined(__STDC__)
431     void GC_register_finalizer_ignore_self(void * obj,
432                                GC_finalization_proc fn, void * cd,
433                                GC_finalization_proc *ofn, void ** ocd)
434 # else
435     void GC_register_finalizer_ignore_self(obj, fn, cd, ofn, ocd)
436     GC_PTR obj;
437     GC_finalization_proc fn;
438     GC_PTR cd;
439     GC_finalization_proc * ofn;
440     GC_PTR * ocd;
441 # endif
442 {
443     GC_register_finalizer_inner(obj, fn, cd, ofn,
444                                 ocd, GC_ignore_self_finalize_mark_proc);
445 }
446
447 # if defined(__STDC__)
448     void GC_register_finalizer_no_order(void * obj,
449                                GC_finalization_proc fn, void * cd,
450                                GC_finalization_proc *ofn, void ** ocd)
451 # else
452     void GC_register_finalizer_no_order(obj, fn, cd, ofn, ocd)
453     GC_PTR obj;
454     GC_finalization_proc fn;
455     GC_PTR cd;
456     GC_finalization_proc * ofn;
457     GC_PTR * ocd;
458 # endif
459 {
460     GC_register_finalizer_inner(obj, fn, cd, ofn,
461                                 ocd, GC_null_finalize_mark_proc);
462 }
463
464
465 /* Called with world stopped.  Cause disappearing links to disappear,   */
466 /* and invoke finalizers.                                               */
467 void GC_finalize()
468 {
469     struct disappearing_link * curr_dl, * prev_dl, * next_dl;
470     struct finalizable_object * curr_fo, * prev_fo, * next_fo;
471     ptr_t real_ptr, real_link;
472     register int i;
473     int dl_size = (log_dl_table_size == -1 ) ? 0 : (1 << log_dl_table_size);
474     int fo_size = (log_fo_table_size == -1 ) ? 0 : (1 << log_fo_table_size);
475     
476   /* Make disappearing links disappear */
477     for (i = 0; i < dl_size; i++) {
478       curr_dl = dl_head[i];
479       prev_dl = 0;
480       while (curr_dl != 0) {
481         real_ptr = (ptr_t)REVEAL_POINTER(curr_dl -> dl_hidden_obj);
482         real_link = (ptr_t)REVEAL_POINTER(curr_dl -> dl_hidden_link);
483         if (!GC_is_marked(real_ptr)) {
484             *(word *)real_link = 0;
485             next_dl = dl_next(curr_dl);
486             if (prev_dl == 0) {
487                 dl_head[i] = next_dl;
488             } else {
489                 dl_set_next(prev_dl, next_dl);
490             }
491             GC_clear_mark_bit((ptr_t)curr_dl);
492             GC_dl_entries--;
493             curr_dl = next_dl;
494         } else {
495             prev_dl = curr_dl;
496             curr_dl = dl_next(curr_dl);
497         }
498       }
499     }
500   /* Mark all objects reachable via chains of 1 or more pointers        */
501   /* from finalizable objects.                                          */
502 #   ifdef PRINTSTATS
503         if (GC_mark_state != MS_NONE) ABORT("Bad mark state");
504 #   endif
505     for (i = 0; i < fo_size; i++) {
506       for (curr_fo = fo_head[i]; curr_fo != 0; curr_fo = fo_next(curr_fo)) {
507         real_ptr = (ptr_t)REVEAL_POINTER(curr_fo -> fo_hidden_base);
508         if (!GC_is_marked(real_ptr)) {
509             (*(curr_fo -> fo_mark_proc))(real_ptr);
510             while (!GC_mark_stack_empty()) GC_mark_from_mark_stack();
511             if (GC_mark_state != MS_NONE) {
512                 /* Mark stack overflowed. Very unlikely. */
513 #               ifdef PRINTSTATS
514                     if (GC_mark_state != MS_INVALID) ABORT("Bad mark state");
515                     GC_printf0("Mark stack overflowed in finalization!!\n");
516 #               endif
517                 /* Make mark bits consistent again.  Forget about       */
518                 /* finalizing this object for now.                      */
519                     GC_set_mark_bit(real_ptr);
520                     while (!GC_mark_some());
521             }
522             if (GC_is_marked(real_ptr)) {
523                 WARN("Finalization cycle involving %lx\n", real_ptr);
524             }
525         }
526         
527       }
528     }
529   /* Enqueue for finalization all objects that are still                */
530   /* unreachable.                                                       */
531     GC_words_finalized = 0;
532     for (i = 0; i < fo_size; i++) {
533       curr_fo = fo_head[i];
534       prev_fo = 0;
535       while (curr_fo != 0) {
536         real_ptr = (ptr_t)REVEAL_POINTER(curr_fo -> fo_hidden_base);
537         if (!GC_is_marked(real_ptr)) {
538             GC_set_mark_bit(real_ptr);
539             /* Delete from hash table */
540               next_fo = fo_next(curr_fo);
541               if (prev_fo == 0) {
542                 fo_head[i] = next_fo;
543               } else {
544                 fo_set_next(prev_fo, next_fo);
545               }
546               GC_fo_entries--;
547             /* Add to list of objects awaiting finalization.    */
548               fo_set_next(curr_fo, GC_finalize_now);
549               GC_finalize_now = curr_fo;
550               /* unhide object pointer so any future collections will   */
551               /* see it.                                                */
552               curr_fo -> fo_hidden_base = 
553                         (word) REVEAL_POINTER(curr_fo -> fo_hidden_base);
554               GC_words_finalized +=
555                         ALIGNED_WORDS(curr_fo -> fo_object_size)
556                         + ALIGNED_WORDS(sizeof(struct finalizable_object));
557 #           ifdef PRINTSTATS
558               if (!GC_is_marked((ptr_t)curr_fo)) {
559                 ABORT("GC_finalize: found accessible unmarked object\n");
560               }
561 #           endif
562             curr_fo = next_fo;
563         } else {
564             prev_fo = curr_fo;
565             curr_fo = fo_next(curr_fo);
566         }
567       }
568     }
569   /* Remove dangling disappearing links. */
570     for (i = 0; i < dl_size; i++) {
571       curr_dl = dl_head[i];
572       prev_dl = 0;
573       while (curr_dl != 0) {
574         real_link = GC_base((ptr_t)REVEAL_POINTER(curr_dl -> dl_hidden_link));
575         if (real_link != 0 && !GC_is_marked(real_link)) {
576             next_dl = dl_next(curr_dl);
577             if (prev_dl == 0) {
578                 dl_head[i] = next_dl;
579             } else {
580                 dl_set_next(prev_dl, next_dl);
581             }
582             GC_clear_mark_bit((ptr_t)curr_dl);
583             GC_dl_entries--;
584             curr_dl = next_dl;
585         } else {
586             prev_dl = curr_dl;
587             curr_dl = dl_next(curr_dl);
588         }
589       }
590     }
591 }
592
593 /* Invoke finalizers for all objects that are ready to be finalized.    */
594 /* Should be called without allocation lock.                            */
595 void GC_invoke_finalizers()
596 {
597     register struct finalizable_object * curr_fo;
598     DCL_LOCK_STATE;
599     
600     while (GC_finalize_now != 0) {
601 #       ifdef THREADS
602             DISABLE_SIGNALS();
603             LOCK();
604 #       endif
605         curr_fo = GC_finalize_now;
606 #       ifdef THREADS
607             if (curr_fo != 0) GC_finalize_now = fo_next(curr_fo);
608             UNLOCK();
609             ENABLE_SIGNALS();
610             if (curr_fo == 0) break;
611 #       else
612             GC_finalize_now = fo_next(curr_fo);
613 #       endif
614         fo_set_next(curr_fo, 0);
615         (*(curr_fo -> fo_fn))((ptr_t)(curr_fo -> fo_hidden_base),
616                               curr_fo -> fo_client_data);
617         curr_fo -> fo_client_data = 0;
618 #       ifdef UNDEFINED
619             /* This is probably a bad idea.  It throws off accounting if */
620             /* nearly all objects are finalizable.  O.w. it shouldn't    */
621             /* matter.                                                   */
622             GC_free((GC_PTR)curr_fo);
623 #       endif
624     }
625 }
626
627 # ifdef __STDC__
628     GC_PTR GC_call_with_alloc_lock(GC_fn_type fn,
629                                          GC_PTR client_data)
630 # else
631     GC_PTR GC_call_with_alloc_lock(fn, client_data)
632     GC_fn_type fn;
633     GC_PTR client_data;
634 # endif
635 {
636     GC_PTR result;
637     DCL_LOCK_STATE;
638     
639 #   ifdef THREADS
640       DISABLE_SIGNALS();
641       LOCK();
642 #   endif
643     result = (*fn)(client_data);
644 #   ifdef THREADS
645       UNLOCK();
646       ENABLE_SIGNALS();
647 #   endif
648     return(result);
649 }
650