/* * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers * Copyright (c) 1991, 1992 by Xerox Corporation. All rights reserved. * 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 copy this garbage collector for any purpose, * provided the above notices are retained on all copies. */ # define I_HIDE_POINTERS # include "gc.h" # include "gc_private.h" # ifdef __STDC__ typedef void * void_star; # else typedef char * void_star; # endif # define LOG_TSIZE 7 # define TSIZE (1 << LOG_TSIZE) # define HASH(addr) \ ((((word)(addr) >> 3) ^ ((word)(addr) >> (3+LOG_TSIZE))) \ & (TSIZE - 1)) static struct disappearing_link { word dl_hidden_base; /* Pointer to object base */ word dl_offset; /* byte offset within object. */ struct disappearing_link * dl_next; } * dl_head[TSIZE] = {0}; static struct finalizable_object { word fo_hidden_base; /* Pointer to object base */ GC_finalization_proc fo_fn; /* Finalizer. */ ptr_t fo_client_data; word fo_object_size; /* In bytes. */ struct finalizable_object * fo_next; } * fo_head[TSIZE] = {0}; # define ALLOC(x, t) t *x = (t *)GC_malloc(sizeof (t)) int GC_register_disappearing_link(link) void_star * link; { ptr_t base; unsigned long offset; struct disappearing_link *curr_dl; int index; /* Allocate before acquiring lock */ ALLOC(new_dl, struct disappearing_link); DCL_LOCK_STATE; DISABLE_SIGNALS(); LOCK(); base = (ptr_t)GC_base((void_star)link); index = HASH(base); offset = (ptr_t)link - base; if (base == 0 || ((word)link & (ALIGNMENT-1))) ABORT("Bad arg to GC_register_disappearing_link"); curr_dl = dl_head[index]; for (curr_dl = dl_head[index]; curr_dl != 0; curr_dl = curr_dl -> dl_next) { if (curr_dl -> dl_hidden_base == HIDE_POINTER(base) && curr_dl -> dl_offset == offset) { UNLOCK(); ENABLE_SIGNALS(); GC_free((extern_ptr_t)new_dl); return(1); } } { new_dl -> dl_hidden_base = HIDE_POINTER(base); new_dl -> dl_offset = offset; new_dl -> dl_next = dl_head[index]; dl_head[index] = new_dl; UNLOCK(); ENABLE_SIGNALS(); return(0); } } int GC_unregister_disappearing_link(link) void_star * link; { ptr_t base; unsigned long offset; struct disappearing_link *curr_dl, *prev_dl; int index; DCL_LOCK_STATE; DISABLE_SIGNALS(); LOCK(); base = (ptr_t)GC_base((void_star)link); index = HASH(base); offset = (ptr_t)link - base; if (base == 0 || ((unsigned long)link & (ALIGNMENT-1))) return(0); prev_dl = 0; curr_dl = dl_head[index]; while (curr_dl != 0) { if (curr_dl -> dl_hidden_base == HIDE_POINTER(base) && curr_dl -> dl_offset == offset) { if (prev_dl == 0) { dl_head[index] = curr_dl -> dl_next; } else { prev_dl -> dl_next = curr_dl -> dl_next; } UNLOCK(); ENABLE_SIGNALS(); GC_free((extern_ptr_t)curr_dl); return(1); } curr_dl = curr_dl -> dl_next; prev_dl = curr_dl; } UNLOCK(); ENABLE_SIGNALS(); return(0); } bool GC_is_marked(p) ptr_t p; { register struct hblk *h = HBLKPTR(p); register hdr * hhdr = HDR(h); register int word_no = (word *)p - (word *)h; return(mark_bit_from_hdr(hhdr, word_no)); } void GC_set_mark_bit(p) ptr_t p; { register struct hblk *h = HBLKPTR(p); register hdr * hhdr = HDR(h); register int word_no = (word *)p - (word *)h; set_mark_bit_from_hdr(hhdr, word_no); } void GC_clear_mark_bit(p) ptr_t p; { register struct hblk *h = HBLKPTR(p); register hdr * hhdr = HDR(h); register int word_no = (word *)p - (word *)h; clear_mark_bit_from_hdr(hhdr, word_no); } void GC_register_finalizer(obj, fn, cd, ofn, ocd) void_star obj; GC_finalization_proc fn; void_star cd; GC_finalization_proc * ofn; void_star * ocd; { ptr_t base; struct finalizable_object * curr_fo, * prev_fo; int index; /* Allocate before acquiring lock */ ALLOC(new_fo, struct finalizable_object); DCL_LOCK_STATE; DISABLE_SIGNALS(); LOCK(); base = (ptr_t)GC_base((void_star)obj); index = HASH(base); if (base != obj) ABORT("Bad arg to GC_register_finalizer"); prev_fo = 0; curr_fo = fo_head[index]; while (curr_fo != 0) { if (curr_fo -> fo_hidden_base == HIDE_POINTER(base)) { if (ofn) *ofn = curr_fo -> fo_fn; if (ocd) *ocd = (void_star) curr_fo -> fo_client_data; if (fn == 0) { /* Delete the structure for base. */ if (prev_fo == 0) { fo_head[index] = curr_fo -> fo_next; } else { prev_fo -> fo_next = curr_fo -> fo_next; } UNLOCK(); ENABLE_SIGNALS(); GC_free((extern_ptr_t)curr_fo); } else { curr_fo -> fo_fn = fn; curr_fo -> fo_client_data = (ptr_t)cd; UNLOCK(); ENABLE_SIGNALS(); } GC_free((extern_ptr_t)new_fo); return; } curr_fo = curr_fo -> fo_next; prev_fo = curr_fo; } { if (ofn) *ofn = 0; if (ocd) *ocd = 0; if (fn == 0) { UNLOCK(); ENABLE_SIGNALS(); GC_free((extern_ptr_t)new_fo); return; } 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 = GC_size(base); new_fo -> fo_next = fo_head[index]; fo_head[index] = new_fo; } UNLOCK(); ENABLE_SIGNALS(); } /* 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; register int i; /* Make disappearing links disappear */ for (i = 0; i < TSIZE; i++) { curr_dl = dl_head[i]; prev_dl = 0; while (curr_dl != 0) { real_ptr = (ptr_t)REVEAL_POINTER(curr_dl -> dl_hidden_base); if (!GC_is_marked(real_ptr)) { *(word *)(real_ptr + curr_dl -> dl_offset) = 0; next_dl = curr_dl -> dl_next; if (prev_dl == 0) { dl_head[i] = next_dl; } else { prev_dl -> dl_next = next_dl; } GC_clear_mark_bit((ptr_t)curr_dl); curr_dl = next_dl; } else { prev_dl = curr_dl; curr_dl = curr_dl -> dl_next; } } } /* Mark all objects reachable via chains of 1 or more pointers */ /* from finalizable objects. */ for (i = 0; i < TSIZE; i++) { for (curr_fo = fo_head[i]; curr_fo != 0; curr_fo = curr_fo -> fo_next) { real_ptr = (ptr_t)REVEAL_POINTER(curr_fo -> fo_hidden_base); if (!GC_is_marked(real_ptr)) { GC_mark_all(real_ptr, real_ptr + curr_fo -> fo_object_size); } /* if (GC_is_marked(real_ptr)) { --> Report finalization cycle here, if desired } */ } } /* Invoke finalization code for all objects that are still */ /* unreachable. */ for (i = 0; i < TSIZE; 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)) { (*(curr_fo -> fo_fn))(real_ptr, curr_fo -> fo_client_data); GC_set_mark_bit(real_ptr); next_fo = curr_fo -> fo_next; if (prev_fo == 0) { fo_head[i] = next_fo; } else { prev_fo -> fo_next = next_fo; } if (!GC_is_marked((ptr_t)curr_fo)) { ABORT("GC_finalize: found accessible unmarked object\n"); } GC_clear_mark_bit((ptr_t)curr_fo); curr_fo = next_fo; } else { prev_fo = curr_fo; curr_fo = curr_fo -> fo_next; } } } } # ifdef __STDC__ void_star GC_call_with_alloc_lock(GC_fn_type fn, void_star client_data) # else void_star GC_call_with_alloc_lock(fn, client_data) GC_fn_type fn; void_star client_data; # endif { DCL_LOCK_STATE; DISABLE_SIGNALS(); LOCK(); (*fn)(client_data); UNLOCK(); ENABLE_SIGNALS(); }