implemented Setup.hs to build boehm cpp libs and install them;
[hs-boehmgc.git] / gc-7.2 / 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 #ifdef FINALIZE_ON_DEMAND
20   int GC_finalize_on_demand = 1;
21 #else
22   int GC_finalize_on_demand = 0;
23 #endif
24
25 #ifdef JAVA_FINALIZATION
26   int GC_java_finalization = 1;
27 #else
28   int GC_java_finalization = 0;
29 #endif
30
31 /* Type of mark procedure used for marking from finalizable object.     */
32 /* This procedure normally does not mark the object, only its           */
33 /* descendents.                                                         */
34 typedef void (* finalization_mark_proc)(ptr_t /* finalizable_obj_ptr */);
35
36 #define HASH3(addr,size,log_size) \
37         ((((word)(addr) >> 3) ^ ((word)(addr) >> (3 + (log_size)))) \
38          & ((size) - 1))
39 #define HASH2(addr,log_size) HASH3(addr, 1 << log_size, log_size)
40
41 struct hash_chain_entry {
42     word hidden_key;
43     struct hash_chain_entry * next;
44 };
45
46 static struct disappearing_link {
47     struct hash_chain_entry prolog;
48 #   define dl_hidden_link prolog.hidden_key
49                                 /* Field to be cleared.         */
50 #   define dl_next(x) (struct disappearing_link *)((x) -> prolog.next)
51 #   define dl_set_next(x,y) (x)->prolog.next = (struct hash_chain_entry *)(y)
52
53     word dl_hidden_obj;         /* Pointer to object base       */
54 } **dl_head = 0;
55
56 static signed_word log_dl_table_size = -1;
57                         /* Binary log of                                */
58                         /* current size of array pointed to by dl_head. */
59                         /* -1 ==> size is 0.                            */
60
61 STATIC word GC_dl_entries = 0;
62                         /* Number of entries currently in disappearing  */
63                         /* link table.                                  */
64
65 static struct finalizable_object {
66     struct hash_chain_entry prolog;
67 #   define fo_hidden_base prolog.hidden_key
68                                 /* Pointer to object base.      */
69                                 /* No longer hidden once object */
70                                 /* is on finalize_now queue.    */
71 #   define fo_next(x) (struct finalizable_object *)((x) -> prolog.next)
72 #   define fo_set_next(x,y) (x)->prolog.next = (struct hash_chain_entry *)(y)
73     GC_finalization_proc fo_fn; /* Finalizer.                   */
74     ptr_t fo_client_data;
75     word fo_object_size;        /* In bytes.                    */
76     finalization_mark_proc fo_mark_proc;        /* Mark-through procedure */
77 } **fo_head = 0;
78
79 STATIC struct finalizable_object * GC_finalize_now = 0;
80         /* List of objects that should be finalized now.        */
81
82 static signed_word log_fo_table_size = -1;
83
84 word GC_fo_entries = 0; /* used also in extra/MacOS.c */
85
86 GC_INNER void GC_push_finalizer_structures(void)
87 {
88     GC_push_all((ptr_t)(&dl_head), (ptr_t)(&dl_head) + sizeof(word));
89     GC_push_all((ptr_t)(&fo_head), (ptr_t)(&fo_head) + sizeof(word));
90     GC_push_all((ptr_t)(&GC_finalize_now),
91                 (ptr_t)(&GC_finalize_now) + sizeof(word));
92 }
93
94 /* Double the size of a hash table. *size_ptr is the log of its current */
95 /* size.  May be a no-op.                                               */
96 /* *table is a pointer to an array of hash headers.  If we succeed, we  */
97 /* update both *table and *log_size_ptr.                                */
98 /* Lock is held.                                                        */
99 STATIC void GC_grow_table(struct hash_chain_entry ***table,
100                           signed_word *log_size_ptr)
101 {
102     register word i;
103     register struct hash_chain_entry *p;
104     signed_word log_old_size = *log_size_ptr;
105     signed_word log_new_size = log_old_size + 1;
106     word old_size = ((log_old_size == -1)? 0: (1 << log_old_size));
107     word new_size = (word)1 << log_new_size;
108     /* FIXME: Power of 2 size often gets rounded up to one more page. */
109     struct hash_chain_entry **new_table = (struct hash_chain_entry **)
110         GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE(
111                 (size_t)new_size * sizeof(struct hash_chain_entry *), NORMAL);
112
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((void *)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 GC_API int GC_CALL GC_general_register_disappearing_link(void * * link,
147                                                          void * obj)
148 {
149     struct disappearing_link *curr_dl;
150     size_t index;
151     struct disappearing_link * new_dl;
152     DCL_LOCK_STATE;
153
154     if (((word)link & (ALIGNMENT-1)) || link == NULL)
155         ABORT("Bad arg to GC_general_register_disappearing_link");
156     LOCK();
157     GC_ASSERT(obj != NULL && GC_base(obj) == obj);
158     if (log_dl_table_size == -1
159         || GC_dl_entries > ((word)1 << log_dl_table_size)) {
160         GC_grow_table((struct hash_chain_entry ***)(&dl_head),
161                       &log_dl_table_size);
162         if (GC_print_stats) {
163             GC_log_printf("Grew dl table to %u entries\n",
164                       (1 << (unsigned)log_dl_table_size));
165         }
166     }
167     index = HASH2(link, log_dl_table_size);
168     for (curr_dl = dl_head[index]; curr_dl != 0; curr_dl = dl_next(curr_dl)) {
169         if (curr_dl -> dl_hidden_link == GC_HIDE_POINTER(link)) {
170             curr_dl -> dl_hidden_obj = GC_HIDE_POINTER(obj);
171             UNLOCK();
172             return GC_DUPLICATE;
173         }
174     }
175     new_dl = (struct disappearing_link *)
176         GC_INTERNAL_MALLOC(sizeof(struct disappearing_link),NORMAL);
177     if (0 == new_dl) {
178       GC_oom_func oom_fn = GC_oom_fn;
179       UNLOCK();
180       new_dl = (struct disappearing_link *)
181                 (*oom_fn)(sizeof(struct disappearing_link));
182       if (0 == new_dl) {
183         return GC_NO_MEMORY;
184       }
185       /* It's not likely we'll make it here, but ... */
186       LOCK();
187       /* Recalculate index since the table may grow.    */
188       index = HASH2(link, log_dl_table_size);
189       /* Check again that our disappearing link not in the table. */
190       for (curr_dl = dl_head[index]; curr_dl != 0;
191            curr_dl = dl_next(curr_dl)) {
192         if (curr_dl -> dl_hidden_link == GC_HIDE_POINTER(link)) {
193           curr_dl -> dl_hidden_obj = GC_HIDE_POINTER(obj);
194           UNLOCK();
195 #         ifndef DBG_HDRS_ALL
196             /* Free unused new_dl returned by GC_oom_fn() */
197             GC_free((void *)new_dl);
198 #         endif
199           return GC_DUPLICATE;
200         }
201       }
202     }
203     new_dl -> dl_hidden_obj = GC_HIDE_POINTER(obj);
204     new_dl -> dl_hidden_link = GC_HIDE_POINTER(link);
205     dl_set_next(new_dl, dl_head[index]);
206     dl_head[index] = new_dl;
207     GC_dl_entries++;
208     UNLOCK();
209     return GC_SUCCESS;
210 }
211
212 GC_API int GC_CALL GC_unregister_disappearing_link(void * * link)
213 {
214     struct disappearing_link *curr_dl, *prev_dl;
215     size_t index;
216     DCL_LOCK_STATE;
217
218     if (((word)link & (ALIGNMENT-1)) != 0) return(0); /* Nothing to do. */
219
220     LOCK();
221     index = HASH2(link, log_dl_table_size);
222     prev_dl = 0; curr_dl = dl_head[index];
223     while (curr_dl != 0) {
224         if (curr_dl -> dl_hidden_link == GC_HIDE_POINTER(link)) {
225             if (prev_dl == 0) {
226                 dl_head[index] = dl_next(curr_dl);
227             } else {
228                 dl_set_next(prev_dl, dl_next(curr_dl));
229             }
230             GC_dl_entries--;
231             UNLOCK();
232 #           ifdef DBG_HDRS_ALL
233               dl_set_next(curr_dl, 0);
234 #           else
235               GC_free((void *)curr_dl);
236 #           endif
237             return(1);
238         }
239         prev_dl = curr_dl;
240         curr_dl = dl_next(curr_dl);
241     }
242     UNLOCK();
243     return(0);
244 }
245
246 /* Possible finalization_marker procedures.  Note that mark stack       */
247 /* overflow is handled by the caller, and is not a disaster.            */
248 STATIC void GC_normal_finalize_mark_proc(ptr_t p)
249 {
250     hdr * hhdr = HDR(p);
251
252     PUSH_OBJ(p, hhdr, GC_mark_stack_top,
253              &(GC_mark_stack[GC_mark_stack_size]));
254 }
255
256 /* This only pays very partial attention to the mark descriptor.        */
257 /* It does the right thing for normal and atomic objects, and treats    */
258 /* most others as normal.                                               */
259 STATIC void GC_ignore_self_finalize_mark_proc(ptr_t p)
260 {
261     hdr * hhdr = HDR(p);
262     word descr = hhdr -> hb_descr;
263     ptr_t q;
264     word r;
265     ptr_t scan_limit;
266     ptr_t target_limit = p + hhdr -> hb_sz - 1;
267
268     if ((descr & GC_DS_TAGS) == GC_DS_LENGTH) {
269        scan_limit = p + descr - sizeof(word);
270     } else {
271        scan_limit = target_limit + 1 - sizeof(word);
272     }
273     for (q = p; q <= scan_limit; q += ALIGNMENT) {
274         r = *(word *)q;
275         if ((ptr_t)r < p || (ptr_t)r > target_limit) {
276             GC_PUSH_ONE_HEAP(r, q);
277         }
278     }
279 }
280
281 /*ARGSUSED*/
282 STATIC void GC_null_finalize_mark_proc(ptr_t p) {}
283
284 /* Possible finalization_marker procedures.  Note that mark stack       */
285 /* overflow is handled by the caller, and is not a disaster.            */
286
287 /* GC_unreachable_finalize_mark_proc is an alias for normal marking,    */
288 /* but it is explicitly tested for, and triggers different              */
289 /* behavior.  Objects registered in this way are not finalized          */
290 /* if they are reachable by other finalizable objects, even if those    */
291 /* other objects specify no ordering.                                   */
292 STATIC void GC_unreachable_finalize_mark_proc(ptr_t p)
293 {
294     GC_normal_finalize_mark_proc(p);
295 }
296
297 /* Register a finalization function.  See gc.h for details.     */
298 /* The last parameter is a procedure that determines            */
299 /* marking for finalization ordering.  Any objects marked       */
300 /* by that procedure will be guaranteed to not have been        */
301 /* finalized when this finalizer is invoked.                    */
302 STATIC void GC_register_finalizer_inner(void * obj,
303                                         GC_finalization_proc fn, void *cd,
304                                         GC_finalization_proc *ofn, void **ocd,
305                                         finalization_mark_proc mp)
306 {
307     ptr_t base;
308     struct finalizable_object * curr_fo, * prev_fo;
309     size_t index;
310     struct finalizable_object *new_fo = 0;
311     hdr *hhdr = NULL; /* initialized to prevent warning. */
312     GC_oom_func oom_fn;
313     DCL_LOCK_STATE;
314
315     LOCK();
316     if (log_fo_table_size == -1
317         || GC_fo_entries > ((word)1 << log_fo_table_size)) {
318         GC_grow_table((struct hash_chain_entry ***)(&fo_head),
319                       &log_fo_table_size);
320         if (GC_print_stats) {
321             GC_log_printf("Grew fo table to %u entries\n",
322                           (1 << (unsigned)log_fo_table_size));
323         }
324     }
325     /* in the THREADS case we hold allocation lock.             */
326     base = (ptr_t)obj;
327     for (;;) {
328       index = HASH2(base, log_fo_table_size);
329       prev_fo = 0; curr_fo = fo_head[index];
330       while (curr_fo != 0) {
331         GC_ASSERT(GC_size(curr_fo) >= sizeof(struct finalizable_object));
332         if (curr_fo -> fo_hidden_base == GC_HIDE_POINTER(base)) {
333           /* Interruption by a signal in the middle of this     */
334           /* should be safe.  The client may see only *ocd      */
335           /* updated, but we'll declare that to be his problem. */
336           if (ocd) *ocd = (void *) (curr_fo -> fo_client_data);
337           if (ofn) *ofn = curr_fo -> fo_fn;
338           /* Delete the structure for base. */
339           if (prev_fo == 0) {
340             fo_head[index] = fo_next(curr_fo);
341           } else {
342             fo_set_next(prev_fo, fo_next(curr_fo));
343           }
344           if (fn == 0) {
345             GC_fo_entries--;
346             /* May not happen if we get a signal.  But a high   */
347             /* estimate will only make the table larger than    */
348             /* necessary.                                       */
349 #           if !defined(THREADS) && !defined(DBG_HDRS_ALL)
350               GC_free((void *)curr_fo);
351 #           endif
352           } else {
353             curr_fo -> fo_fn = fn;
354             curr_fo -> fo_client_data = (ptr_t)cd;
355             curr_fo -> fo_mark_proc = mp;
356             /* Reinsert it.  We deleted it first to maintain    */
357             /* consistency in the event of a signal.            */
358             if (prev_fo == 0) {
359               fo_head[index] = curr_fo;
360             } else {
361               fo_set_next(prev_fo, curr_fo);
362             }
363           }
364           UNLOCK();
365 #         ifndef DBG_HDRS_ALL
366             if (EXPECT(new_fo != 0, FALSE)) {
367               /* Free unused new_fo returned by GC_oom_fn() */
368               GC_free((void *)new_fo);
369             }
370 #         endif
371           return;
372         }
373         prev_fo = curr_fo;
374         curr_fo = fo_next(curr_fo);
375       }
376       if (EXPECT(new_fo != 0, FALSE)) {
377         /* new_fo is returned by GC_oom_fn(), so fn != 0 and hhdr != 0. */
378         break;
379       }
380       if (fn == 0) {
381         if (ocd) *ocd = 0;
382         if (ofn) *ofn = 0;
383         UNLOCK();
384         return;
385       }
386       GET_HDR(base, hhdr);
387       if (EXPECT(0 == hhdr, FALSE)) {
388         /* We won't collect it, hence finalizer wouldn't be run. */
389         if (ocd) *ocd = 0;
390         if (ofn) *ofn = 0;
391         UNLOCK();
392         return;
393       }
394       new_fo = (struct finalizable_object *)
395         GC_INTERNAL_MALLOC(sizeof(struct finalizable_object),NORMAL);
396       if (EXPECT(new_fo != 0, TRUE))
397         break;
398       oom_fn = GC_oom_fn;
399       UNLOCK();
400       new_fo = (struct finalizable_object *)
401                 (*oom_fn)(sizeof(struct finalizable_object));
402       if (0 == new_fo) {
403         /* No enough memory.  *ocd and *ofn remains unchanged.  */
404         return;
405       }
406       /* It's not likely we'll make it here, but ... */
407       LOCK();
408       /* Recalculate index since the table may grow and         */
409       /* check again that our finalizer is not in the table.    */
410     }
411     GC_ASSERT(GC_size(new_fo) >= sizeof(struct finalizable_object));
412     if (ocd) *ocd = 0;
413     if (ofn) *ofn = 0;
414     new_fo -> fo_hidden_base = GC_HIDE_POINTER(base);
415     new_fo -> fo_fn = fn;
416     new_fo -> fo_client_data = (ptr_t)cd;
417     new_fo -> fo_object_size = hhdr -> hb_sz;
418     new_fo -> fo_mark_proc = mp;
419     fo_set_next(new_fo, fo_head[index]);
420     GC_fo_entries++;
421     fo_head[index] = new_fo;
422     UNLOCK();
423 }
424
425 GC_API void GC_CALL GC_register_finalizer(void * obj,
426                                   GC_finalization_proc fn, void * cd,
427                                   GC_finalization_proc *ofn, void ** ocd)
428 {
429     GC_register_finalizer_inner(obj, fn, cd, ofn,
430                                 ocd, GC_normal_finalize_mark_proc);
431 }
432
433 GC_API void GC_CALL GC_register_finalizer_ignore_self(void * obj,
434                                GC_finalization_proc fn, void * cd,
435                                GC_finalization_proc *ofn, void ** ocd)
436 {
437     GC_register_finalizer_inner(obj, fn, cd, ofn,
438                                 ocd, GC_ignore_self_finalize_mark_proc);
439 }
440
441 GC_API void GC_CALL GC_register_finalizer_no_order(void * obj,
442                                GC_finalization_proc fn, void * cd,
443                                GC_finalization_proc *ofn, void ** ocd)
444 {
445     GC_register_finalizer_inner(obj, fn, cd, ofn,
446                                 ocd, GC_null_finalize_mark_proc);
447 }
448
449 static GC_bool need_unreachable_finalization = FALSE;
450         /* Avoid the work if this isn't used.   */
451
452 GC_API void GC_CALL GC_register_finalizer_unreachable(void * obj,
453                                GC_finalization_proc fn, void * cd,
454                                GC_finalization_proc *ofn, void ** ocd)
455 {
456     need_unreachable_finalization = TRUE;
457     GC_ASSERT(GC_java_finalization);
458     GC_register_finalizer_inner(obj, fn, cd, ofn,
459                                 ocd, GC_unreachable_finalize_mark_proc);
460 }
461
462 #ifndef NO_DEBUGGING
463   void GC_dump_finalization(void)
464   {
465     struct disappearing_link * curr_dl;
466     struct finalizable_object * curr_fo;
467     ptr_t real_ptr, real_link;
468     int dl_size = (log_dl_table_size == -1 ) ? 0 : (1 << log_dl_table_size);
469     int fo_size = (log_fo_table_size == -1 ) ? 0 : (1 << log_fo_table_size);
470     int i;
471
472     GC_printf("Disappearing links:\n");
473     for (i = 0; i < dl_size; i++) {
474       for (curr_dl = dl_head[i]; curr_dl != 0; curr_dl = dl_next(curr_dl)) {
475         real_ptr = GC_REVEAL_POINTER(curr_dl -> dl_hidden_obj);
476         real_link = GC_REVEAL_POINTER(curr_dl -> dl_hidden_link);
477         GC_printf("Object: %p, Link:%p\n", real_ptr, real_link);
478       }
479     }
480     GC_printf("Finalizers:\n");
481     for (i = 0; i < fo_size; i++) {
482       for (curr_fo = fo_head[i]; curr_fo != 0; curr_fo = fo_next(curr_fo)) {
483         real_ptr = GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
484         GC_printf("Finalizable object: %p\n", real_ptr);
485       }
486     }
487   }
488 #endif /* !NO_DEBUGGING */
489
490 #ifndef SMALL_CONFIG
491   STATIC word GC_old_dl_entries = 0; /* for stats printing */
492 #endif
493
494 #ifndef THREADS
495   /* Global variables to minimize the level of recursion when a client  */
496   /* finalizer allocates memory.                                        */
497   STATIC int GC_finalizer_nested = 0;
498                         /* Only the lowest byte is used, the rest is    */
499                         /* padding for proper global data alignment     */
500                         /* required for some compilers (like Watcom).   */
501   STATIC unsigned GC_finalizer_skipped = 0;
502
503   /* Checks and updates the level of finalizers recursion.              */
504   /* Returns NULL if GC_invoke_finalizers() should not be called by the */
505   /* collector (to minimize the risk of a deep finalizers recursion),   */
506   /* otherwise returns a pointer to GC_finalizer_nested.                */
507   STATIC unsigned char *GC_check_finalizer_nested(void)
508   {
509     unsigned nesting_level = *(unsigned char *)&GC_finalizer_nested;
510     if (nesting_level) {
511       /* We are inside another GC_invoke_finalizers().          */
512       /* Skip some implicitly-called GC_invoke_finalizers()     */
513       /* depending on the nesting (recursion) level.            */
514       if (++GC_finalizer_skipped < (1U << nesting_level)) return NULL;
515       GC_finalizer_skipped = 0;
516     }
517     *(char *)&GC_finalizer_nested = (char)(nesting_level + 1);
518     return (unsigned char *)&GC_finalizer_nested;
519   }
520 #endif /* THREADS */
521
522 /* Called with held lock (but the world is running).                    */
523 /* Cause disappearing links to disappear and unreachable objects to be  */
524 /* enqueued for finalization.                                           */
525 GC_INNER void GC_finalize(void)
526 {
527     struct disappearing_link * curr_dl, * prev_dl, * next_dl;
528     struct finalizable_object * curr_fo, * prev_fo, * next_fo;
529     ptr_t real_ptr, real_link;
530     size_t i;
531     size_t dl_size = (log_dl_table_size == -1 ) ? 0 : (1 << log_dl_table_size);
532     size_t fo_size = (log_fo_table_size == -1 ) ? 0 : (1 << log_fo_table_size);
533
534 #   ifndef SMALL_CONFIG
535       /* Save current GC_dl_entries value for stats printing */
536       GC_old_dl_entries = GC_dl_entries;
537 #   endif
538
539   /* Make disappearing links disappear */
540     for (i = 0; i < dl_size; i++) {
541       curr_dl = dl_head[i];
542       prev_dl = 0;
543       while (curr_dl != 0) {
544         real_ptr = GC_REVEAL_POINTER(curr_dl -> dl_hidden_obj);
545         real_link = GC_REVEAL_POINTER(curr_dl -> dl_hidden_link);
546         if (!GC_is_marked(real_ptr)) {
547             *(word *)real_link = 0;
548             next_dl = dl_next(curr_dl);
549             if (prev_dl == 0) {
550                 dl_head[i] = next_dl;
551             } else {
552                 dl_set_next(prev_dl, next_dl);
553             }
554             GC_clear_mark_bit((ptr_t)curr_dl);
555             GC_dl_entries--;
556             curr_dl = next_dl;
557         } else {
558             prev_dl = curr_dl;
559             curr_dl = dl_next(curr_dl);
560         }
561       }
562     }
563   /* Mark all objects reachable via chains of 1 or more pointers        */
564   /* from finalizable objects.                                          */
565     GC_ASSERT(GC_mark_state == MS_NONE);
566     for (i = 0; i < fo_size; i++) {
567       for (curr_fo = fo_head[i]; curr_fo != 0; curr_fo = fo_next(curr_fo)) {
568         GC_ASSERT(GC_size(curr_fo) >= sizeof(struct finalizable_object));
569         real_ptr = GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
570         if (!GC_is_marked(real_ptr)) {
571             GC_MARKED_FOR_FINALIZATION(real_ptr);
572             GC_MARK_FO(real_ptr, curr_fo -> fo_mark_proc);
573             if (GC_is_marked(real_ptr)) {
574                 WARN("Finalization cycle involving %p\n", real_ptr);
575             }
576         }
577       }
578     }
579   /* Enqueue for finalization all objects that are still                */
580   /* unreachable.                                                       */
581     GC_bytes_finalized = 0;
582     for (i = 0; i < fo_size; i++) {
583       curr_fo = fo_head[i];
584       prev_fo = 0;
585       while (curr_fo != 0) {
586         real_ptr = GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
587         if (!GC_is_marked(real_ptr)) {
588             if (!GC_java_finalization) {
589               GC_set_mark_bit(real_ptr);
590             }
591             /* Delete from hash table */
592               next_fo = fo_next(curr_fo);
593               if (prev_fo == 0) {
594                 fo_head[i] = next_fo;
595               } else {
596                 fo_set_next(prev_fo, next_fo);
597               }
598               GC_fo_entries--;
599             /* Add to list of objects awaiting finalization.    */
600               fo_set_next(curr_fo, GC_finalize_now);
601               GC_finalize_now = curr_fo;
602               /* unhide object pointer so any future collections will   */
603               /* see it.                                                */
604               curr_fo -> fo_hidden_base =
605                         (word)GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
606               GC_bytes_finalized +=
607                         curr_fo -> fo_object_size
608                         + sizeof(struct finalizable_object);
609             GC_ASSERT(GC_is_marked(GC_base((ptr_t)curr_fo)));
610             curr_fo = next_fo;
611         } else {
612             prev_fo = curr_fo;
613             curr_fo = fo_next(curr_fo);
614         }
615       }
616     }
617
618   if (GC_java_finalization) {
619     /* make sure we mark everything reachable from objects finalized
620        using the no_order mark_proc */
621       for (curr_fo = GC_finalize_now;
622          curr_fo != NULL; curr_fo = fo_next(curr_fo)) {
623         real_ptr = (ptr_t)curr_fo -> fo_hidden_base;
624         if (!GC_is_marked(real_ptr)) {
625             if (curr_fo -> fo_mark_proc == GC_null_finalize_mark_proc) {
626                 GC_MARK_FO(real_ptr, GC_normal_finalize_mark_proc);
627             }
628             if (curr_fo -> fo_mark_proc != GC_unreachable_finalize_mark_proc) {
629                 GC_set_mark_bit(real_ptr);
630             }
631         }
632       }
633
634     /* now revive finalize-when-unreachable objects reachable from
635        other finalizable objects */
636       if (need_unreachable_finalization) {
637         curr_fo = GC_finalize_now;
638         prev_fo = 0;
639         while (curr_fo != 0) {
640           next_fo = fo_next(curr_fo);
641           if (curr_fo -> fo_mark_proc == GC_unreachable_finalize_mark_proc) {
642             real_ptr = (ptr_t)curr_fo -> fo_hidden_base;
643             if (!GC_is_marked(real_ptr)) {
644               GC_set_mark_bit(real_ptr);
645             } else {
646               if (prev_fo == 0)
647                 GC_finalize_now = next_fo;
648               else
649                 fo_set_next(prev_fo, next_fo);
650
651               curr_fo -> fo_hidden_base =
652                                 GC_HIDE_POINTER(curr_fo -> fo_hidden_base);
653               GC_bytes_finalized -=
654                   curr_fo->fo_object_size + sizeof(struct finalizable_object);
655
656               i = HASH2(real_ptr, log_fo_table_size);
657               fo_set_next (curr_fo, fo_head[i]);
658               GC_fo_entries++;
659               fo_head[i] = curr_fo;
660               curr_fo = prev_fo;
661             }
662           }
663           prev_fo = curr_fo;
664           curr_fo = next_fo;
665         }
666       }
667   }
668
669   /* Remove dangling disappearing links. */
670     for (i = 0; i < dl_size; i++) {
671       curr_dl = dl_head[i];
672       prev_dl = 0;
673       while (curr_dl != 0) {
674         real_link = GC_base(GC_REVEAL_POINTER(curr_dl -> dl_hidden_link));
675         if (real_link != 0 && !GC_is_marked(real_link)) {
676             next_dl = dl_next(curr_dl);
677             if (prev_dl == 0) {
678                 dl_head[i] = next_dl;
679             } else {
680                 dl_set_next(prev_dl, next_dl);
681             }
682             GC_clear_mark_bit((ptr_t)curr_dl);
683             GC_dl_entries--;
684             curr_dl = next_dl;
685         } else {
686             prev_dl = curr_dl;
687             curr_dl = dl_next(curr_dl);
688         }
689       }
690     }
691   if (GC_fail_count) {
692     /* Don't prevent running finalizers if there has been an allocation */
693     /* failure recently.                                                */
694 #   ifdef THREADS
695       GC_reset_finalizer_nested();
696 #   else
697       GC_finalizer_nested = 0;
698 #   endif
699   }
700 }
701
702 #ifndef JAVA_FINALIZATION_NOT_NEEDED
703
704   /* Enqueue all remaining finalizers to be run - Assumes lock is held. */
705   STATIC void GC_enqueue_all_finalizers(void)
706   {
707     struct finalizable_object * curr_fo, * prev_fo, * next_fo;
708     ptr_t real_ptr;
709     register int i;
710     int fo_size;
711
712     fo_size = (log_fo_table_size == -1 ) ? 0 : (1 << log_fo_table_size);
713     GC_bytes_finalized = 0;
714     for (i = 0; i < fo_size; i++) {
715         curr_fo = fo_head[i];
716         prev_fo = 0;
717       while (curr_fo != 0) {
718           real_ptr = GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
719           GC_MARK_FO(real_ptr, GC_normal_finalize_mark_proc);
720           GC_set_mark_bit(real_ptr);
721
722           /* Delete from hash table */
723           next_fo = fo_next(curr_fo);
724           if (prev_fo == 0) {
725               fo_head[i] = next_fo;
726           } else {
727               fo_set_next(prev_fo, next_fo);
728           }
729           GC_fo_entries--;
730
731           /* Add to list of objects awaiting finalization.      */
732           fo_set_next(curr_fo, GC_finalize_now);
733           GC_finalize_now = curr_fo;
734
735           /* unhide object pointer so any future collections will       */
736           /* see it.                                            */
737           curr_fo -> fo_hidden_base =
738                         (word)GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
739           GC_bytes_finalized +=
740                 curr_fo -> fo_object_size + sizeof(struct finalizable_object);
741           curr_fo = next_fo;
742         }
743     }
744   }
745
746   /* Invoke all remaining finalizers that haven't yet been run.
747    * This is needed for strict compliance with the Java standard,
748    * which can make the runtime guarantee that all finalizers are run.
749    * Unfortunately, the Java standard implies we have to keep running
750    * finalizers until there are no more left, a potential infinite loop.
751    * YUCK.
752    * Note that this is even more dangerous than the usual Java
753    * finalizers, in that objects reachable from static variables
754    * may have been finalized when these finalizers are run.
755    * Finalizers run at this point must be prepared to deal with a
756    * mostly broken world.
757    * This routine is externally callable, so is called without
758    * the allocation lock.
759    */
760   GC_API void GC_CALL GC_finalize_all(void)
761   {
762     DCL_LOCK_STATE;
763
764     LOCK();
765     while (GC_fo_entries > 0) {
766       GC_enqueue_all_finalizers();
767       UNLOCK();
768       GC_invoke_finalizers();
769       /* Running the finalizers in this thread is arguably not a good   */
770       /* idea when we should be notifying another thread to run them.   */
771       /* But otherwise we don't have a great way to wait for them to    */
772       /* run.                                                           */
773       LOCK();
774     }
775     UNLOCK();
776   }
777
778 #endif /* !JAVA_FINALIZATION_NOT_NEEDED */
779
780 /* Returns true if it is worth calling GC_invoke_finalizers. (Useful if */
781 /* finalizers can only be called from some kind of `safe state' and     */
782 /* getting into that safe state is expensive.)                          */
783 GC_API int GC_CALL GC_should_invoke_finalizers(void)
784 {
785     return GC_finalize_now != 0;
786 }
787
788 /* Invoke finalizers for all objects that are ready to be finalized.    */
789 /* Should be called without allocation lock.                            */
790 GC_API int GC_CALL GC_invoke_finalizers(void)
791 {
792     struct finalizable_object * curr_fo;
793     int count = 0;
794     word bytes_freed_before = 0; /* initialized to prevent warning. */
795     DCL_LOCK_STATE;
796
797     while (GC_finalize_now != 0) {
798 #       ifdef THREADS
799             LOCK();
800 #       endif
801         if (count == 0) {
802             bytes_freed_before = GC_bytes_freed;
803             /* Don't do this outside, since we need the lock. */
804         }
805         curr_fo = GC_finalize_now;
806 #       ifdef THREADS
807             if (curr_fo != 0) GC_finalize_now = fo_next(curr_fo);
808             UNLOCK();
809             if (curr_fo == 0) break;
810 #       else
811             GC_finalize_now = fo_next(curr_fo);
812 #       endif
813         fo_set_next(curr_fo, 0);
814         (*(curr_fo -> fo_fn))((ptr_t)(curr_fo -> fo_hidden_base),
815                               curr_fo -> fo_client_data);
816         curr_fo -> fo_client_data = 0;
817         ++count;
818 #       ifdef UNDEFINED
819             /* This is probably a bad idea.  It throws off accounting if */
820             /* nearly all objects are finalizable.  O.w. it shouldn't    */
821             /* matter.                                                   */
822             GC_free((void *)curr_fo);
823 #       endif
824     }
825     /* bytes_freed_before is initialized whenever count != 0 */
826     if (count != 0 && bytes_freed_before != GC_bytes_freed) {
827         LOCK();
828         GC_finalizer_bytes_freed += (GC_bytes_freed - bytes_freed_before);
829         UNLOCK();
830     }
831     return count;
832 }
833
834 /* All accesses to it should be synchronized to avoid data races.       */
835 GC_finalizer_notifier_proc GC_finalizer_notifier =
836         (GC_finalizer_notifier_proc)0;
837
838 static GC_word last_finalizer_notification = 0;
839
840 GC_INNER void GC_notify_or_invoke_finalizers(void)
841 {
842     GC_finalizer_notifier_proc notifier_fn = 0;
843 #   if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH)
844       static word last_back_trace_gc_no = 1;    /* Skip first one. */
845 #   endif
846     DCL_LOCK_STATE;
847
848 #   if defined(THREADS) && !defined(KEEP_BACK_PTRS) \
849        && !defined(MAKE_BACK_GRAPH)
850       /* Quick check (while unlocked) for an empty finalization queue.  */
851       if (GC_finalize_now == 0) return;
852 #   endif
853     LOCK();
854
855     /* This is a convenient place to generate backtraces if appropriate, */
856     /* since that code is not callable with the allocation lock.         */
857 #   if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH)
858       if (GC_gc_no > last_back_trace_gc_no) {
859 #       ifdef KEEP_BACK_PTRS
860           long i;
861           /* Stops when GC_gc_no wraps; that's OK.      */
862           last_back_trace_gc_no = (word)(-1);  /* disable others. */
863           for (i = 0; i < GC_backtraces; ++i) {
864               /* FIXME: This tolerates concurrent heap mutation,        */
865               /* which may cause occasional mysterious results.         */
866               /* We need to release the GC lock, since GC_print_callers */
867               /* acquires it.  It probably shouldn't.                   */
868               UNLOCK();
869               GC_generate_random_backtrace_no_gc();
870               LOCK();
871           }
872           last_back_trace_gc_no = GC_gc_no;
873 #       endif
874 #       ifdef MAKE_BACK_GRAPH
875           if (GC_print_back_height) {
876             UNLOCK();
877             GC_print_back_graph_stats();
878             LOCK();
879           }
880 #       endif
881       }
882 #   endif
883     if (GC_finalize_now == 0) {
884       UNLOCK();
885       return;
886     }
887
888     if (!GC_finalize_on_demand) {
889       unsigned char *pnested = GC_check_finalizer_nested();
890       UNLOCK();
891       /* Skip GC_invoke_finalizers() if nested */
892       if (pnested != NULL) {
893         (void) GC_invoke_finalizers();
894         *pnested = 0; /* Reset since no more finalizers. */
895 #       ifndef THREADS
896           GC_ASSERT(GC_finalize_now == 0);
897 #       endif   /* Otherwise GC can run concurrently and add more */
898       }
899       return;
900     }
901
902     /* These variables require synchronization to avoid data races.     */
903     if (last_finalizer_notification != GC_gc_no) {
904         last_finalizer_notification = GC_gc_no;
905         notifier_fn = GC_finalizer_notifier;
906     }
907     UNLOCK();
908     if (notifier_fn != 0)
909         (*notifier_fn)(); /* Invoke the notifier */
910 }
911
912 GC_API void * GC_CALL GC_call_with_alloc_lock(GC_fn_type fn,
913                                               void * client_data)
914 {
915     void * result;
916     DCL_LOCK_STATE;
917
918 #   ifdef THREADS
919       LOCK();
920       /* FIXME - This looks wrong!! */
921       SET_LOCK_HOLDER();
922 #   endif
923     result = (*fn)(client_data);
924 #   ifdef THREADS
925 #     ifndef GC_ASSERTIONS
926         UNSET_LOCK_HOLDER();
927 #     endif /* o.w. UNLOCK() does it implicitly */
928       UNLOCK();
929 #   endif
930     return(result);
931 }
932
933 #ifndef SMALL_CONFIG
934   GC_INNER void GC_print_finalization_stats(void)
935   {
936     struct finalizable_object *fo = GC_finalize_now;
937     unsigned long ready = 0;
938
939     GC_log_printf(
940         "%lu finalization table entries; %lu disappearing links alive\n",
941         (unsigned long)GC_fo_entries, (unsigned long)GC_dl_entries);
942     for (; 0 != fo; fo = fo_next(fo)) ++ready;
943     GC_log_printf("%lu objects are eligible for immediate finalization; "
944                   "%ld links cleared\n",
945                   ready, (long)GC_old_dl_entries - (long)GC_dl_entries);
946   }
947 #endif /* !SMALL_CONFIG */