diff gc/stubborn.c @ 51488:5de98dce4bd1

*** empty log message ***
author Dave Love <fx@gnu.org>
date Thu, 05 Jun 2003 17:49:22 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gc/stubborn.c	Thu Jun 05 17:49:22 2003 +0000
@@ -0,0 +1,326 @@
+/* 
+ * 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