| /* |
| * 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 "private/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; |
| |
| void GC_push_stubborn_structures GC_PROTO((void)) |
| { |
| GC_push_all((ptr_t)(&GC_changing_list_start), |
| (ptr_t)(&GC_changing_list_start) + sizeof(GC_PTR *)); |
| } |
| |
| # 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_INTERNAL_MALLOC( |
| (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_INTERNAL_MALLOC( |
| 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; |
| { |
| } |
| |
| void GC_push_stubborn_structures GC_PROTO((void)) |
| { |
| } |
| |
| #endif |