aboutsummaryrefslogtreecommitdiffstats
path: root/gc/stubborn.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--gc/stubborn.c317
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