diff options
Diffstat (limited to '')
-rw-r--r-- | gc/stubborn.c | 317 |
1 files changed, 317 insertions, 0 deletions
diff --git a/gc/stubborn.c b/gc/stubborn.c new file mode 100644 index 0000000..bef7b98 --- /dev/null +++ b/gc/stubborn.c @@ -0,0 +1,317 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 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 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, July 31, 1995 5:02 pm PDT */ + + +#include "gc_priv.h" + +# ifdef STUBBORN_ALLOC +/* Stubborn object (hard to change, nearly immutable) allocation. */ + +extern ptr_t GC_clear_stack(); /* in misc.c, behaves like identity */ + +#define GENERAL_MALLOC(lb,k) \ + (GC_PTR)GC_clear_stack(GC_generic_malloc((word)lb, k)) + +/* Data structure representing immutable objects that */ +/* are still being initialized. */ +/* This is a bit baroque in order to avoid acquiring */ +/* the lock twice for a typical allocation. */ + +GC_PTR * GC_changing_list_start; + +# ifdef THREADS + VOLATILE GC_PTR * VOLATILE GC_changing_list_current; +# else + GC_PTR * GC_changing_list_current; +# endif + /* Points at last added element. Also (ab)used for */ + /* synchronization. Updates and reads are assumed atomic. */ + +GC_PTR * GC_changing_list_limit; + /* Points at the last word of the buffer, which is always 0 */ + /* All entries in (GC_changing_list_current, */ + /* GC_changing_list_limit] are 0 */ + + +void GC_stubborn_init() +{ +# define INIT_SIZE 10 + + GC_changing_list_start = (GC_PTR *) + GC_generic_malloc_inner( + (word)(INIT_SIZE * sizeof(GC_PTR)), + PTRFREE); + BZERO(GC_changing_list_start, + INIT_SIZE * sizeof(GC_PTR)); + if (GC_changing_list_start == 0) { + GC_err_printf0("Insufficient space to start up\n"); + ABORT("GC_stubborn_init: put of space"); + } + GC_changing_list_current = GC_changing_list_start; + GC_changing_list_limit = GC_changing_list_start + INIT_SIZE - 1; + * GC_changing_list_limit = 0; +} + +/* Compact and possibly grow GC_uninit_list. The old copy is */ +/* left alone. Lock must be held. */ +/* When called GC_changing_list_current == GC_changing_list_limit */ +/* which is one past the current element. */ +/* When we finish GC_changing_list_current again points one past last */ +/* element. */ +/* Invariant while this is running: GC_changing_list_current */ +/* points at a word containing 0. */ +/* Returns FALSE on failure. */ +GC_bool GC_compact_changing_list() +{ + register GC_PTR *p, *q; + register word count = 0; + word old_size = (char **)GC_changing_list_limit + - (char **)GC_changing_list_start+1; + /* The casts are needed as a workaround for an Amiga bug */ + register word new_size = old_size; + GC_PTR * new_list; + + for (p = GC_changing_list_start; p < GC_changing_list_limit; p++) { + if (*p != 0) count++; + } + if (2 * count > old_size) new_size = 2 * count; + new_list = (GC_PTR *) + GC_generic_malloc_inner( + new_size * sizeof(GC_PTR), PTRFREE); + /* PTRFREE is a lie. But we don't want the collector to */ + /* consider these. We do want the list itself to be */ + /* collectable. */ + if (new_list == 0) return(FALSE); + BZERO(new_list, new_size * sizeof(GC_PTR)); + q = new_list; + for (p = GC_changing_list_start; p < GC_changing_list_limit; p++) { + if (*p != 0) *q++ = *p; + } + GC_changing_list_start = new_list; + GC_changing_list_limit = new_list + new_size - 1; + GC_changing_list_current = q; + return(TRUE); +} + +/* Add p to changing list. Clear p on failure. */ +# define ADD_CHANGING(p) \ + { \ + register struct hblk * h = HBLKPTR(p); \ + register word index = PHT_HASH(h); \ + \ + set_pht_entry_from_index(GC_changed_pages, index); \ + } \ + if (*GC_changing_list_current != 0 \ + && ++GC_changing_list_current == GC_changing_list_limit) { \ + if (!GC_compact_changing_list()) (p) = 0; \ + } \ + *GC_changing_list_current = p; + +void GC_change_stubborn(p) +GC_PTR p; +{ + DCL_LOCK_STATE; + + DISABLE_SIGNALS(); + LOCK(); + ADD_CHANGING(p); + UNLOCK(); + ENABLE_SIGNALS(); +} + +void GC_end_stubborn_change(p) +GC_PTR p; +{ +# ifdef THREADS + register VOLATILE GC_PTR * my_current = GC_changing_list_current; +# else + register GC_PTR * my_current = GC_changing_list_current; +# endif + register GC_bool tried_quick; + DCL_LOCK_STATE; + + if (*my_current == p) { + /* Hopefully the normal case. */ + /* Compaction could not have been running when we started. */ + *my_current = 0; +# ifdef THREADS + if (my_current == GC_changing_list_current) { + /* Compaction can't have run in the interim. */ + /* We got away with the quick and dirty approach. */ + return; + } + tried_quick = TRUE; +# else + return; +# endif + } else { + tried_quick = FALSE; + } + DISABLE_SIGNALS(); + LOCK(); + my_current = GC_changing_list_current; + for (; my_current >= GC_changing_list_start; my_current--) { + if (*my_current == p) { + *my_current = 0; + UNLOCK(); + ENABLE_SIGNALS(); + return; + } + } + if (!tried_quick) { + GC_err_printf1("Bad arg to GC_end_stubborn_change: 0x%lx\n", + (unsigned long)p); + ABORT("Bad arg to GC_end_stubborn_change"); + } + UNLOCK(); + ENABLE_SIGNALS(); +} + +/* Allocate lb bytes of composite (pointerful) data */ +/* No pointer fields may be changed after a call to */ +/* GC_end_stubborn_change(p) where p is the value */ +/* returned by GC_malloc_stubborn. */ +# ifdef __STDC__ + GC_PTR GC_malloc_stubborn(size_t lb) +# else + GC_PTR GC_malloc_stubborn(lb) + size_t lb; +# endif +{ +register ptr_t op; +register ptr_t *opp; +register word lw; +ptr_t result; +DCL_LOCK_STATE; + + if( SMALL_OBJ(lb) ) { +# ifdef MERGE_SIZES + lw = GC_size_map[lb]; +# else + lw = ALIGNED_WORDS(lb); +# endif + opp = &(GC_sobjfreelist[lw]); + FASTLOCK(); + if( !FASTLOCK_SUCCEEDED() || (op = *opp) == 0 ) { + FASTUNLOCK(); + result = GC_generic_malloc((word)lb, STUBBORN); + goto record; + } + *opp = obj_link(op); + obj_link(op) = 0; + GC_words_allocd += lw; + result = (GC_PTR) op; + ADD_CHANGING(result); + FASTUNLOCK(); + return((GC_PTR)result); + } else { + result = (GC_PTR) + GC_generic_malloc((word)lb, STUBBORN); + } +record: + DISABLE_SIGNALS(); + LOCK(); + ADD_CHANGING(result); + UNLOCK(); + ENABLE_SIGNALS(); + return((GC_PTR)GC_clear_stack(result)); +} + + +/* Functions analogous to GC_read_dirty and GC_page_was_dirty. */ +/* Report pages on which stubborn objects were changed. */ +void GC_read_changed() +{ + register GC_PTR * p = GC_changing_list_start; + register GC_PTR q; + register struct hblk * h; + register word index; + + if (p == 0) /* initializing */ return; + BCOPY(GC_changed_pages, GC_prev_changed_pages, + (sizeof GC_changed_pages)); + BZERO(GC_changed_pages, (sizeof GC_changed_pages)); + for (; p <= GC_changing_list_current; p++) { + if ((q = *p) != 0) { + h = HBLKPTR(q); + index = PHT_HASH(h); + set_pht_entry_from_index(GC_changed_pages, index); + } + } +} + +GC_bool GC_page_was_changed(h) +struct hblk * h; +{ + register word index = PHT_HASH(h); + + return(get_pht_entry_from_index(GC_prev_changed_pages, index)); +} + +/* Remove unreachable entries from changed list. Should only be */ +/* called with mark bits consistent and lock held. */ +void GC_clean_changing_list() +{ + register GC_PTR * p = GC_changing_list_start; + register GC_PTR q; + register ptr_t r; + register unsigned long count = 0; + register unsigned long dropped_count = 0; + + if (p == 0) /* initializing */ return; + for (; p <= GC_changing_list_current; p++) { + if ((q = *p) != 0) { + count++; + r = (ptr_t)GC_base(q); + if (r == 0 || !GC_is_marked(r)) { + *p = 0; + dropped_count++; + } + } + } +# ifdef PRINTSTATS + if (count > 0) { + GC_printf2("%lu entries in changing list: reclaimed %lu\n", + (unsigned long)count, (unsigned long)dropped_count); + } +# endif +} + +#else /* !STUBBORN_ALLOC */ + +# ifdef __STDC__ + GC_PTR GC_malloc_stubborn(size_t lb) +# else + GC_PTR GC_malloc_stubborn(lb) + size_t lb; +# endif +{ + return(GC_malloc(lb)); +} + +/*ARGSUSED*/ +void GC_end_stubborn_change(p) +GC_PTR p; +{ +} + +/*ARGSUSED*/ +void GC_change_stubborn(p) +GC_PTR p; +{ +} + + +#endif |