/* * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers * Copyright (c) 1991-1996 by Xerox Corporation. All rights reserved. * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. * Copyright (C) 2007 Free Software Foundation, Inc * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. * * Permission is hereby granted to use or copy this program * for any purpose, provided the above notices are retained on all copies. * Permission to modify the code and to distribute modified code is granted, * provided the above notices are retained, and a notice that the code was * modified is included with the above copyright notice. */ /* Boehm, February 1, 1996 1:19 pm PST */ # define I_HIDE_POINTERS # include "private/gc_pmark.h" # ifdef FINALIZE_ON_DEMAND int GC_finalize_on_demand = 1; # else int GC_finalize_on_demand = 0; # endif # ifdef JAVA_FINALIZATION int GC_java_finalization = 1; # else int GC_java_finalization = 0; # endif /* Type of mark procedure used for marking from finalizable object. */ /* This procedure normally does not mark the object, only its */ /* descendents. */ typedef void finalization_mark_proc(/* ptr_t finalizable_obj_ptr */); # define HASH3(addr,size,log_size) \ ((((word)(addr) >> 3) ^ ((word)(addr) >> (3+(log_size)))) \ & ((size) - 1)) #define HASH2(addr,log_size) HASH3(addr, 1 << log_size, log_size) struct hash_chain_entry { word hidden_key; struct hash_chain_entry * next; }; unsigned GC_finalization_failures = 0; /* Number of finalization requests that failed for lack of memory. */ static struct disappearing_link { struct hash_chain_entry prolog; # define dl_hidden_link prolog.hidden_key /* Field to be cleared. */ # define dl_next(x) (struct disappearing_link *)((x) -> prolog.next) # define dl_set_next(x,y) (x) -> prolog.next = (struct hash_chain_entry *)(y) word dl_hidden_obj; /* Pointer to object base */ } **dl_head = 0; static signed_word log_dl_table_size = -1; /* Binary log of */ /* current size of array pointed to by dl_head. */ /* -1 ==> size is 0. */ word GC_dl_entries = 0; /* Number of entries currently in disappearing */ /* link table. */ static struct finalizable_object { struct hash_chain_entry prolog; # define fo_hidden_base prolog.hidden_key /* Pointer to object base. */ /* No longer hidden once object */ /* is on finalize_now queue. */ # define fo_next(x) (struct finalizable_object *)((x) -> prolog.next) # define fo_set_next(x,y) (x) -> prolog.next = (struct hash_chain_entry *)(y) GC_finalization_proc fo_fn; /* Finalizer. */ ptr_t fo_client_data; word fo_object_size; /* In bytes. */ finalization_mark_proc * fo_mark_proc; /* Mark-through procedure */ } **fo_head = 0; struct finalizable_object * GC_finalize_now = 0; /* LIst of objects that should be finalized now. */ static signed_word log_fo_table_size = -1; word GC_fo_entries = 0; void GC_push_finalizer_structures GC_PROTO((void)) { GC_push_all((ptr_t)(&dl_head), (ptr_t)(&dl_head) + sizeof(word)); GC_push_all((ptr_t)(&fo_head), (ptr_t)(&fo_head) + sizeof(word)); GC_push_all((ptr_t)(&GC_finalize_now), (ptr_t)(&GC_finalize_now) + sizeof(word)); } /* Double the size of a hash table. *size_ptr is the log of its current */ /* size. May be a noop. */ /* *table is a pointer to an array of hash headers. If we succeed, we */ /* update both *table and *log_size_ptr. */ /* Lock is held. Signals are disabled. */ void GC_grow_table(table, log_size_ptr) struct hash_chain_entry ***table; signed_word * log_size_ptr; { register word i; register struct hash_chain_entry *p; int log_old_size = *log_size_ptr; register int log_new_size = log_old_size + 1; word old_size = ((log_old_size == -1)? 0: (1 << log_old_size)); register word new_size = 1 << log_new_size; struct hash_chain_entry **new_table = (struct hash_chain_entry **) GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE( (size_t)new_size * sizeof(struct hash_chain_entry *), NORMAL); if (new_table == 0) { if (table == 0) { ABORT("Insufficient space for initial table allocation"); } else { return; } } for (i = 0; i < old_size; i++) { p = (*table)[i]; while (p != 0) { register ptr_t real_key = (ptr_t)REVEAL_POINTER(p -> hidden_key); register struct hash_chain_entry *next = p -> next; register int new_hash = HASH3(real_key, new_size, log_new_size); p -> next = new_table[new_hash]; new_table[new_hash] = p; p = next; } } *log_size_ptr = log_new_size; *table = new_table; } # if defined(__STDC__) || defined(__cplusplus) int GC_register_disappearing_link(GC_PTR * link) # else int GC_register_disappearing_link(link) GC_PTR * link; # endif { ptr_t base; base = (ptr_t)GC_base((GC_PTR)link); if (base == 0) ABORT("Bad arg to GC_register_disappearing_link"); return(GC_general_register_disappearing_link(link, base)); } # if defined(__STDC__) || defined(__cplusplus) int GC_general_register_disappearing_link(GC_PTR * link, GC_PTR obj) # else int GC_general_register_disappearing_link(link, obj) GC_PTR * link; GC_PTR obj; # endif { struct disappearing_link *curr_dl; int index; struct disappearing_link * new_dl; DCL_LOCK_STATE; if ((word)link & (ALIGNMENT-1)) ABORT("Bad arg to GC_general_register_disappearing_link"); # ifdef THREADS DISABLE_SIGNALS(); LOCK(); # endif if (log_dl_table_size == -1 || GC_dl_entries > ((word)1 << log_dl_table_size)) { # ifndef THREADS DISABLE_SIGNALS(); # endif GC_grow_table((struct hash_chain_entry ***)(&dl_head), &log_dl_table_size); # ifdef CONDPRINT if (GC_print_stats) { GC_printf1("Grew dl table to %lu entries\n", (unsigned long)(1 << log_dl_table_size)); } # endif # ifndef THREADS ENABLE_SIGNALS(); # endif } index = HASH2(link, log_dl_table_size); curr_dl = dl_head[index]; for (curr_dl = dl_head[index]; curr_dl != 0; curr_dl = dl_next(curr_dl)) { if (curr_dl -> dl_hidden_link == HIDE_POINTER(link)) { curr_dl -> dl_hidden_obj = HIDE_POINTER(obj); # ifdef THREADS UNLOCK(); ENABLE_SIGNALS(); # endif return(1); } } new_dl = (struct disappearing_link *) GC_INTERNAL_MALLOC(sizeof(struct disappearing_link),NORMAL); if (0 == new_dl) { # ifdef THREADS UNLOCK(); ENABLE_SIGNALS(); # endif new_dl = (struct disappearing_link *) GC_oom_fn(sizeof(struct disappearing_link)); if (0 == new_dl) { GC_finalization_failures++; return(0); } /* It's not likely we'll make it here, but ... */ # ifdef THREADS DISABLE_SIGNALS(); LOCK(); # endif } new_dl -> dl_hidden_obj = HIDE_POINTER(obj); new_dl -> dl_hidden_link = HIDE_POINTER(link); dl_set_next(new_dl, dl_head[index]); dl_head[index] = new_dl; GC_dl_entries++; # ifdef THREADS UNLOCK(); ENABLE_SIGNALS(); # endif return(0); } # if defined(__STDC__) || defined(__cplusplus) int GC_unregister_disappearing_link(GC_PTR * link) # else int GC_unregister_disappearing_link(link) GC_PTR * link; # endif { struct disappearing_link *curr_dl, *prev_dl; int index; DCL_LOCK_STATE; DISABLE_SIGNALS(); LOCK(); index = HASH2(link, log_dl_table_size); if (((unsigned long)link & (ALIGNMENT-1))) goto out; prev_dl = 0; curr_dl = dl_head[index]; while (curr_dl != 0) { if (curr_dl -> dl_hidden_link == HIDE_POINTER(link)) { if (prev_dl == 0) { dl_head[index] = dl_next(curr_dl); } else { dl_set_next(prev_dl, dl_next(curr_dl)); } GC_dl_entries--; UNLOCK(); ENABLE_SIGNALS(); # ifdef DBG_HDRS_ALL dl_set_next(curr_dl, 0); # else GC_free((GC_PTR)curr_dl); # endif return(1); } prev_dl = curr_dl; curr_dl = dl_next(curr_dl); } out: UNLOCK(); ENABLE_SIGNALS(); return(0); } /* Possible finalization_marker procedures. Note that mark stack */ /* overflow is handled by the caller, and is not a disaster. */ GC_API void GC_normal_finalize_mark_proc(p) ptr_t p; { hdr * hhdr = HDR(p); PUSH_OBJ((word *)p, hhdr, GC_mark_stack_top, &(GC_mark_stack[GC_mark_stack_size])); } /* This only pays very partial attention to the mark descriptor. */ /* It does the right thing for normal and atomic objects, and treats */ /* most others as normal. */ GC_API void GC_ignore_self_finalize_mark_proc(p) ptr_t p; { hdr * hhdr = HDR(p); word descr = hhdr -> hb_descr; ptr_t q, r; ptr_t scan_limit; ptr_t target_limit = p + WORDS_TO_BYTES(hhdr -> hb_sz) - 1; if ((descr & GC_DS_TAGS) == GC_DS_LENGTH) { scan_limit = p + descr - sizeof(word); } else { scan_limit = target_limit + 1 - sizeof(word); } for (q = p; q <= scan_limit; q += ALIGNMENT) { r = *(ptr_t *)q; if (r < p || r > target_limit) { GC_PUSH_ONE_HEAP((word)r, q); } } } /*ARGSUSED*/ GC_API void GC_null_finalize_mark_proc(p) ptr_t p; { } /* Possible finalization_marker procedures. Note that mark stack */ /* overflow is handled by the caller, and is not a disaster. */ GC_API void GC_unreachable_finalize_mark_proc(p) ptr_t p; { return GC_normal_finalize_mark_proc(p); } /* Register a finalization function. See gc.h for details. */ /* in the nonthreads case, we try to avoid disabling signals, */ /* since it can be expensive. Threads packages typically */ /* make it cheaper. */ /* The last parameter is a procedure that determines */ /* marking for finalization ordering. Any objects marked */ /* by that procedure will be guaranteed to not have been */ /* finalized when this finalizer is invoked. */ GC_API void GC_register_finalizer_inner(obj, fn, cd, ofn, ocd, mp) GC_PTR obj; GC_finalization_proc fn; GC_PTR cd; GC_finalization_proc * ofn; GC_PTR * ocd; finalization_mark_proc * mp; { ptr_t base; struct finalizable_object * curr_fo, * prev_fo; int index; struct finalizable_object *new_fo; hdr *hhdr; DCL_LOCK_STATE; # ifdef THREADS DISABLE_SIGNALS(); LOCK(); # endif if (log_fo_table_size == -1 || GC_fo_entries > ((word)1 << log_fo_table_size)) { # ifndef THREADS DISABLE_SIGNALS(); # endif GC_grow_table((struct hash_chain_entry ***)(&fo_head), &log_fo_table_size); # ifdef CONDPRINT if (GC_print_stats) { GC_printf1("Grew fo table to %lu entries\n", (unsigned long)(1 << log_fo_table_size)); } # endif # ifndef THREADS ENABLE_SIGNALS(); # endif } /* in the THREADS case signals are disabled and we hold allocation */ /* lock; otherwise neither is true. Proceed carefully. */ base = (ptr_t)obj; index = HASH2(base, log_fo_table_size); prev_fo = 0; curr_fo = fo_head[index]; while (curr_fo != 0) { if (curr_fo -> fo_hidden_base == HIDE_POINTER(base)) { /* Interruption by a signal in the middle of this */ /* should be safe. The client may see only *ocd */ /* updated, but we'll declare that to be his */ /* problem. */ if (ocd) *ocd = (GC_PTR) curr_fo -> fo_client_data; if (ofn) *ofn = curr_fo -> fo_fn; /* Delete the structure for base. */ if (prev_fo == 0) { fo_head[index] = fo_next(curr_fo); } else { fo_set_next(prev_fo, fo_next(curr_fo)); } if (fn == 0) { GC_fo_entries--; /* May not happen if we get a signal. But a high */ /* estimate will only make the table larger than */ /* necessary. */ # if !defined(THREADS) && !defined(DBG_HDRS_ALL) GC_free((GC_PTR)curr_fo); # endif } else { curr_fo -> fo_fn = fn; curr_fo -> fo_client_data = (ptr_t)cd; curr_fo -> fo_mark_proc = mp; /* Reinsert it. We deleted it first to maintain */ /* consistency in the event of a signal. */ if (prev_fo == 0) { fo_head[index] = curr_fo; } else { fo_set_next(prev_fo, curr_fo); } } # ifdef THREADS UNLOCK(); ENABLE_SIGNALS(); # endif return; } prev_fo = curr_fo; curr_fo = fo_next(curr_fo); } if (ofn) *ofn = 0; if (ocd) *ocd = 0; if (fn == 0) { # ifdef THREADS UNLOCK(); ENABLE_SIGNALS(); # endif return; } GET_HDR(base, hhdr); if (0 == hhdr) { /* We won't collect it, hence finalizer wouldn't be run. */ # ifdef THREADS UNLOCK(); ENABLE_SIGNALS(); # endif return; } new_fo = (struct finalizable_object *) GC_INTERNAL_MALLOC(sizeof(struct finalizable_object),NORMAL); if (0 == new_fo) { # ifdef THREADS UNLOCK(); ENABLE_SIGNALS(); # endif new_fo = (struct finalizable_object *) GC_oom_fn(sizeof(struct finalizable_object)); if (0 == new_fo) { GC_finalization_failures++; return; } /* It's not likely we'll make it here, but ... */ # ifdef THREADS DISABLE_SIGNALS(); LOCK(); # endif } new_fo -> fo_hidden_base = (word)HIDE_POINTER(base); new_fo -> fo_fn = fn; new_fo -> fo_client_data = (ptr_t)cd; new_fo -> fo_object_size = hhdr -> hb_sz; new_fo -> fo_mark_proc = mp; fo_set_next(new_fo, fo_head[index]); GC_fo_entries++; fo_head[index] = new_fo; # ifdef THREADS UNLOCK(); ENABLE_SIGNALS(); # endif } # if defined(__STDC__) void GC_register_finalizer(void * obj, GC_finalization_proc fn, void * cd, GC_finalization_proc *ofn, void ** ocd) # else void GC_register_finalizer(obj, fn, cd, ofn, ocd) GC_PTR obj; GC_finalization_proc fn; GC_PTR cd; GC_finalization_proc * ofn; GC_PTR * ocd; # endif { GC_register_finalizer_inner(obj, fn, cd, ofn, ocd, GC_normal_finalize_mark_proc); } # if defined(__STDC__) void GC_register_finalizer_ignore_self(void * obj, GC_finalization_proc fn, void * cd, GC_finalization_proc *ofn, void ** ocd) # else void GC_register_finalizer_ignore_self(obj, fn, cd, ofn, ocd) GC_PTR obj; GC_finalization_proc fn; GC_PTR cd; GC_finalization_proc * ofn; GC_PTR * ocd; # endif { GC_register_finalizer_inner(obj, fn, cd, ofn, ocd, GC_ignore_self_finalize_mark_proc); } # if defined(__STDC__) void GC_register_finalizer_no_order(void * obj, GC_finalization_proc fn, void * cd, GC_finalization_proc *ofn, void ** ocd) # else void GC_register_finalizer_no_order(obj, fn, cd, ofn, ocd) GC_PTR obj; GC_finalization_proc fn; GC_PTR cd; GC_finalization_proc * ofn; GC_PTR * ocd; # endif { GC_register_finalizer_inner(obj, fn, cd, ofn, ocd, GC_null_finalize_mark_proc); } # if defined(__STDC__) void GC_register_finalizer_unreachable(void * obj, GC_finalization_proc fn, void * cd, GC_finalization_proc *ofn, void ** ocd) # else void GC_register_finalizer_unreachable(obj, fn, cd, ofn, ocd) GC_PTR obj; GC_finalization_proc fn; GC_PTR cd; GC_finalization_proc * ofn; GC_PTR * ocd; # endif { GC_register_finalizer_inner(obj, fn, cd, ofn, ocd, GC_unreachable_finalize_mark_proc); } #ifndef NO_DEBUGGING void GC_dump_finalization() { struct disappearing_link * curr_dl; struct finalizable_object * curr_fo; ptr_t real_ptr, real_link; int dl_size = (log_dl_table_size == -1 ) ? 0 : (1 << log_dl_table_size); int fo_size = (log_fo_table_size == -1 ) ? 0 : (1 << log_fo_table_size); int i; GC_printf0("Disappearing links:\n"); for (i = 0; i < dl_size; i++) { for (curr_dl = dl_head[i]; curr_dl != 0; curr_dl = dl_next(curr_dl)) { real_ptr = (ptr_t)REVEAL_POINTER(curr_dl -> dl_hidden_obj); real_link = (ptr_t)REVEAL_POINTER(curr_dl -> dl_hidden_link); GC_printf2("Object: 0x%lx, Link:0x%lx\n", real_ptr, real_link); } } GC_printf0("Finalizers:\n"); for (i = 0; i < fo_size; i++) { for (curr_fo = fo_head[i]; curr_fo != 0; curr_fo = fo_next(curr_fo)) { real_ptr = (ptr_t)REVEAL_POINTER(curr_fo -> fo_hidden_base); GC_printf1("Finalizable object: 0x%lx\n", real_ptr); } } } #endif /* Called with world stopped. Cause disappearing links to disappear, */ /* and invoke finalizers. */ void GC_finalize() { struct disappearing_link * curr_dl, * prev_dl, * next_dl; struct finalizable_object * curr_fo, * prev_fo, * next_fo; ptr_t real_ptr, real_link; register int i; int dl_size = (log_dl_table_size == -1 ) ? 0 : (1 << log_dl_table_size); int fo_size = (log_fo_table_size == -1 ) ? 0 : (1 << log_fo_table_size); /* Make disappearing links disappear */ for (i = 0; i < dl_size; i++) { curr_dl = dl_head[i]; prev_dl = 0; while (curr_dl != 0) { real_ptr = (ptr_t)REVEAL_POINTER(curr_dl -> dl_hidden_obj); real_link = (ptr_t)REVEAL_POINTER(curr_dl -> dl_hidden_link); if (!GC_is_marked(real_ptr)) { *(word *)real_link = 0; next_dl = dl_next(curr_dl); if (prev_dl == 0) { dl_head[i] = next_dl; } else { dl_set_next(prev_dl, next_dl); } GC_clear_mark_bit((ptr_t)curr_dl); GC_dl_entries--; curr_dl = next_dl; } else { prev_dl = curr_dl; curr_dl = dl_next(curr_dl); } } } /* Mark all objects reachable via chains of 1 or more pointers */ /* from finalizable objects. */ GC_ASSERT(GC_mark_state == MS_NONE); for (i = 0; i < fo_size; i++) { for (curr_fo = fo_head[i]; curr_fo != 0; curr_fo = fo_next(curr_fo)) { real_ptr = (ptr_t)REVEAL_POINTER(curr_fo -> fo_hidden_base); if (!GC_is_marked(real_ptr)) { GC_MARKED_FOR_FINALIZATION(real_ptr); GC_MARK_FO(real_ptr, curr_fo -> fo_mark_proc); if (GC_is_marked(real_ptr)) { WARN("Finalization cycle involving %lx\n", real_ptr); } } } } /* Enqueue for finalization all objects that are still */ /* unreachable. */ GC_words_finalized = 0; for (i = 0; i < fo_size; i++) { curr_fo = fo_head[i]; prev_fo = 0; while (curr_fo != 0) { real_ptr = (ptr_t)REVEAL_POINTER(curr_fo -> fo_hidden_base); if (!GC_is_marked(real_ptr)) { if (!GC_java_finalization) { GC_set_mark_bit(real_ptr); } /* Delete from hash table */ next_fo = fo_next(curr_fo); if (prev_fo == 0) { fo_head[i] = next_fo; } else { fo_set_next(prev_fo, next_fo); } GC_fo_entries--; /* Add to list of objects awaiting finalization. */ fo_set_next(curr_fo, GC_finalize_now); GC_finalize_now = curr_fo; /* unhide object pointer so any future collections will */ /* see it. */ curr_fo -> fo_hidden_base = (word) REVEAL_POINTER(curr_fo -> fo_hidden_base); GC_words_finalized += ALIGNED_WORDS(curr_fo -> fo_object_size) + ALIGNED_WORDS(sizeof(struct finalizable_object)); GC_ASSERT(GC_is_marked(GC_base((ptr_t)curr_fo))); curr_fo = next_fo; } else { prev_fo = curr_fo; curr_fo = fo_next(curr_fo); } } } if (GC_java_finalization) { /* make sure we mark everything reachable from objects finalized using the no_order mark_proc */ for (curr_fo = GC_finalize_now; curr_fo != NULL; curr_fo = fo_next(curr_fo)) { real_ptr = (ptr_t)curr_fo -> fo_hidden_base; if (!GC_is_marked(real_ptr)) { if (curr_fo -> fo_mark_proc == GC_null_finalize_mark_proc) { GC_MARK_FO(real_ptr, GC_normal_finalize_mark_proc); } if (curr_fo -> fo_mark_proc != GC_unreachable_finalize_mark_proc) { GC_set_mark_bit(real_ptr); } } } /* now revive finalize-when-unreachable objects reachable from other finalizable objects */ curr_fo = GC_finalize_now; prev_fo = 0; while (curr_fo != 0) { next_fo = fo_next(curr_fo); if (curr_fo -> fo_mark_proc == GC_unreachable_finalize_mark_proc) { real_ptr = (ptr_t)curr_fo -> fo_hidden_base; if (!GC_is_marked(real_ptr)) { GC_set_mark_bit(real_ptr); } else { if (prev_fo == 0) GC_finalize_now = next_fo; else fo_set_next(prev_fo, next_fo); curr_fo -> fo_hidden_base = (word) HIDE_POINTER(curr_fo -> fo_hidden_base); GC_words_finalized -= ALIGNED_WORDS(curr_fo -> fo_object_size) + ALIGNED_WORDS(sizeof(struct finalizable_object)); i = HASH2(real_ptr, log_fo_table_size); fo_set_next (curr_fo, fo_head[i]); GC_fo_entries++; fo_head[i] = curr_fo; curr_fo = prev_fo; } } prev_fo = curr_fo; curr_fo = next_fo; } } /* Remove dangling disappearing links. */ for (i = 0; i < dl_size; i++) { curr_dl = dl_head[i]; prev_dl = 0; while (curr_dl != 0) { real_link = GC_base((ptr_t)REVEAL_POINTER(curr_dl -> dl_hidden_link)); if (real_link != 0 && !GC_is_marked(real_link)) { next_dl = dl_next(curr_dl); if (prev_dl == 0) { dl_head[i] = next_dl; } else { dl_set_next(prev_dl, next_dl); } GC_clear_mark_bit((ptr_t)curr_dl); GC_dl_entries--; curr_dl = next_dl; } else { prev_dl = curr_dl; curr_dl = dl_next(curr_dl); } } } } #ifndef JAVA_FINALIZATION_NOT_NEEDED /* Enqueue all remaining finalizers to be run - Assumes lock is * held, and signals are disabled */ void GC_enqueue_all_finalizers() { struct finalizable_object * curr_fo, * prev_fo, * next_fo; ptr_t real_ptr; register int i; int fo_size; fo_size = (log_fo_table_size == -1 ) ? 0 : (1 << log_fo_table_size); GC_words_finalized = 0; for (i = 0; i < fo_size; i++) { curr_fo = fo_head[i]; prev_fo = 0; while (curr_fo != 0) { real_ptr = (ptr_t)REVEAL_POINTER(curr_fo -> fo_hidden_base); GC_MARK_FO(real_ptr, GC_normal_finalize_mark_proc); GC_set_mark_bit(real_ptr); /* Delete from hash table */ next_fo = fo_next(curr_fo); if (prev_fo == 0) { fo_head[i] = next_fo; } else { fo_set_next(prev_fo, next_fo); } GC_fo_entries--; /* Add to list of objects awaiting finalization. */ fo_set_next(curr_fo, GC_finalize_now); GC_finalize_now = curr_fo; /* unhide object pointer so any future collections will */ /* see it. */ curr_fo -> fo_hidden_base = (word) REVEAL_POINTER(curr_fo -> fo_hidden_base); GC_words_finalized += ALIGNED_WORDS(curr_fo -> fo_object_size) + ALIGNED_WORDS(sizeof(struct finalizable_object)); curr_fo = next_fo; } } return; } /* Invoke all remaining finalizers that haven't yet been run. * This is needed for strict compliance with the Java standard, * which can make the runtime guarantee that all finalizers are run. * Unfortunately, the Java standard implies we have to keep running * finalizers until there are no more left, a potential infinite loop. * YUCK. * Note that this is even more dangerous than the usual Java * finalizers, in that objects reachable from static variables * may have been finalized when these finalizers are run. * Finalizers run at this point must be prepared to deal with a * mostly broken world. * This routine is externally callable, so is called without * the allocation lock. */ GC_API void GC_finalize_all() { DCL_LOCK_STATE; DISABLE_SIGNALS(); LOCK(); while (GC_fo_entries > 0) { GC_enqueue_all_finalizers(); UNLOCK(); ENABLE_SIGNALS(); GC_INVOKE_FINALIZERS(); DISABLE_SIGNALS(); LOCK(); } UNLOCK(); ENABLE_SIGNALS(); } #endif /* Returns true if it is worth calling GC_invoke_finalizers. (Useful if */ /* finalizers can only be called from some kind of `safe state' and */ /* getting into that safe state is expensive.) */ int GC_should_invoke_finalizers GC_PROTO((void)) { return GC_finalize_now != 0; } /* Invoke finalizers for all objects that are ready to be finalized. */ /* Should be called without allocation lock. */ int GC_invoke_finalizers() { struct finalizable_object * curr_fo; int count = 0; word mem_freed_before; DCL_LOCK_STATE; while (GC_finalize_now != 0) { # ifdef THREADS DISABLE_SIGNALS(); LOCK(); # endif if (count == 0) { mem_freed_before = GC_mem_freed; } curr_fo = GC_finalize_now; # ifdef THREADS if (curr_fo != 0) GC_finalize_now = fo_next(curr_fo); UNLOCK(); ENABLE_SIGNALS(); if (curr_fo == 0) break; # else GC_finalize_now = fo_next(curr_fo); # endif fo_set_next(curr_fo, 0); (*(curr_fo -> fo_fn))((ptr_t)(curr_fo -> fo_hidden_base), curr_fo -> fo_client_data); curr_fo -> fo_client_data = 0; ++count; # ifdef UNDEFINED /* This is probably a bad idea. It throws off accounting if */ /* nearly all objects are finalizable. O.w. it shouldn't */ /* matter. */ GC_free((GC_PTR)curr_fo); # endif } if (count != 0 && mem_freed_before != GC_mem_freed) { LOCK(); GC_finalizer_mem_freed += (GC_mem_freed - mem_freed_before); UNLOCK(); } return count; } void (* GC_finalizer_notifier)() = (void (*) GC_PROTO((void)))0; static GC_word last_finalizer_notification = 0; void GC_notify_or_invoke_finalizers GC_PROTO((void)) { /* This is a convenient place to generate backtraces if appropriate, */ /* since that code is not callable with the allocation lock. */ # if defined(KEEP_BACK_PTRS) || defined(MAKE_BACK_GRAPH) static word last_back_trace_gc_no = 1; /* Skip first one. */ if (GC_gc_no > last_back_trace_gc_no) { word i; # ifdef KEEP_BACK_PTRS LOCK(); /* Stops when GC_gc_no wraps; that's OK. */ last_back_trace_gc_no = (word)(-1); /* disable others. */ for (i = 0; i < GC_backtraces; ++i) { /* FIXME: This tolerates concurrent heap mutation, */ /* which may cause occasional mysterious results. */ /* We need to release the GC lock, since GC_print_callers */ /* acquires it. It probably shouldn't. */ UNLOCK(); GC_generate_random_backtrace_no_gc(); LOCK(); } last_back_trace_gc_no = GC_gc_no; UNLOCK(); # endif # ifdef MAKE_BACK_GRAPH if (GC_print_back_height) GC_print_back_graph_stats(); # endif } # endif if (GC_finalize_now == 0) return; if (!GC_finalize_on_demand) { (void) GC_invoke_finalizers(); # ifndef THREADS GC_ASSERT(GC_finalize_now == 0); # endif /* Otherwise GC can run concurrently and add more */ return; } if (GC_finalizer_notifier != (void (*) GC_PROTO((void)))0 && last_finalizer_notification != GC_gc_no) { last_finalizer_notification = GC_gc_no; GC_finalizer_notifier(); } } # ifdef __STDC__ GC_PTR GC_call_with_alloc_lock(GC_fn_type fn, GC_PTR client_data) # else GC_PTR GC_call_with_alloc_lock(fn, client_data) GC_fn_type fn; GC_PTR client_data; # endif { GC_PTR result; DCL_LOCK_STATE; # ifdef THREADS DISABLE_SIGNALS(); LOCK(); SET_LOCK_HOLDER(); # endif result = (*fn)(client_data); # ifdef THREADS # ifndef GC_ASSERTIONS UNSET_LOCK_HOLDER(); # endif /* o.w. UNLOCK() does it implicitly */ UNLOCK(); ENABLE_SIGNALS(); # endif return(result); } #if !defined(NO_DEBUGGING) void GC_print_finalization_stats() { struct finalizable_object *fo = GC_finalize_now; size_t ready = 0; GC_printf2("%lu finalization table entries; %lu disappearing links\n", GC_fo_entries, GC_dl_entries); for (; 0 != fo; fo = fo_next(fo)) ++ready; GC_printf1("%lu objects are eligible for immediate finalization\n", ready); } #endif /* NO_DEBUGGING */