Upgrade Boehm GC to 7.2alpha4.
[cacao.git] / src / mm / boehm-gc / 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 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
489
490 #ifndef SMALL_CONFIG
491   STATIC word GC_old_dl_entries = 0; /* for stats printing */
492 #endif
493
494 #ifdef THREADS
495   /* Defined in pthread_support.c or win32_threads.c.  Called with the  */
496   /* allocation lock held.                                              */
497   GC_INNER void GC_reset_finalizer_nested(void);
498   GC_INNER unsigned *GC_check_finalizer_nested(void);
499 #else
500   /* Global variables to minimize the level of recursion when a client  */
501   /* finalizer allocates memory.                                        */
502   STATIC unsigned GC_finalizer_nested = 0;
503   STATIC unsigned GC_finalizer_skipped = 0;
504
505   /* Checks and updates the level of finalizers recursion.              */
506   /* Returns NULL if GC_invoke_finalizers() should not be called by the */
507   /* collector (to minimize the risk of a deep finalizers recursion),   */
508   /* otherwise returns a pointer to GC_finalizer_nested.                */
509   STATIC unsigned *GC_check_finalizer_nested(void)
510   {
511     unsigned nesting_level = GC_finalizer_nested;
512     if (nesting_level) {
513       /* We are inside another GC_invoke_finalizers().          */
514       /* Skip some implicitly-called GC_invoke_finalizers()     */
515       /* depending on the nesting (recursion) level.            */
516       if (++GC_finalizer_skipped < (1U << nesting_level)) return NULL;
517       GC_finalizer_skipped = 0;
518     }
519     GC_finalizer_nested = nesting_level + 1;
520     return &GC_finalizer_nested;
521   }
522 #endif /* THREADS */
523
524 /* Called with held lock (but the world is running).                    */
525 /* Cause disappearing links to disappear and unreachable objects to be  */
526 /* enqueued for finalization.                                           */
527 GC_INNER void GC_finalize(void)
528 {
529     struct disappearing_link * curr_dl, * prev_dl, * next_dl;
530     struct finalizable_object * curr_fo, * prev_fo, * next_fo;
531     ptr_t real_ptr, real_link;
532     size_t i;
533     size_t dl_size = (log_dl_table_size == -1 ) ? 0 : (1 << log_dl_table_size);
534     size_t fo_size = (log_fo_table_size == -1 ) ? 0 : (1 << log_fo_table_size);
535
536 #   ifndef SMALL_CONFIG
537       /* Save current GC_dl_entries value for stats printing */
538       GC_old_dl_entries = GC_dl_entries;
539 #   endif
540
541   /* Make disappearing links disappear */
542     for (i = 0; i < dl_size; i++) {
543       curr_dl = dl_head[i];
544       prev_dl = 0;
545       while (curr_dl != 0) {
546         real_ptr = GC_REVEAL_POINTER(curr_dl -> dl_hidden_obj);
547         real_link = GC_REVEAL_POINTER(curr_dl -> dl_hidden_link);
548         if (!GC_is_marked(real_ptr)) {
549             *(word *)real_link = 0;
550             next_dl = dl_next(curr_dl);
551             if (prev_dl == 0) {
552                 dl_head[i] = next_dl;
553             } else {
554                 dl_set_next(prev_dl, next_dl);
555             }
556             GC_clear_mark_bit((ptr_t)curr_dl);
557             GC_dl_entries--;
558             curr_dl = next_dl;
559         } else {
560             prev_dl = curr_dl;
561             curr_dl = dl_next(curr_dl);
562         }
563       }
564     }
565   /* Mark all objects reachable via chains of 1 or more pointers        */
566   /* from finalizable objects.                                          */
567     GC_ASSERT(GC_mark_state == MS_NONE);
568     for (i = 0; i < fo_size; i++) {
569       for (curr_fo = fo_head[i]; curr_fo != 0; curr_fo = fo_next(curr_fo)) {
570         GC_ASSERT(GC_size(curr_fo) >= sizeof(struct finalizable_object));
571         real_ptr = GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
572         if (!GC_is_marked(real_ptr)) {
573             GC_MARKED_FOR_FINALIZATION(real_ptr);
574             GC_MARK_FO(real_ptr, curr_fo -> fo_mark_proc);
575             if (GC_is_marked(real_ptr)) {
576                 WARN("Finalization cycle involving %p\n", real_ptr);
577             }
578         }
579       }
580     }
581   /* Enqueue for finalization all objects that are still                */
582   /* unreachable.                                                       */
583     GC_bytes_finalized = 0;
584     for (i = 0; i < fo_size; i++) {
585       curr_fo = fo_head[i];
586       prev_fo = 0;
587       while (curr_fo != 0) {
588         real_ptr = GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
589         if (!GC_is_marked(real_ptr)) {
590             if (!GC_java_finalization) {
591               GC_set_mark_bit(real_ptr);
592             }
593             /* Delete from hash table */
594               next_fo = fo_next(curr_fo);
595               if (prev_fo == 0) {
596                 fo_head[i] = next_fo;
597               } else {
598                 fo_set_next(prev_fo, next_fo);
599               }
600               GC_fo_entries--;
601             /* Add to list of objects awaiting finalization.    */
602               fo_set_next(curr_fo, GC_finalize_now);
603               GC_finalize_now = curr_fo;
604               /* unhide object pointer so any future collections will   */
605               /* see it.                                                */
606               curr_fo -> fo_hidden_base =
607                         (word)GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
608               GC_bytes_finalized +=
609                         curr_fo -> fo_object_size
610                         + sizeof(struct finalizable_object);
611             GC_ASSERT(GC_is_marked(GC_base((ptr_t)curr_fo)));
612             curr_fo = next_fo;
613         } else {
614             prev_fo = curr_fo;
615             curr_fo = fo_next(curr_fo);
616         }
617       }
618     }
619
620   if (GC_java_finalization) {
621     /* make sure we mark everything reachable from objects finalized
622        using the no_order mark_proc */
623       for (curr_fo = GC_finalize_now;
624          curr_fo != NULL; curr_fo = fo_next(curr_fo)) {
625         real_ptr = (ptr_t)curr_fo -> fo_hidden_base;
626         if (!GC_is_marked(real_ptr)) {
627             if (curr_fo -> fo_mark_proc == GC_null_finalize_mark_proc) {
628                 GC_MARK_FO(real_ptr, GC_normal_finalize_mark_proc);
629             }
630             if (curr_fo -> fo_mark_proc != GC_unreachable_finalize_mark_proc) {
631                 GC_set_mark_bit(real_ptr);
632             }
633         }
634       }
635
636     /* now revive finalize-when-unreachable objects reachable from
637        other finalizable objects */
638       if (need_unreachable_finalization) {
639         curr_fo = GC_finalize_now;
640         prev_fo = 0;
641         while (curr_fo != 0) {
642           next_fo = fo_next(curr_fo);
643           if (curr_fo -> fo_mark_proc == GC_unreachable_finalize_mark_proc) {
644             real_ptr = (ptr_t)curr_fo -> fo_hidden_base;
645             if (!GC_is_marked(real_ptr)) {
646               GC_set_mark_bit(real_ptr);
647             } else {
648               if (prev_fo == 0)
649                 GC_finalize_now = next_fo;
650               else
651                 fo_set_next(prev_fo, next_fo);
652
653               curr_fo -> fo_hidden_base =
654                                 GC_HIDE_POINTER(curr_fo -> fo_hidden_base);
655               GC_bytes_finalized -=
656                   curr_fo->fo_object_size + sizeof(struct finalizable_object);
657
658               i = HASH2(real_ptr, log_fo_table_size);
659               fo_set_next (curr_fo, fo_head[i]);
660               GC_fo_entries++;
661               fo_head[i] = curr_fo;
662               curr_fo = prev_fo;
663             }
664           }
665           prev_fo = curr_fo;
666           curr_fo = next_fo;
667         }
668       }
669   }
670
671   /* Remove dangling disappearing links. */
672     for (i = 0; i < dl_size; i++) {
673       curr_dl = dl_head[i];
674       prev_dl = 0;
675       while (curr_dl != 0) {
676         real_link = GC_base(GC_REVEAL_POINTER(curr_dl -> dl_hidden_link));
677         if (real_link != 0 && !GC_is_marked(real_link)) {
678             next_dl = dl_next(curr_dl);
679             if (prev_dl == 0) {
680                 dl_head[i] = next_dl;
681             } else {
682                 dl_set_next(prev_dl, next_dl);
683             }
684             GC_clear_mark_bit((ptr_t)curr_dl);
685             GC_dl_entries--;
686             curr_dl = next_dl;
687         } else {
688             prev_dl = curr_dl;
689             curr_dl = dl_next(curr_dl);
690         }
691       }
692     }
693   if (GC_fail_count) {
694     /* Don't prevent running finalizers if there has been an allocation */
695     /* failure recently.                                                */
696 #   ifdef THREADS
697       GC_reset_finalizer_nested();
698 #   else
699       GC_finalizer_nested = 0;
700 #   endif
701   }
702 }
703
704 #ifndef JAVA_FINALIZATION_NOT_NEEDED
705
706   /* Enqueue all remaining finalizers to be run - Assumes lock is held. */
707   STATIC void GC_enqueue_all_finalizers(void)
708   {
709     struct finalizable_object * curr_fo, * prev_fo, * next_fo;
710     ptr_t real_ptr;
711     register int i;
712     int fo_size;
713
714     fo_size = (log_fo_table_size == -1 ) ? 0 : (1 << log_fo_table_size);
715     GC_bytes_finalized = 0;
716     for (i = 0; i < fo_size; i++) {
717         curr_fo = fo_head[i];
718         prev_fo = 0;
719       while (curr_fo != 0) {
720           real_ptr = GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
721           GC_MARK_FO(real_ptr, GC_normal_finalize_mark_proc);
722           GC_set_mark_bit(real_ptr);
723
724           /* Delete from hash table */
725           next_fo = fo_next(curr_fo);
726           if (prev_fo == 0) {
727               fo_head[i] = next_fo;
728           } else {
729               fo_set_next(prev_fo, next_fo);
730           }
731           GC_fo_entries--;
732
733           /* Add to list of objects awaiting finalization.      */
734           fo_set_next(curr_fo, GC_finalize_now);
735           GC_finalize_now = curr_fo;
736
737           /* unhide object pointer so any future collections will       */
738           /* see it.                                            */
739           curr_fo -> fo_hidden_base =
740                         (word)GC_REVEAL_POINTER(curr_fo -> fo_hidden_base);
741           GC_bytes_finalized +=
742                 curr_fo -> fo_object_size + sizeof(struct finalizable_object);
743           curr_fo = next_fo;
744         }
745     }
746   }
747
748   /* Invoke all remaining finalizers that haven't yet been run.
749    * This is needed for strict compliance with the Java standard,
750    * which can make the runtime guarantee that all finalizers are run.
751    * Unfortunately, the Java standard implies we have to keep running
752    * finalizers until there are no more left, a potential infinite loop.
753    * YUCK.
754    * Note that this is even more dangerous than the usual Java
755    * finalizers, in that objects reachable from static variables
756    * may have been finalized when these finalizers are run.
757    * Finalizers run at this point must be prepared to deal with a
758    * mostly broken world.
759    * This routine is externally callable, so is called without
760    * the allocation lock.
761    */
762   GC_API void GC_CALL GC_finalize_all(void)
763   {
764     DCL_LOCK_STATE;
765
766     LOCK();
767     while (GC_fo_entries > 0) {
768       GC_enqueue_all_finalizers();
769       UNLOCK();
770       GC_invoke_finalizers();
771       /* Running the finalizers in this thread is arguably not a good   */
772       /* idea when we should be notifying another thread to run them.   */
773       /* But otherwise we don't have a great way to wait for them to    */
774       /* run.                                                           */
775       LOCK();
776     }
777     UNLOCK();
778   }
779
780 #endif /* !JAVA_FINALIZATION_NOT_NEEDED */
781
782 /* Returns true if it is worth calling GC_invoke_finalizers. (Useful if */
783 /* finalizers can only be called from some kind of `safe state' and     */
784 /* getting into that safe state is expensive.)                          */
785 GC_API int GC_CALL GC_should_invoke_finalizers(void)
786 {
787     return GC_finalize_now != 0;
788 }
789
790 /* Invoke finalizers for all objects that are ready to be finalized.    */
791 /* Should be called without allocation lock.                            */
792 GC_API int GC_CALL GC_invoke_finalizers(void)
793 {
794     struct finalizable_object * curr_fo;
795     int count = 0;
796     word bytes_freed_before = 0; /* initialized to prevent warning. */
797     DCL_LOCK_STATE;
798
799     while (GC_finalize_now != 0) {
800 #       ifdef THREADS
801             LOCK();
802 #       endif
803         if (count == 0) {
804             bytes_freed_before = GC_bytes_freed;
805             /* Don't do this outside, since we need the lock. */
806         }
807         curr_fo = GC_finalize_now;
808 #       ifdef THREADS
809             if (curr_fo != 0) GC_finalize_now = fo_next(curr_fo);
810             UNLOCK();
811             if (curr_fo == 0) break;
812 #       else
813             GC_finalize_now = fo_next(curr_fo);
814 #       endif
815         fo_set_next(curr_fo, 0);
816         (*(curr_fo -> fo_fn))((ptr_t)(curr_fo -> fo_hidden_base),
817                               curr_fo -> fo_client_data);
818         curr_fo -> fo_client_data = 0;
819         ++count;
820 #       ifdef UNDEFINED
821             /* This is probably a bad idea.  It throws off accounting if */
822             /* nearly all objects are finalizable.  O.w. it shouldn't    */
823             /* matter.                                                   */
824             GC_free((void *)curr_fo);
825 #       endif
826     }
827     /* bytes_freed_before is initialized whenever count != 0 */
828     if (count != 0 && bytes_freed_before != GC_bytes_freed) {
829         LOCK();
830         GC_finalizer_bytes_freed += (GC_bytes_freed - bytes_freed_before);
831         UNLOCK();
832     }
833     return count;
834 }
835
836 /* All accesses to it should be synchronized to avoid data races.       */
837 GC_finalizer_notifier_proc GC_finalizer_notifier =
838         (GC_finalizer_notifier_proc)0;
839
840 static GC_word last_finalizer_notification = 0;
841
842 GC_INNER void GC_notify_or_invoke_finalizers(void)
843 {
844     GC_finalizer_notifier_proc notifier_fn = 0;
845 #   if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH)
846       static word last_back_trace_gc_no = 1;    /* Skip first one. */
847 #   elif defined(THREADS)
848       /* Quick check (while unlocked) for an empty finalization queue.  */
849       if (GC_finalize_now == 0) return;
850 #   endif
851     LOCK();
852
853     /* This is a convenient place to generate backtraces if appropriate, */
854     /* since that code is not callable with the allocation lock.         */
855 #   if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH)
856       if (GC_gc_no > last_back_trace_gc_no) {
857 #       ifdef KEEP_BACK_PTRS
858           long i;
859           /* Stops when GC_gc_no wraps; that's OK.      */
860           last_back_trace_gc_no = (word)(-1);  /* disable others. */
861           for (i = 0; i < GC_backtraces; ++i) {
862               /* FIXME: This tolerates concurrent heap mutation,        */
863               /* which may cause occasional mysterious results.         */
864               /* We need to release the GC lock, since GC_print_callers */
865               /* acquires it.  It probably shouldn't.                   */
866               UNLOCK();
867               GC_generate_random_backtrace_no_gc();
868               LOCK();
869           }
870           last_back_trace_gc_no = GC_gc_no;
871 #       endif
872 #       ifdef MAKE_BACK_GRAPH
873           if (GC_print_back_height) {
874             UNLOCK();
875             GC_print_back_graph_stats();
876             LOCK();
877           }
878 #       endif
879       }
880 #   endif
881     if (GC_finalize_now == 0) {
882       UNLOCK();
883       return;
884     }
885
886     if (!GC_finalize_on_demand) {
887       unsigned *pnested = GC_check_finalizer_nested();
888       UNLOCK();
889       /* Skip GC_invoke_finalizers() if nested */
890       if (pnested != NULL) {
891         (void) GC_invoke_finalizers();
892         *pnested = 0; /* Reset since no more finalizers. */
893 #       ifndef THREADS
894           GC_ASSERT(GC_finalize_now == 0);
895 #       endif   /* Otherwise GC can run concurrently and add more */
896       }
897       return;
898     }
899
900     /* These variables require synchronization to avoid data races.     */
901     if (last_finalizer_notification != GC_gc_no) {
902         last_finalizer_notification = GC_gc_no;
903         notifier_fn = GC_finalizer_notifier;
904     }
905     UNLOCK();
906     if (notifier_fn != 0)
907         (*notifier_fn)(); /* Invoke the notifier */
908 }
909
910 GC_API void * GC_CALL GC_call_with_alloc_lock(GC_fn_type fn,
911                                               void * client_data)
912 {
913     void * result;
914     DCL_LOCK_STATE;
915
916 #   ifdef THREADS
917       LOCK();
918       /* FIXME - This looks wrong!! */
919       SET_LOCK_HOLDER();
920 #   endif
921     result = (*fn)(client_data);
922 #   ifdef THREADS
923 #     ifndef GC_ASSERTIONS
924         UNSET_LOCK_HOLDER();
925 #     endif /* o.w. UNLOCK() does it implicitly */
926       UNLOCK();
927 #   endif
928     return(result);
929 }
930
931 #ifndef SMALL_CONFIG
932   GC_INNER void GC_print_finalization_stats(void)
933   {
934     struct finalizable_object *fo = GC_finalize_now;
935     unsigned long ready = 0;
936
937     GC_log_printf(
938         "%lu finalization table entries; %lu disappearing links alive\n",
939         (unsigned long)GC_fo_entries, (unsigned long)GC_dl_entries);
940     for (; 0 != fo; fo = fo_next(fo)) ++ready;
941     GC_log_printf("%lu objects are eligible for immediate finalization; "
942                   "%ld links cleared\n",
943                   ready, (long)GC_old_dl_entries - (long)GC_dl_entries);
944   }
945 #endif /* SMALL_CONFIG */