Eliminate 'integer shift by a negative amount' code defect in finalize
[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  * Copyright (c) 1996-1999 by Silicon Graphics.  All rights reserved.
5  * Copyright (C) 2007 Free Software Foundation, Inc
6
7  * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
8  * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
9  *
10  * Permission is hereby granted to use or copy this program
11  * for any purpose,  provided the above notices are retained on all copies.
12  * Permission to modify the code and to distribute modified code is granted,
13  * provided the above notices are retained, and a notice that the code was
14  * modified is included with the above copyright notice.
15  */
16
17 #include "private/gc_pmark.h"
18
19 #ifndef GC_NO_FINALIZATION
20
21 /* Type of mark procedure used for marking from finalizable object.     */
22 /* This procedure normally does not mark the object, only its           */
23 /* descendants.                                                         */
24 typedef void (* finalization_mark_proc)(ptr_t /* finalizable_obj_ptr */);
25
26 #define HASH3(addr,size,log_size) \
27         ((((word)(addr) >> 3) ^ ((word)(addr) >> (3 + (log_size)))) \
28          & ((size) - 1))
29 #define HASH2(addr,log_size) HASH3(addr, (word)1 << (log_size), log_size)
30
31 struct hash_chain_entry {
32     word hidden_key;
33     struct hash_chain_entry * next;
34 };
35
36 struct disappearing_link {
37     struct hash_chain_entry prolog;
38 #   define dl_hidden_link prolog.hidden_key
39                                 /* Field to be cleared.         */
40 #   define dl_next(x) (struct disappearing_link *)((x) -> prolog.next)
41 #   define dl_set_next(x, y) \
42                 (void)((x)->prolog.next = (struct hash_chain_entry *)(y))
43     word dl_hidden_obj;         /* Pointer to object base       */
44 };
45
46 struct dl_hashtbl_s {
47     struct disappearing_link **head;
48     signed_word log_size;
49     word entries;
50 };
51
52 STATIC struct dl_hashtbl_s GC_dl_hashtbl = {
53     /* head */ NULL, /* log_size */ -1, /* entries */ 0 };
54 #ifndef GC_LONG_REFS_NOT_NEEDED
55   STATIC struct dl_hashtbl_s GC_ll_hashtbl = { NULL, -1, 0 };
56 #endif
57
58 struct finalizable_object {
59     struct hash_chain_entry prolog;
60 #   define fo_hidden_base prolog.hidden_key
61                                 /* Pointer to object base.      */
62                                 /* No longer hidden once object */
63                                 /* is on finalize_now queue.    */
64 #   define fo_next(x) (struct finalizable_object *)((x) -> prolog.next)
65 #   define fo_set_next(x,y) ((x)->prolog.next = (struct hash_chain_entry *)(y))
66     GC_finalization_proc fo_fn; /* Finalizer.                   */
67     ptr_t fo_client_data;
68     word fo_object_size;        /* In bytes.                    */
69     finalization_mark_proc fo_mark_proc;        /* Mark-through procedure */
70 };
71
72 static signed_word log_fo_table_size = -1;
73
74 STATIC struct {
75   struct finalizable_object **fo_head;
76   /* List of objects that should be finalized now: */
77   struct finalizable_object *finalize_now;
78 } GC_fnlz_roots = { NULL, NULL };
79
80 GC_API void GC_CALL GC_push_finalizer_structures(void)
81 {
82   GC_ASSERT((word)&GC_dl_hashtbl.head % sizeof(word) == 0);
83   GC_ASSERT((word)&GC_fnlz_roots % sizeof(word) == 0);
84 # ifndef GC_LONG_REFS_NOT_NEEDED
85     GC_ASSERT((word)&GC_ll_hashtbl.head % sizeof(word) == 0);
86     GC_PUSH_ALL_SYM(GC_ll_hashtbl.head);
87 # endif
88   GC_PUSH_ALL_SYM(GC_dl_hashtbl.head);
89   GC_PUSH_ALL_SYM(GC_fnlz_roots);
90 }
91
92 /* Double the size of a hash table. *size_ptr is the log of its current */
93 /* size.  May be a no-op.                                               */
94 /* *table is a pointer to an array of hash headers.  If we succeed, we  */
95 /* update both *table and *log_size_ptr.  Lock is held.                 */
96 STATIC void GC_grow_table(struct hash_chain_entry ***table,
97                           signed_word *log_size_ptr)
98 {
99     register word i;
100     register struct hash_chain_entry *p;
101     signed_word log_old_size = *log_size_ptr;
102     signed_word log_new_size = log_old_size + 1;
103     word old_size = log_old_size == -1 ? 0 : (word)1 << log_old_size;
104     word new_size = (word)1 << log_new_size;
105     /* FIXME: Power of 2 size often gets rounded up to one more page. */
106     struct hash_chain_entry **new_table;
107
108     GC_ASSERT(I_HOLD_LOCK());
109     new_table = (struct hash_chain_entry **)
110                     GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE(
111                         (size_t)new_size * sizeof(struct hash_chain_entry *),
112                         NORMAL);
113     if (new_table == 0) {
114         if (*table == 0) {
115             ABORT("Insufficient space for initial table allocation");
116         } else {
117             return;
118         }
119     }
120     for (i = 0; i < old_size; i++) {
121       p = (*table)[i];
122       while (p != 0) {
123         ptr_t real_key = GC_REVEAL_POINTER(p -> hidden_key);
124         struct hash_chain_entry *next = p -> next;
125         size_t new_hash = HASH3(real_key, new_size, log_new_size);
126
127         p -> next = new_table[new_hash];
128         new_table[new_hash] = p;
129         p = next;
130       }
131     }
132     *log_size_ptr = log_new_size;
133     *table = new_table;
134 }
135
136 GC_API int GC_CALL GC_register_disappearing_link(void * * link)
137 {
138     ptr_t base;
139
140     base = (ptr_t)GC_base(link);
141     if (base == 0)
142         ABORT("Bad arg to GC_register_disappearing_link");
143     return(GC_general_register_disappearing_link(link, base));
144 }
145
146 STATIC int GC_register_disappearing_link_inner(
147                         struct dl_hashtbl_s *dl_hashtbl, void **link,
148                         const void *obj, const char *tbl_log_name)
149 {
150     struct disappearing_link *curr_dl;
151     size_t index;
152     struct disappearing_link * new_dl;
153     DCL_LOCK_STATE;
154
155     LOCK();
156     GC_ASSERT(obj != NULL && GC_base_C(obj) == obj);
157     if (dl_hashtbl -> log_size == -1
158         || dl_hashtbl -> entries > ((word)1 << dl_hashtbl -> log_size)) {
159         GC_grow_table((struct hash_chain_entry ***)&dl_hashtbl -> head,
160                       &dl_hashtbl -> log_size);
161         GC_ASSERT(dl_hashtbl->log_size >= 0);
162         GC_COND_LOG_PRINTF("Grew %s table to %u entries\n", tbl_log_name,
163                            1 << (unsigned)dl_hashtbl -> log_size);
164     }
165     index = HASH2(link, dl_hashtbl -> log_size);
166     for (curr_dl = dl_hashtbl -> head[index]; curr_dl != 0;
167          curr_dl = dl_next(curr_dl)) {
168         if (curr_dl -> dl_hidden_link == GC_HIDE_POINTER(link)) {
169             curr_dl -> dl_hidden_obj = GC_HIDE_POINTER(obj);
170             UNLOCK();
171             return GC_DUPLICATE;
172         }
173     }
174     new_dl = (struct disappearing_link *)
175         GC_INTERNAL_MALLOC(sizeof(struct disappearing_link),NORMAL);
176     if (0 == new_dl) {
177       GC_oom_func oom_fn = GC_oom_fn;
178       UNLOCK();
179       new_dl = (struct disappearing_link *)
180                 (*oom_fn)(sizeof(struct disappearing_link));
181       if (0 == new_dl) {
182         return GC_NO_MEMORY;
183       }
184       /* It's not likely we'll make it here, but ... */
185       LOCK();
186       /* Recalculate index since the table may grow.    */
187       index = HASH2(link, dl_hashtbl -> log_size);
188       /* Check again that our disappearing link not in the table. */
189       for (curr_dl = dl_hashtbl -> head[index]; curr_dl != 0;
190            curr_dl = dl_next(curr_dl)) {
191         if (curr_dl -> dl_hidden_link == GC_HIDE_POINTER(link)) {
192           curr_dl -> dl_hidden_obj = GC_HIDE_POINTER(obj);
193           UNLOCK();
194 #         ifndef DBG_HDRS_ALL
195             /* Free unused new_dl returned by GC_oom_fn() */
196             GC_free((void *)new_dl);
197 #         endif
198           return GC_DUPLICATE;
199         }
200       }
201     }
202     new_dl -> dl_hidden_obj = GC_HIDE_POINTER(obj);
203     new_dl -> dl_hidden_link = GC_HIDE_POINTER(link);
204     dl_set_next(new_dl, dl_hashtbl -> head[index]);
205     dl_hashtbl -> head[index] = new_dl;
206     dl_hashtbl -> entries++;
207     UNLOCK();
208     return GC_SUCCESS;
209 }
210
211 GC_API int GC_CALL GC_general_register_disappearing_link(void * * link,
212                                                          const void * obj)
213 {
214     if (((word)link & (ALIGNMENT-1)) != 0 || !NONNULL_ARG_NOT_NULL(link))
215         ABORT("Bad arg to GC_general_register_disappearing_link");
216     return GC_register_disappearing_link_inner(&GC_dl_hashtbl, link, obj,
217                                                "dl");
218 }
219
220 #ifdef DBG_HDRS_ALL
221 # define FREE_DL_ENTRY(curr_dl) dl_set_next(curr_dl, NULL)
222 #else
223 # define FREE_DL_ENTRY(curr_dl) GC_free(curr_dl)
224 #endif
225
226 /* Unregisters given link and returns the link entry to free.   */
227 /* Assume the lock is held.                                     */
228 GC_INLINE struct disappearing_link *GC_unregister_disappearing_link_inner(
229                                 struct dl_hashtbl_s *dl_hashtbl, void **link)
230 {
231     struct disappearing_link *curr_dl;
232     struct disappearing_link *prev_dl = NULL;
233     size_t index;
234
235     if (dl_hashtbl->log_size == -1)
236         return NULL; /* prevent integer shift by a negative amount */
237
238     index = HASH2(link, dl_hashtbl->log_size);
239     for (curr_dl = dl_hashtbl -> head[index]; curr_dl;
240          curr_dl = dl_next(curr_dl)) {
241         if (curr_dl -> dl_hidden_link == GC_HIDE_POINTER(link)) {
242             /* Remove found entry from the table. */
243             if (NULL == prev_dl) {
244                 dl_hashtbl -> head[index] = dl_next(curr_dl);
245             } else {
246                 dl_set_next(prev_dl, dl_next(curr_dl));
247             }
248             dl_hashtbl -> entries--;
249             break;
250         }
251         prev_dl = curr_dl;
252     }
253     return curr_dl;
254 }
255
256 GC_API int GC_CALL GC_unregister_disappearing_link(void * * link)
257 {
258     struct disappearing_link *curr_dl;
259     DCL_LOCK_STATE;
260
261     if (((word)link & (ALIGNMENT-1)) != 0) return(0); /* Nothing to do. */
262
263     LOCK();
264     curr_dl = GC_unregister_disappearing_link_inner(&GC_dl_hashtbl, link);
265     UNLOCK();
266     if (NULL == curr_dl) return 0;
267     FREE_DL_ENTRY(curr_dl);
268     return 1;
269 }
270
271 /* Toggle-ref support.  */
272 #ifndef GC_TOGGLE_REFS_NOT_NEEDED
273   typedef union {
274     /* Lowest bit is used to distinguish between choices.       */
275     void *strong_ref;
276     GC_hidden_pointer weak_ref;
277   } GCToggleRef;
278
279   STATIC GC_toggleref_func GC_toggleref_callback = 0;
280   STATIC GCToggleRef *GC_toggleref_arr = NULL;
281   STATIC int GC_toggleref_array_size = 0;
282   STATIC int GC_toggleref_array_capacity = 0;
283
284   GC_INNER void GC_process_togglerefs(void)
285   {
286     int i;
287     int new_size = 0;
288
289     GC_ASSERT(I_HOLD_LOCK());
290     for (i = 0; i < GC_toggleref_array_size; ++i) {
291       GCToggleRef r = GC_toggleref_arr[i];
292       void *obj = r.strong_ref;
293
294       if (((word)obj & 1) != 0) {
295         obj = GC_REVEAL_POINTER(r.weak_ref);
296       }
297       if (NULL == obj) {
298         continue;
299       }
300       switch (GC_toggleref_callback(obj)) {
301       case GC_TOGGLE_REF_DROP:
302         break;
303       case GC_TOGGLE_REF_STRONG:
304         GC_toggleref_arr[new_size++].strong_ref = obj;
305         break;
306       case GC_TOGGLE_REF_WEAK:
307         GC_toggleref_arr[new_size++].weak_ref = GC_HIDE_POINTER(obj);
308         break;
309       default:
310         ABORT("Bad toggle-ref status returned by callback");
311       }
312     }
313
314     if (new_size < GC_toggleref_array_size) {
315       BZERO(&GC_toggleref_arr[new_size],
316             (GC_toggleref_array_size - new_size) * sizeof(GCToggleRef));
317       GC_toggleref_array_size = new_size;
318     }
319   }
320
321   STATIC void GC_normal_finalize_mark_proc(ptr_t);
322
323   static void push_and_mark_object(void *p)
324   {
325     GC_normal_finalize_mark_proc(p);
326     while (!GC_mark_stack_empty()) {
327       MARK_FROM_MARK_STACK();
328     }
329     GC_set_mark_bit(p);
330     if (GC_mark_state != MS_NONE) {
331       while (!GC_mark_some(0)) {
332         /* Empty. */
333       }
334     }
335   }
336
337   STATIC void GC_mark_togglerefs(void)
338   {
339     int i;
340     if (NULL == GC_toggleref_arr)
341       return;
342
343     /* TODO: Hide GC_toggleref_arr to avoid its marking from roots. */
344     GC_set_mark_bit(GC_toggleref_arr);
345     for (i = 0; i < GC_toggleref_array_size; ++i) {
346       void *obj = GC_toggleref_arr[i].strong_ref;
347       if (obj != NULL && ((word)obj & 1) == 0) {
348         push_and_mark_object(obj);
349       }
350     }
351   }
352
353   STATIC void GC_clear_togglerefs(void)
354   {
355     int i;
356     for (i = 0; i < GC_toggleref_array_size; ++i) {
357       if ((GC_toggleref_arr[i].weak_ref & 1) != 0) {
358         if (!GC_is_marked(GC_REVEAL_POINTER(GC_toggleref_arr[i].weak_ref))) {
359           GC_toggleref_arr[i].weak_ref = 0;
360         } else {
361           /* No need to copy, BDWGC is a non-moving collector.    */
362         }
363       }
364     }
365   }
366
367   GC_API void GC_CALL GC_set_toggleref_func(GC_toggleref_func fn)
368   {
369     DCL_LOCK_STATE;
370
371     LOCK();
372     GC_toggleref_callback = fn;
373     UNLOCK();
374   }
375
376   GC_API GC_toggleref_func GC_CALL GC_get_toggleref_func(void)
377   {
378     GC_toggleref_func fn;
379     DCL_LOCK_STATE;
380
381     LOCK();
382     fn = GC_toggleref_callback;
383     UNLOCK();
384     return fn;
385   }
386
387   static GC_bool ensure_toggleref_capacity(int capacity_inc)
388   {
389     GC_ASSERT(capacity_inc >= 0);
390     if (NULL == GC_toggleref_arr) {
391       GC_toggleref_array_capacity = 32; /* initial capacity */
392       GC_toggleref_arr = GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE(
393                         GC_toggleref_array_capacity * sizeof(GCToggleRef),
394                         NORMAL);
395       if (NULL == GC_toggleref_arr)
396         return FALSE;
397     }
398     if ((unsigned)GC_toggleref_array_size + (unsigned)capacity_inc
399         >= (unsigned)GC_toggleref_array_capacity) {
400       GCToggleRef *new_array;
401       while ((unsigned)GC_toggleref_array_capacity
402               < (unsigned)GC_toggleref_array_size + (unsigned)capacity_inc) {
403         GC_toggleref_array_capacity *= 2;
404         if (GC_toggleref_array_capacity < 0) /* overflow */
405           return FALSE;
406       }
407
408       new_array = GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE(
409                         GC_toggleref_array_capacity * sizeof(GCToggleRef),
410                         NORMAL);
411       if (NULL == new_array)
412         return FALSE;
413       BCOPY(GC_toggleref_arr, new_array,
414             GC_toggleref_array_size * sizeof(GCToggleRef));
415       GC_INTERNAL_FREE(GC_toggleref_arr);
416       GC_toggleref_arr = new_array;
417     }
418     return TRUE;
419   }
420
421   GC_API int GC_CALL GC_toggleref_add(void *obj, int is_strong_ref)
422   {
423     int res = GC_SUCCESS;
424     DCL_LOCK_STATE;
425
426     GC_ASSERT(obj != NULL);
427     LOCK();
428     if (GC_toggleref_callback != 0) {
429       if (!ensure_toggleref_capacity(1)) {
430         res = GC_NO_MEMORY;
431       } else {
432         GC_toggleref_arr[GC_toggleref_array_size++].strong_ref =
433                         is_strong_ref ? obj : (void *)GC_HIDE_POINTER(obj);
434       }
435     }
436     UNLOCK();
437     return res;
438   }
439 #endif /* !GC_TOGGLE_REFS_NOT_NEEDED */
440
441 /* Finalizer callback support. */
442 STATIC GC_await_finalize_proc GC_object_finalized_proc = 0;
443
444 GC_API void GC_CALL GC_set_await_finalize_proc(GC_await_finalize_proc fn)
445 {
446   DCL_LOCK_STATE;
447
448   LOCK();
449   GC_object_finalized_proc = fn;
450   UNLOCK();
451 }
452
453 GC_API GC_await_finalize_proc GC_CALL GC_get_await_finalize_proc(void)
454 {
455   GC_await_finalize_proc fn;
456   DCL_LOCK_STATE;
457
458   LOCK();
459   fn = GC_object_finalized_proc;
460   UNLOCK();
461   return fn;
462 }
463
464 #ifndef GC_LONG_REFS_NOT_NEEDED
465   GC_API int GC_CALL GC_register_long_link(void * * link, const void * obj)
466   {
467     if (((word)link & (ALIGNMENT-1)) != 0 || !NONNULL_ARG_NOT_NULL(link))
468         ABORT("Bad arg to GC_register_long_link");
469     return GC_register_disappearing_link_inner(&GC_ll_hashtbl, link, obj,
470                                                "long dl");
471   }
472
473   GC_API int GC_CALL GC_unregister_long_link(void * * link)
474   {
475     struct disappearing_link *curr_dl;
476     DCL_LOCK_STATE;
477
478     if (((word)link & (ALIGNMENT-1)) != 0) return(0); /* Nothing to do. */
479
480     LOCK();
481     curr_dl = GC_unregister_disappearing_link_inner(&GC_ll_hashtbl, link);
482     UNLOCK();
483     if (NULL == curr_dl) return 0;
484     FREE_DL_ENTRY(curr_dl);
485     return 1;
486   }
487 #endif /* !GC_LONG_REFS_NOT_NEEDED */
488
489 #ifndef GC_MOVE_DISAPPEARING_LINK_NOT_NEEDED
490   /* Moves a link.  Assume the lock is held.    */
491   STATIC int GC_move_disappearing_link_inner(
492                                 struct dl_hashtbl_s *dl_hashtbl,
493                                 void **link, void **new_link)
494   {
495     struct disappearing_link *curr_dl, *prev_dl, *new_dl;
496     size_t curr_index, new_index;
497     word curr_hidden_link;
498     word new_hidden_link;
499
500     if (dl_hashtbl->log_size == -1)
501         return GC_NOT_FOUND; /* prevent integer shift by a negative amount */
502
503     /* Find current link.       */
504     curr_index = HASH2(link, dl_hashtbl -> log_size);
505     curr_hidden_link = GC_HIDE_POINTER(link);
506     prev_dl = NULL;
507     for (curr_dl = dl_hashtbl -> head[curr_index]; curr_dl;
508          curr_dl = dl_next(curr_dl)) {
509       if (curr_dl -> dl_hidden_link == curr_hidden_link)
510         break;
511       prev_dl = curr_dl;
512     }
513
514     if (NULL == curr_dl) {
515       return GC_NOT_FOUND;
516     }
517
518     if (link == new_link) {
519       return GC_SUCCESS; /* Nothing to do.      */
520     }
521
522     /* link found; now check new_link not present.      */
523     new_index = HASH2(new_link, dl_hashtbl -> log_size);
524     new_hidden_link = GC_HIDE_POINTER(new_link);
525     for (new_dl = dl_hashtbl -> head[new_index]; new_dl;
526          new_dl = dl_next(new_dl)) {
527       if (new_dl -> dl_hidden_link == new_hidden_link) {
528         /* Target already registered; bail.     */
529         return GC_DUPLICATE;
530       }
531     }
532
533     /* Remove from old, add to new, update link.        */
534     if (NULL == prev_dl) {
535       dl_hashtbl -> head[curr_index] = dl_next(curr_dl);
536     } else {
537       dl_set_next(prev_dl, dl_next(curr_dl));
538     }
539     curr_dl -> dl_hidden_link = new_hidden_link;
540     dl_set_next(curr_dl, dl_hashtbl -> head[new_index]);
541     dl_hashtbl -> head[new_index] = curr_dl;
542     return GC_SUCCESS;
543   }
544
545   GC_API int GC_CALL GC_move_disappearing_link(void **link, void **new_link)
546   {
547     int result;
548     DCL_LOCK_STATE;
549
550     if (((word)new_link & (ALIGNMENT-1)) != 0
551         || !NONNULL_ARG_NOT_NULL(new_link))
552       ABORT("Bad new_link arg to GC_move_disappearing_link");
553     if (((word)link & (ALIGNMENT-1)) != 0)
554       return GC_NOT_FOUND; /* Nothing to do. */
555
556     LOCK();
557     result = GC_move_disappearing_link_inner(&GC_dl_hashtbl, link, new_link);
558     UNLOCK();
559     return result;
560   }
561
562 # ifndef GC_LONG_REFS_NOT_NEEDED
563     GC_API int GC_CALL GC_move_long_link(void **link, void **new_link)
564     {
565       int result;
566       DCL_LOCK_STATE;
567
568       if (((word)new_link & (ALIGNMENT-1)) != 0
569           || !NONNULL_ARG_NOT_NULL(new_link))
570         ABORT("Bad new_link arg to GC_move_long_link");
571       if (((word)link & (ALIGNMENT-1)) != 0)
572         return GC_NOT_FOUND; /* Nothing to do. */
573
574       LOCK();
575       result = GC_move_disappearing_link_inner(&GC_ll_hashtbl, link, new_link);
576       UNLOCK();
577       return result;
578     }
579 # endif /* !GC_LONG_REFS_NOT_NEEDED */
580 #endif /* !GC_MOVE_DISAPPEARING_LINK_NOT_NEEDED */
581
582 /* Possible finalization_marker procedures.  Note that mark stack       */
583 /* overflow is handled by the caller, and is not a disaster.            */
584 STATIC void GC_normal_finalize_mark_proc(ptr_t p)
585 {
586     hdr * hhdr = HDR(p);
587
588     PUSH_OBJ(p, hhdr, GC_mark_stack_top,
589              &(GC_mark_stack[GC_mark_stack_size]));
590 }
591
592 /* This only pays very partial attention to the mark descriptor.        */
593 /* It does the right thing for normal and atomic objects, and treats    */
594 /* most others as normal.                                               */
595 STATIC void GC_ignore_self_finalize_mark_proc(ptr_t p)
596 {
597     hdr * hhdr = HDR(p);
598     word descr = hhdr -> hb_descr;
599     ptr_t q;
600     ptr_t scan_limit;
601     ptr_t target_limit = p + hhdr -> hb_sz - 1;
602
603     if ((descr & GC_DS_TAGS) == GC_DS_LENGTH) {
604        scan_limit = p + descr - sizeof(word);
605     } else {
606        scan_limit = target_limit + 1 - sizeof(word);
607     }
608     for (q = p; (word)q <= (word)scan_limit; q += ALIGNMENT) {
609         word r = *(word *)q;
610
611         if (r < (word)p || r > (word)target_limit) {
612             GC_PUSH_ONE_HEAP(r, q, GC_mark_stack_top);
613         }
614     }
615 }
616
617 STATIC void GC_null_finalize_mark_proc(ptr_t p GC_ATTR_UNUSED) {}
618
619 /* Possible finalization_marker procedures.  Note that mark stack       */
620 /* overflow is handled by the caller, and is not a disaster.            */
621
622 /* GC_unreachable_finalize_mark_proc is an alias for normal marking,    */
623 /* but it is explicitly tested for, and triggers different              */
624 /* behavior.  Objects registered in this way are not finalized          */
625 /* if they are reachable by other finalizable objects, even if those    */
626 /* other objects specify no ordering.                                   */
627 STATIC void GC_unreachable_finalize_mark_proc(ptr_t p)
628 {
629     GC_normal_finalize_mark_proc(p);
630 }
631
632 /* Register a finalization function.  See gc.h for details.     */
633 /* The last parameter is a procedure that determines            */
634 /* marking for finalization ordering.  Any objects marked       */
635 /* by that procedure will be guaranteed to not have been        */
636 /* finalized when this finalizer is invoked.                    */
637 STATIC void GC_register_finalizer_inner(void * obj,
638                                         GC_finalization_proc fn, void *cd,
639                                         GC_finalization_proc *ofn, void **ocd,
640                                         finalization_mark_proc mp)
641 {
642     struct finalizable_object * curr_fo;
643     size_t index;
644     struct finalizable_object *new_fo = 0;
645     hdr *hhdr = NULL; /* initialized to prevent warning. */
646     DCL_LOCK_STATE;
647
648     LOCK();
649     if (log_fo_table_size == -1
650         || GC_fo_entries > ((word)1 << log_fo_table_size)) {
651         GC_grow_table((struct hash_chain_entry ***)&GC_fnlz_roots.fo_head,
652                       &log_fo_table_size);
653         GC_ASSERT(log_fo_table_size >= 0);
654         GC_COND_LOG_PRINTF("Grew fo table to %u entries\n",
655                            1 << (unsigned)log_fo_table_size);
656     }
657     /* in the THREADS case we hold allocation lock.             */
658     for (;;) {
659       struct finalizable_object *prev_fo = NULL;
660       GC_oom_func oom_fn;
661
662       index = HASH2(obj, log_fo_table_size);
663       curr_fo = GC_fnlz_roots.fo_head[index];
664       while (curr_fo != 0) {
665         GC_ASSERT(GC_size(curr_fo) >= sizeof(struct finalizable_object));
666         if (curr_fo -> fo_hidden_base == GC_HIDE_POINTER(obj)) {
667           /* Interruption by a signal in the middle of this     */
668           /* should be safe.  The client may see only *ocd      */
669           /* updated, but we'll declare that to be his problem. */
670           if (ocd) *ocd = (void *) (curr_fo -> fo_client_data);
671           if (ofn) *ofn = curr_fo -> fo_fn;
672           /* Delete the structure for obj.      */
673           if (prev_fo == 0) {
674             GC_fnlz_roots.fo_head[index] = fo_next(curr_fo);
675           } else {
676             fo_set_next(prev_fo, fo_next(curr_fo));
677           }
678           if (fn == 0) {
679             GC_fo_entries--;
680             /* May not happen if we get a signal.  But a high   */
681             /* estimate will only make the table larger than    */
682             /* necessary.                                       */
683 #           if !defined(THREADS) && !defined(DBG_HDRS_ALL)
684               GC_free((void *)curr_fo);
685 #           endif
686           } else {
687             curr_fo -> fo_fn = fn;
688             curr_fo -> fo_client_data = (ptr_t)cd;
689             curr_fo -> fo_mark_proc = mp;
690             /* Reinsert it.  We deleted it first to maintain    */
691             /* consistency in the event of a signal.            */
692             if (prev_fo == 0) {
693               GC_fnlz_roots.fo_head[index] = curr_fo;
694             } else {
695               fo_set_next(prev_fo, curr_fo);
696             }
697           }
698           UNLOCK();
699 #         ifndef DBG_HDRS_ALL
700             if (EXPECT(new_fo != 0, FALSE)) {
701               /* Free unused new_fo returned by GC_oom_fn() */
702               GC_free((void *)new_fo);
703             }
704 #         endif
705           return;
706         }
707         prev_fo = curr_fo;
708         curr_fo = fo_next(curr_fo);
709       }
710       if (EXPECT(new_fo != 0, FALSE)) {
711         /* new_fo is returned by GC_oom_fn().   */
712         GC_ASSERT(fn != 0);
713 #       ifdef LINT2
714           if (NULL == hhdr) ABORT("Bad hhdr in GC_register_finalizer_inner");
715 #       endif
716         break;
717       }
718       if (fn == 0) {
719         if (ocd) *ocd = 0;
720         if (ofn) *ofn = 0;
721         UNLOCK();
722         return;
723       }
724       GET_HDR(obj, hhdr);
725       if (EXPECT(0 == hhdr, FALSE)) {
726         /* We won't collect it, hence finalizer wouldn't be run. */
727         if (ocd) *ocd = 0;
728         if (ofn) *ofn = 0;
729         UNLOCK();
730         return;
731       }
732       new_fo = (struct finalizable_object *)
733         GC_INTERNAL_MALLOC(sizeof(struct finalizable_object),NORMAL);
734       if (EXPECT(new_fo != 0, TRUE))
735         break;
736       oom_fn = GC_oom_fn;
737       UNLOCK();
738       new_fo = (struct finalizable_object *)
739                 (*oom_fn)(sizeof(struct finalizable_object));
740       if (0 == new_fo) {
741         /* No enough memory.  *ocd and *ofn remains unchanged.  */
742         return;
743       }
744       /* It's not likely we'll make it here, but ... */
745       LOCK();
746       /* Recalculate index since the table may grow and         */
747       /* check again that our finalizer is not in the table.    */
748     }
749     GC_ASSERT(GC_size(new_fo) >= sizeof(struct finalizable_object));
750     if (ocd) *ocd = 0;
751     if (ofn) *ofn = 0;
752     new_fo -> fo_hidden_base = GC_HIDE_POINTER(obj);
753     new_fo -> fo_fn = fn;
754     new_fo -> fo_client_data = (ptr_t)cd;
755     new_fo -> fo_object_size = hhdr -> hb_sz;
756     new_fo -> fo_mark_proc = mp;
757     fo_set_next(new_fo, GC_fnlz_roots.fo_head[index]);
758     GC_fo_entries++;
759     GC_fnlz_roots.fo_head[index] = new_fo;
760     UNLOCK();
761 }
762
763 GC_API void GC_CALL GC_register_finalizer(void * obj,
764                                   GC_finalization_proc fn, void * cd,
765                                   GC_finalization_proc *ofn, void ** ocd)
766 {
767     GC_register_finalizer_inner(obj, fn, cd, ofn,
768                                 ocd, GC_normal_finalize_mark_proc);
769 }
770
771 GC_API void GC_CALL GC_register_finalizer_ignore_self(void * obj,
772                                GC_finalization_proc fn, void * cd,
773                                GC_finalization_proc *ofn, void ** ocd)
774 {
775     GC_register_finalizer_inner(obj, fn, cd, ofn,
776                                 ocd, GC_ignore_self_finalize_mark_proc);
777 }
778
779 GC_API void GC_CALL GC_register_finalizer_no_order(void * obj,
780                                GC_finalization_proc fn, void * cd,
781                                GC_finalization_proc *ofn, void ** ocd)
782 {
783     GC_register_finalizer_inner(obj, fn, cd, ofn,
784                                 ocd, GC_null_finalize_mark_proc);
785 }
786
787 static GC_bool need_unreachable_finalization = FALSE;
788         /* Avoid the work if this isn't used.   */
789
790 GC_API void GC_CALL GC_register_finalizer_unreachable(void * obj,
791                                GC_finalization_proc fn, void * cd,
792                                GC_finalization_proc *ofn, void ** ocd)
793 {
794     need_unreachable_finalization = TRUE;
795     GC_ASSERT(GC_java_finalization);
796     GC_register_finalizer_inner(obj, fn, cd, ofn,
797                                 ocd, GC_unreachable_finalize_mark_proc);
798 }
799
800 #ifndef NO_DEBUGGING
801   STATIC void GC_dump_finalization_links(
802                                 const struct dl_hashtbl_s *dl_hashtbl)
803   {
804     size_t dl_size = dl_hashtbl->log_size == -1 ? 0 :
805                                 (size_t)1 << dl_hashtbl->log_size;
806     size_t i;
807
808     for (i = 0; i < dl_size; i++) {
809       struct disappearing_link *curr_dl;
810
811       for (curr_dl = dl_hashtbl -> head[i]; curr_dl != 0;
812            curr_dl = dl_next(curr_dl)) {
813         ptr_t real_ptr = GC_REVEAL_POINTER(curr_dl -> dl_hidden_obj);
814         ptr_t real_link = GC_REVEAL_POINTER(curr_dl -> dl_hidden_link);
815
816         GC_printf("Object: %p, link: %p\n",
817                   (void *)real_ptr, (void *)real_link);
818       }
819     }
820   }
821
822   GC_API void GC_CALL GC_dump_finalization(void)
823   {
824     struct finalizable_object * curr_fo;
825     size_t fo_size = log_fo_table_size == -1 ? 0 :
826                                 (size_t)1 << log_fo_table_size;
827     size_t i;
828
829     GC_printf("Disappearing (short) links:\n");
830     GC_dump_finalization_links(&GC_dl_hashtbl);
831 #   ifndef GC_LONG_REFS_NOT_NEEDED
832       GC_printf("Disappearing long links:\n");
833       GC_dump_finalization_links(&GC_ll_hashtbl);
834 #   endif
835     GC_printf("Finalizers:\n");
836     for (i = 0; i < fo_size; i++) {
837       for (curr_fo = GC_fnlz_roots.fo_head[i];
838            curr_fo != NULL; curr_fo = fo_next(curr_fo)) {
839         ptr_t real_ptr = GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
840
841         GC_printf("Finalizable object: %p\n", (void *)real_ptr);
842       }
843     }
844   }
845 #endif /* !NO_DEBUGGING */
846
847 #ifndef SMALL_CONFIG
848   STATIC word GC_old_dl_entries = 0; /* for stats printing */
849 # ifndef GC_LONG_REFS_NOT_NEEDED
850     STATIC word GC_old_ll_entries = 0;
851 # endif
852 #endif /* !SMALL_CONFIG */
853
854 #ifndef THREADS
855   /* Global variables to minimize the level of recursion when a client  */
856   /* finalizer allocates memory.                                        */
857   STATIC int GC_finalizer_nested = 0;
858                         /* Only the lowest byte is used, the rest is    */
859                         /* padding for proper global data alignment     */
860                         /* required for some compilers (like Watcom).   */
861   STATIC unsigned GC_finalizer_skipped = 0;
862
863   /* Checks and updates the level of finalizers recursion.              */
864   /* Returns NULL if GC_invoke_finalizers() should not be called by the */
865   /* collector (to minimize the risk of a deep finalizers recursion),   */
866   /* otherwise returns a pointer to GC_finalizer_nested.                */
867   STATIC unsigned char *GC_check_finalizer_nested(void)
868   {
869     unsigned nesting_level = *(unsigned char *)&GC_finalizer_nested;
870     if (nesting_level) {
871       /* We are inside another GC_invoke_finalizers().          */
872       /* Skip some implicitly-called GC_invoke_finalizers()     */
873       /* depending on the nesting (recursion) level.            */
874       if (++GC_finalizer_skipped < (1U << nesting_level)) return NULL;
875       GC_finalizer_skipped = 0;
876     }
877     *(char *)&GC_finalizer_nested = (char)(nesting_level + 1);
878     return (unsigned char *)&GC_finalizer_nested;
879   }
880 #endif /* THREADS */
881
882 #define ITERATE_DL_HASHTBL_BEGIN(dl_hashtbl, curr_dl, prev_dl) \
883   { \
884     size_t i; \
885     size_t dl_size = dl_hashtbl->log_size == -1 ? 0 : \
886                                 (size_t)1 << dl_hashtbl->log_size; \
887     for (i = 0; i < dl_size; i++) { \
888       struct disappearing_link *prev_dl = NULL; \
889       curr_dl = dl_hashtbl -> head[i]; \
890       while (curr_dl) {
891
892 #define ITERATE_DL_HASHTBL_END(curr_dl, prev_dl) \
893         prev_dl = curr_dl; \
894         curr_dl = dl_next(curr_dl); \
895       } \
896     } \
897   }
898
899 #define DELETE_DL_HASHTBL_ENTRY(dl_hashtbl, curr_dl, prev_dl, next_dl) \
900   { \
901     next_dl = dl_next(curr_dl); \
902     if (NULL == prev_dl) { \
903         dl_hashtbl -> head[i] = next_dl; \
904     } else { \
905         dl_set_next(prev_dl, next_dl); \
906     } \
907     GC_clear_mark_bit(curr_dl); \
908     dl_hashtbl -> entries--; \
909     curr_dl = next_dl; \
910     continue; \
911   }
912
913 GC_INLINE void GC_make_disappearing_links_disappear(
914                                 struct dl_hashtbl_s* dl_hashtbl)
915 {
916     struct disappearing_link *curr, *next;
917
918     ITERATE_DL_HASHTBL_BEGIN(dl_hashtbl, curr, prev)
919         ptr_t real_ptr = GC_REVEAL_POINTER(curr -> dl_hidden_obj);
920         ptr_t real_link = GC_REVEAL_POINTER(curr -> dl_hidden_link);
921
922         if (!GC_is_marked(real_ptr)) {
923             *(word *)real_link = 0;
924             GC_clear_mark_bit(curr);
925             DELETE_DL_HASHTBL_ENTRY(dl_hashtbl, curr, prev, next);
926         }
927     ITERATE_DL_HASHTBL_END(curr, prev)
928 }
929
930 GC_INLINE void GC_remove_dangling_disappearing_links(
931                                 struct dl_hashtbl_s* dl_hashtbl)
932 {
933     struct disappearing_link *curr, *next;
934
935     ITERATE_DL_HASHTBL_BEGIN(dl_hashtbl, curr, prev)
936         ptr_t real_link = GC_base(GC_REVEAL_POINTER(curr -> dl_hidden_link));
937
938         if (NULL != real_link && !GC_is_marked(real_link)) {
939             GC_clear_mark_bit(curr);
940             DELETE_DL_HASHTBL_ENTRY(dl_hashtbl, curr, prev, next);
941         }
942     ITERATE_DL_HASHTBL_END(curr, prev)
943 }
944
945 /* Called with held lock (but the world is running).                    */
946 /* Cause disappearing links to disappear and unreachable objects to be  */
947 /* enqueued for finalization.                                           */
948 GC_INNER void GC_finalize(void)
949 {
950     struct finalizable_object * curr_fo, * prev_fo, * next_fo;
951     ptr_t real_ptr;
952     size_t i;
953     size_t fo_size = log_fo_table_size == -1 ? 0 :
954                                 (size_t)1 << log_fo_table_size;
955
956 #   ifndef SMALL_CONFIG
957       /* Save current GC_[dl/ll]_entries value for stats printing */
958       GC_old_dl_entries = GC_dl_hashtbl.entries;
959 #     ifndef GC_LONG_REFS_NOT_NEEDED
960         GC_old_ll_entries = GC_ll_hashtbl.entries;
961 #     endif
962 #   endif
963
964 #   ifndef GC_TOGGLE_REFS_NOT_NEEDED
965       GC_mark_togglerefs();
966 #   endif
967     GC_make_disappearing_links_disappear(&GC_dl_hashtbl);
968
969   /* Mark all objects reachable via chains of 1 or more pointers        */
970   /* from finalizable objects.                                          */
971     GC_ASSERT(GC_mark_state == MS_NONE);
972     for (i = 0; i < fo_size; i++) {
973       for (curr_fo = GC_fnlz_roots.fo_head[i];
974            curr_fo != NULL; curr_fo = fo_next(curr_fo)) {
975         GC_ASSERT(GC_size(curr_fo) >= sizeof(struct finalizable_object));
976         real_ptr = GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
977         if (!GC_is_marked(real_ptr)) {
978             GC_MARKED_FOR_FINALIZATION(real_ptr);
979             GC_MARK_FO(real_ptr, curr_fo -> fo_mark_proc);
980             if (GC_is_marked(real_ptr)) {
981                 WARN("Finalization cycle involving %p\n", real_ptr);
982             }
983         }
984       }
985     }
986   /* Enqueue for finalization all objects that are still                */
987   /* unreachable.                                                       */
988     GC_bytes_finalized = 0;
989     for (i = 0; i < fo_size; i++) {
990       curr_fo = GC_fnlz_roots.fo_head[i];
991       prev_fo = 0;
992       while (curr_fo != 0) {
993         real_ptr = GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
994         if (!GC_is_marked(real_ptr)) {
995             if (!GC_java_finalization) {
996               GC_set_mark_bit(real_ptr);
997             }
998             /* Delete from hash table */
999               next_fo = fo_next(curr_fo);
1000               if (NULL == prev_fo) {
1001                 GC_fnlz_roots.fo_head[i] = next_fo;
1002               } else {
1003                 fo_set_next(prev_fo, next_fo);
1004               }
1005               GC_fo_entries--;
1006               if (GC_object_finalized_proc)
1007                 GC_object_finalized_proc(real_ptr);
1008
1009             /* Add to list of objects awaiting finalization.    */
1010               fo_set_next(curr_fo, GC_fnlz_roots.finalize_now);
1011               GC_fnlz_roots.finalize_now = curr_fo;
1012               /* unhide object pointer so any future collections will   */
1013               /* see it.                                                */
1014               curr_fo -> fo_hidden_base =
1015                         (word)GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
1016               GC_bytes_finalized +=
1017                         curr_fo -> fo_object_size
1018                         + sizeof(struct finalizable_object);
1019             GC_ASSERT(GC_is_marked(GC_base(curr_fo)));
1020             curr_fo = next_fo;
1021         } else {
1022             prev_fo = curr_fo;
1023             curr_fo = fo_next(curr_fo);
1024         }
1025       }
1026     }
1027
1028   if (GC_java_finalization) {
1029     /* make sure we mark everything reachable from objects finalized
1030        using the no_order mark_proc */
1031       for (curr_fo = GC_fnlz_roots.finalize_now;
1032            curr_fo != NULL; curr_fo = fo_next(curr_fo)) {
1033         real_ptr = (ptr_t)curr_fo -> fo_hidden_base;
1034         if (!GC_is_marked(real_ptr)) {
1035             if (curr_fo -> fo_mark_proc == GC_null_finalize_mark_proc) {
1036                 GC_MARK_FO(real_ptr, GC_normal_finalize_mark_proc);
1037             }
1038             if (curr_fo -> fo_mark_proc != GC_unreachable_finalize_mark_proc) {
1039                 GC_set_mark_bit(real_ptr);
1040             }
1041         }
1042       }
1043
1044     /* now revive finalize-when-unreachable objects reachable from
1045        other finalizable objects */
1046       if (need_unreachable_finalization) {
1047         curr_fo = GC_fnlz_roots.finalize_now;
1048         GC_ASSERT(NULL == curr_fo || log_fo_table_size >= 0);
1049         prev_fo = NULL;
1050         while (curr_fo != NULL) {
1051           next_fo = fo_next(curr_fo);
1052           if (curr_fo -> fo_mark_proc == GC_unreachable_finalize_mark_proc) {
1053             real_ptr = (ptr_t)curr_fo -> fo_hidden_base;
1054             if (!GC_is_marked(real_ptr)) {
1055               GC_set_mark_bit(real_ptr);
1056             } else {
1057               if (NULL == prev_fo) {
1058                 GC_fnlz_roots.finalize_now = next_fo;
1059               } else {
1060                 fo_set_next(prev_fo, next_fo);
1061               }
1062               curr_fo -> fo_hidden_base =
1063                                 GC_HIDE_POINTER(curr_fo -> fo_hidden_base);
1064               GC_bytes_finalized -=
1065                   curr_fo->fo_object_size + sizeof(struct finalizable_object);
1066
1067               i = HASH2(real_ptr, log_fo_table_size);
1068               fo_set_next(curr_fo, GC_fnlz_roots.fo_head[i]);
1069               GC_fo_entries++;
1070               GC_fnlz_roots.fo_head[i] = curr_fo;
1071               curr_fo = prev_fo;
1072             }
1073           }
1074           prev_fo = curr_fo;
1075           curr_fo = next_fo;
1076         }
1077       }
1078   }
1079
1080   GC_remove_dangling_disappearing_links(&GC_dl_hashtbl);
1081 # ifndef GC_TOGGLE_REFS_NOT_NEEDED
1082     GC_clear_togglerefs();
1083 # endif
1084 # ifndef GC_LONG_REFS_NOT_NEEDED
1085     GC_make_disappearing_links_disappear(&GC_ll_hashtbl);
1086     GC_remove_dangling_disappearing_links(&GC_ll_hashtbl);
1087 # endif
1088
1089   if (GC_fail_count) {
1090     /* Don't prevent running finalizers if there has been an allocation */
1091     /* failure recently.                                                */
1092 #   ifdef THREADS
1093       GC_reset_finalizer_nested();
1094 #   else
1095       GC_finalizer_nested = 0;
1096 #   endif
1097   }
1098 }
1099
1100 #ifndef JAVA_FINALIZATION_NOT_NEEDED
1101
1102   /* Enqueue all remaining finalizers to be run - Assumes lock is held. */
1103   STATIC void GC_enqueue_all_finalizers(void)
1104   {
1105     struct finalizable_object * curr_fo, * next_fo;
1106     ptr_t real_ptr;
1107     int i;
1108     int fo_size;
1109
1110     fo_size = log_fo_table_size == -1 ? 0 : 1 << log_fo_table_size;
1111     GC_bytes_finalized = 0;
1112     for (i = 0; i < fo_size; i++) {
1113       curr_fo = GC_fnlz_roots.fo_head[i];
1114       GC_fnlz_roots.fo_head[i] = NULL;
1115       while (curr_fo != NULL) {
1116           real_ptr = GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
1117           GC_MARK_FO(real_ptr, GC_normal_finalize_mark_proc);
1118           GC_set_mark_bit(real_ptr);
1119
1120           next_fo = fo_next(curr_fo);
1121
1122           /* Add to list of objects awaiting finalization.      */
1123           fo_set_next(curr_fo, GC_fnlz_roots.finalize_now);
1124           GC_fnlz_roots.finalize_now = curr_fo;
1125
1126           /* unhide object pointer so any future collections will       */
1127           /* see it.                                                    */
1128           curr_fo -> fo_hidden_base =
1129                         (word)GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
1130           GC_bytes_finalized +=
1131                 curr_fo -> fo_object_size + sizeof(struct finalizable_object);
1132           curr_fo = next_fo;
1133         }
1134     }
1135     GC_fo_entries = 0;  /* all entries deleted from the hash table */
1136   }
1137
1138   /* Invoke all remaining finalizers that haven't yet been run.
1139    * This is needed for strict compliance with the Java standard,
1140    * which can make the runtime guarantee that all finalizers are run.
1141    * Unfortunately, the Java standard implies we have to keep running
1142    * finalizers until there are no more left, a potential infinite loop.
1143    * YUCK.
1144    * Note that this is even more dangerous than the usual Java
1145    * finalizers, in that objects reachable from static variables
1146    * may have been finalized when these finalizers are run.
1147    * Finalizers run at this point must be prepared to deal with a
1148    * mostly broken world.
1149    * This routine is externally callable, so is called without
1150    * the allocation lock.
1151    */
1152   GC_API void GC_CALL GC_finalize_all(void)
1153   {
1154     DCL_LOCK_STATE;
1155
1156     LOCK();
1157     while (GC_fo_entries > 0) {
1158       GC_enqueue_all_finalizers();
1159       UNLOCK();
1160       GC_invoke_finalizers();
1161       /* Running the finalizers in this thread is arguably not a good   */
1162       /* idea when we should be notifying another thread to run them.   */
1163       /* But otherwise we don't have a great way to wait for them to    */
1164       /* run.                                                           */
1165       LOCK();
1166     }
1167     UNLOCK();
1168   }
1169
1170 #endif /* !JAVA_FINALIZATION_NOT_NEEDED */
1171
1172 /* Returns true if it is worth calling GC_invoke_finalizers. (Useful if */
1173 /* finalizers can only be called from some kind of "safe state" and     */
1174 /* getting into that safe state is expensive.)                          */
1175 GC_API int GC_CALL GC_should_invoke_finalizers(void)
1176 {
1177   return GC_fnlz_roots.finalize_now != NULL;
1178 }
1179
1180 /* Invoke finalizers for all objects that are ready to be finalized.    */
1181 /* Should be called without allocation lock.                            */
1182 GC_API int GC_CALL GC_invoke_finalizers(void)
1183 {
1184     int count = 0;
1185     word bytes_freed_before = 0; /* initialized to prevent warning. */
1186     DCL_LOCK_STATE;
1187
1188     while (GC_fnlz_roots.finalize_now != NULL) {
1189         struct finalizable_object * curr_fo;
1190
1191 #       ifdef THREADS
1192             LOCK();
1193 #       endif
1194         if (count == 0) {
1195             bytes_freed_before = GC_bytes_freed;
1196             /* Don't do this outside, since we need the lock. */
1197         }
1198         curr_fo = GC_fnlz_roots.finalize_now;
1199 #       ifdef THREADS
1200             if (curr_fo != 0) GC_fnlz_roots.finalize_now = fo_next(curr_fo);
1201             UNLOCK();
1202             if (curr_fo == 0) break;
1203 #       else
1204             GC_fnlz_roots.finalize_now = fo_next(curr_fo);
1205 #       endif
1206         fo_set_next(curr_fo, 0);
1207         (*(curr_fo -> fo_fn))((ptr_t)(curr_fo -> fo_hidden_base),
1208                               curr_fo -> fo_client_data);
1209         curr_fo -> fo_client_data = 0;
1210         ++count;
1211         /* Explicit freeing of curr_fo is probably a bad idea.  */
1212         /* It throws off accounting if nearly all objects are   */
1213         /* finalizable.  Otherwise it should not matter.        */
1214     }
1215     /* bytes_freed_before is initialized whenever count != 0 */
1216     if (count != 0 && bytes_freed_before != GC_bytes_freed) {
1217         LOCK();
1218         GC_finalizer_bytes_freed += (GC_bytes_freed - bytes_freed_before);
1219         UNLOCK();
1220     }
1221     return count;
1222 }
1223
1224 static word last_finalizer_notification = 0;
1225
1226 GC_INNER void GC_notify_or_invoke_finalizers(void)
1227 {
1228     GC_finalizer_notifier_proc notifier_fn = 0;
1229 #   if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH)
1230       static word last_back_trace_gc_no = 1;    /* Skip first one. */
1231 #   endif
1232     DCL_LOCK_STATE;
1233
1234 #   if defined(THREADS) && !defined(KEEP_BACK_PTRS) \
1235        && !defined(MAKE_BACK_GRAPH)
1236       /* Quick check (while unlocked) for an empty finalization queue.  */
1237       if (NULL == GC_fnlz_roots.finalize_now) return;
1238 #   endif
1239     LOCK();
1240
1241     /* This is a convenient place to generate backtraces if appropriate, */
1242     /* since that code is not callable with the allocation lock.         */
1243 #   if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH)
1244       if (GC_gc_no > last_back_trace_gc_no) {
1245 #       ifdef KEEP_BACK_PTRS
1246           long i;
1247           /* Stops when GC_gc_no wraps; that's OK.      */
1248           last_back_trace_gc_no = (word)(-1);  /* disable others. */
1249           for (i = 0; i < GC_backtraces; ++i) {
1250               /* FIXME: This tolerates concurrent heap mutation,        */
1251               /* which may cause occasional mysterious results.         */
1252               /* We need to release the GC lock, since GC_print_callers */
1253               /* acquires it.  It probably shouldn't.                   */
1254               UNLOCK();
1255               GC_generate_random_backtrace_no_gc();
1256               LOCK();
1257           }
1258           last_back_trace_gc_no = GC_gc_no;
1259 #       endif
1260 #       ifdef MAKE_BACK_GRAPH
1261           if (GC_print_back_height) {
1262             UNLOCK();
1263             GC_print_back_graph_stats();
1264             LOCK();
1265           }
1266 #       endif
1267       }
1268 #   endif
1269     if (NULL == GC_fnlz_roots.finalize_now) {
1270       UNLOCK();
1271       return;
1272     }
1273
1274     if (!GC_finalize_on_demand) {
1275       unsigned char *pnested = GC_check_finalizer_nested();
1276       UNLOCK();
1277       /* Skip GC_invoke_finalizers() if nested */
1278       if (pnested != NULL) {
1279         (void) GC_invoke_finalizers();
1280         *pnested = 0; /* Reset since no more finalizers. */
1281 #       ifndef THREADS
1282           GC_ASSERT(NULL == GC_fnlz_roots.finalize_now);
1283 #       endif   /* Otherwise GC can run concurrently and add more */
1284       }
1285       return;
1286     }
1287
1288     /* These variables require synchronization to avoid data races.     */
1289     if (last_finalizer_notification != GC_gc_no) {
1290         last_finalizer_notification = GC_gc_no;
1291         notifier_fn = GC_finalizer_notifier;
1292     }
1293     UNLOCK();
1294     if (notifier_fn != 0)
1295         (*notifier_fn)(); /* Invoke the notifier */
1296 }
1297
1298 #ifndef SMALL_CONFIG
1299 # ifndef GC_LONG_REFS_NOT_NEEDED
1300 #   define IF_LONG_REFS_PRESENT_ELSE(x,y) (x)
1301 # else
1302 #   define IF_LONG_REFS_PRESENT_ELSE(x,y) (y)
1303 # endif
1304
1305   GC_INNER void GC_print_finalization_stats(void)
1306   {
1307     struct finalizable_object *fo;
1308     unsigned long ready = 0;
1309
1310     GC_log_printf("%lu finalization entries;"
1311                   " %lu/%lu short/long disappearing links alive\n",
1312                   (unsigned long)GC_fo_entries,
1313                   (unsigned long)GC_dl_hashtbl.entries,
1314                   (unsigned long)IF_LONG_REFS_PRESENT_ELSE(
1315                                                 GC_ll_hashtbl.entries, 0));
1316
1317     for (fo = GC_fnlz_roots.finalize_now; fo != NULL; fo = fo_next(fo))
1318       ++ready;
1319     GC_log_printf("%lu finalization-ready objects;"
1320                   " %ld/%ld short/long links cleared\n",
1321                   ready,
1322                   (long)GC_old_dl_entries - (long)GC_dl_hashtbl.entries,
1323                   (long)IF_LONG_REFS_PRESENT_ELSE(
1324                               GC_old_ll_entries - GC_ll_hashtbl.entries, 0));
1325   }
1326 #endif /* !SMALL_CONFIG */
1327
1328 #endif /* !GC_NO_FINALIZATION */