From af72c5c45274761a03e3584158b0a44532ed47a7 Mon Sep 17 00:00:00 2001
From: Martin Stjernholm <mast@lysator.liu.se>
Date: Sun, 2 Jul 2000 04:24:06 +0200
Subject: [PATCH] Fixed bugs when gc'ing strong links, among other things.

Rev: src/gc.c:1.98
Rev: src/gc.h:1.51
---
 src/gc.c | 204 +++++++++++++++++++++++++++++--------------------------
 src/gc.h |   8 +--
 2 files changed, 111 insertions(+), 101 deletions(-)

diff --git a/src/gc.c b/src/gc.c
index fef9a42d1b..88e2c8044e 100644
--- a/src/gc.c
+++ b/src/gc.c
@@ -29,7 +29,7 @@ struct callback *gc_evaluator_callback=0;
 
 #include "block_alloc.h"
 
-RCSID("$Id: gc.c,v 1.97 2000/06/16 23:43:49 hubbe Exp $");
+RCSID("$Id: gc.c,v 1.98 2000/07/02 02:24:06 mast Exp $");
 
 /* Run garbage collect approximately every time
  * 20 percent of all arrays, objects and programs is
@@ -1045,14 +1045,14 @@ int gc_mark(void *a)
 static int gc_cycle_indent = 0;
 #define CYCLE_DEBUG_MSG(M, TXT) do {					\
   fprintf(stderr, "%*s%-33s %p [%p] ", gc_cycle_indent, "",		\
-	  (TXT), (M)->data, gc_rec_last->data);				\
+	  (TXT), (M) ? (M)->data : 0, gc_rec_last->data);		\
   describe_marker(M);							\
 } while (0)
 #else
 #define CYCLE_DEBUG_MSG(M, TXT) do {} while (0)
 #endif
 
-static void break_cycle (struct marker *beg, struct marker *pos)
+static struct marker *break_cycle (struct marker *beg, struct marker *pos)
 {
   /* There's a cycle from beg to gc_rec_last which should be broken at
    * pos. Do it by switching places of the markers before and after pos. */
@@ -1063,26 +1063,28 @@ static void break_cycle (struct marker *beg, struct marker *pos)
 #endif
 
   if (beg->cycle) {
-#ifdef PIKE_DEBUG
-    if (pos->cycle == beg->cycle || gc_rec_last->cycle == beg->cycle)
-      gc_fatal(pos->data, 0, "Same cycle on both sides of broken link.\n");
-#endif
-    for (p = &rec_list; p->link->cycle != beg->cycle; p = p->link) {}
-    beg = p->link;
+    for (q = &rec_list; q->link->cycle != beg->cycle; q = q->link) {}
+    beg = q->link;
+    if (beg->cycle == pos->cycle) {
+      /* Breaking something previously marked as a cycle. Clear it
+       * since we're no longer sure it's an ambigious cycle. */
+      unsigned cycle = beg->cycle;
+      for (p = beg; p && p->cycle == cycle; p = p->link)
+	p->cycle = 0;
+    }
   }
   else
-    for (p = &rec_list; p->link != beg; p = p->link) {}
+    for (q = &rec_list; q->link != beg; q = q->link) {}
 
-  CYCLE_DEBUG_MSG(beg, "break_cycle, break from");
-  CYCLE_DEBUG_MSG(pos, "break_cycle, break at");
+  q->link = pos;
 
-  p->link = pos;
+  CYCLE_DEBUG_MSG(beg, "> break_cycle, begin at");
 
   for (p = beg; p->link != pos; p = p->link) {}
 
   for (q = pos;; q = q->link) {
     q->flags |= GC_MOVED_BACK|GC_DONT_POP;
-    CYCLE_DEBUG_MSG(q, "break_cycle, mark for don't pop");
+    CYCLE_DEBUG_MSG(q, "> break_cycle, mark for don't pop");
     if (q == gc_rec_last) break;
   }
 
@@ -1091,6 +1093,14 @@ static void break_cycle (struct marker *beg, struct marker *pos)
     q->link = beg;
     p->link = m;
   }
+
+  if (beg->flags & GC_WEAK_REF) {
+    beg->flags &= ~GC_WEAK_REF;
+    pos->flags |= GC_WEAK_REF;
+    CYCLE_DEBUG_MSG(pos, "> break_cycle, moved weak flag");
+  }
+
+  return beg;
 }
 
 int gc_cycle_push(void *x, struct marker *m, int weak)
@@ -1156,7 +1166,7 @@ int gc_cycle_push(void *x, struct marker *m, int weak)
 	gc_rec_last->flags &= ~GC_LIVE_RECURSE;
 #ifdef GC_CYCLE_DEBUG
 	gc_cycle_indent -= 2;
-	CYCLE_DEBUG_MSG(gc_rec_last, "gc_cycle_push, unwinding live");
+	CYCLE_DEBUG_MSG(gc_rec_last, "> gc_cycle_push, unwinding live");
 #endif
 	gc_rec_last = (struct marker *)
 	  dequeue_lifo(&gc_mark_queue, (queue_call) gc_set_rec_last);
@@ -1176,9 +1186,9 @@ int gc_cycle_push(void *x, struct marker *m, int weak)
   }
 
 #ifdef PIKE_DEBUG
-  if (weak < 0 && gc_rec_last->flags & GC_FOLLOWED_NONSTRONG)
-    gc_fatal(x, 0, "Followed strong link too late.\n");
-  if (weak >= 0) gc_rec_last->flags |= GC_FOLLOWED_NONSTRONG;
+  if (weak >= 0 && gc_rec_last->flags & GC_FOLLOWED_STRONG)
+    gc_fatal(x, 0, "Followed strong link too early.\n");
+  if (weak < 0) gc_rec_last->flags |= GC_FOLLOWED_STRONG;
 #endif
 
   if (m->flags & GC_IN_REC_LIST) { /* A cyclic reference is found. */
@@ -1191,6 +1201,7 @@ int gc_cycle_push(void *x, struct marker *m, int weak)
       struct marker *p, *weak_ref = 0, *nonstrong_ref = 0;
       if (!weak) {
 	struct marker *q;
+	CYCLE_DEBUG_MSG(m, "gc_cycle_push, search normal");
 	for (q = m, p = m->link; p; q = p, p = p->link) {
 	  if (p->flags & (GC_WEAK_REF|GC_STRONG_REF)) {
 	    if (p->flags & GC_WEAK_REF) weak_ref = p;
@@ -1201,19 +1212,21 @@ int gc_cycle_push(void *x, struct marker *m, int weak)
       }
 
       else if (weak < 0) {
+	CYCLE_DEBUG_MSG(m, "gc_cycle_push, search strong");
 	for (p = m->link; p; p = p->link) {
 	  if (p->flags & GC_WEAK_REF) weak_ref = p;
 	  if (!(p->flags & GC_STRONG_REF)) nonstrong_ref = p;
 	  if (p == gc_rec_last) break;
 	}
 #ifdef PIKE_DEBUG
-	if (!nonstrong_ref)
+	if (p == gc_rec_last && !nonstrong_ref)
 	  gc_fatal(x, 0, "Only strong links in cycle.\n");
 #endif
       }
 
       else {
 	struct marker *q;
+	CYCLE_DEBUG_MSG(m, "gc_cycle_push, search weak");
 	for (q = m, p = m->link; p; q = p, p = p->link) {
 	  if (!(p->flags & GC_WEAK_REF) && !nonstrong_ref) nonstrong_ref = q;
 	  if (p == gc_rec_last) break;
@@ -1226,16 +1239,20 @@ int gc_cycle_push(void *x, struct marker *m, int weak)
 	   * or more weak links in the cycle. Let's break it at the
 	   * last one (to ensure that a sequence of several weak links
 	   * are broken at the last one). */
-	  CYCLE_DEBUG_MSG(m, "gc_cycle_push, weak break");
+	  CYCLE_DEBUG_MSG(weak_ref, "gc_cycle_push, weak break");
 	  break_cycle (m, weak_ref);
 	}
 
 	else if (weak < 0) {
 	  /* The backward link is strong. Must break the cycle at the
 	   * last nonstrong link. */
-	  CYCLE_DEBUG_MSG(m, "gc_cycle_push, nonstrong break");
+	  if (m->flags & GC_STRONG_REF)
+	    nonstrong_ref->flags =
+	      (nonstrong_ref->flags & ~GC_WEAK_REF) | GC_STRONG_REF;
+	  else
+	    m->flags = (m->flags & ~GC_WEAK_REF) | GC_STRONG_REF;
+	  CYCLE_DEBUG_MSG(nonstrong_ref, "gc_cycle_push, nonstrong break");
 	  break_cycle (m, nonstrong_ref);
-	  if (m->flags & GC_STRONG_REF) nonstrong_ref->flags |= GC_STRONG_REF;
 	}
 
 	else if (nonstrong_ref) {
@@ -1243,40 +1260,44 @@ int gc_cycle_push(void *x, struct marker *m, int weak)
 	   * cycle with a nonweak link in it. Break before the first
 	   * link that's stronger than the others. */
 	  if (nonstrong_ref != m) {
-	    CYCLE_DEBUG_MSG(m, "gc_cycle_push, weaker break");
+	    CYCLE_DEBUG_MSG(nonstrong_ref, "gc_cycle_push, weaker break");
 	    break_cycle (m, nonstrong_ref);
 	  }
 	}
 
-	else {
-	  /* A normal or weak cycle which will be destructed in
-	   * arbitrary order. */
+	else if (!weak) {
+	  /* A normal cycle which will be destructed in arbitrary
+	   * order. For reasons having to do with strong links we
+	   * can't mark weak cycles this way. */
 	  unsigned cycle = m->cycle ? m->cycle : ++last_cycle;
 	  if (cycle == gc_rec_last->cycle)
 	    CYCLE_DEBUG_MSG(m, "gc_cycle_push, old cycle");
 	  else {
 	    unsigned replace_cycle = gc_rec_last->cycle;
 	    CYCLE_DEBUG_MSG(m, "gc_cycle_push, cycle");
-	    for (p = m; p != gc_rec_last; p = p->link) {
+	    for (p = m;; p = p->link) {
 	      p->cycle = cycle;
-	      CYCLE_DEBUG_MSG(p, "gc_cycle_push, mark cycle");
+	      CYCLE_DEBUG_MSG(p, "> gc_cycle_push, mark cycle");
+	      if (p == gc_rec_last) break;
 	    }
-	    if (replace_cycle != cycle)
-	      for (; p && p->cycle == replace_cycle; p = p->link) {
+	    if (replace_cycle && replace_cycle != cycle)
+	      for (p = p->link; p && p->cycle == replace_cycle; p = p->link) {
 		p->cycle = cycle;
-		CYCLE_DEBUG_MSG(p, "gc_cycle_push, re-mark cycle");
+		CYCLE_DEBUG_MSG(p, "> gc_cycle_push, re-mark cycle");
 	      }}}}		/* Mmm.. lisp ;) */
 
-      else			/* A forward reference. */
-	if (m->flags & GC_ON_STACK) {
-	  /* It's a reference to a marker that has been swapped
-	   * further down the list by break_cycle(). In that case we
-	   * must mark gc_rec_last to stay on the list. */
-	  CYCLE_DEBUG_MSG(m, "gc_cycle_push, mark for don't pop");
+      else {			/* A forward reference. */
+	CYCLE_DEBUG_MSG(m, "gc_cycle_push, forward ref");
+	if (m->flags & GC_ON_STACK &&
+	    !(gc_rec_last->cycle && gc_rec_last->cycle == m->cycle)) {
+	  /* Since it's not part of the same cycle it's a reference to
+	   * a marker that has been swapped further down the list by
+	   * break_cycle(). In that case we must mark gc_rec_last to
+	   * stay on the list. */
 	  gc_rec_last->flags |= GC_DONT_POP;
+	  CYCLE_DEBUG_MSG(gc_rec_last, "gc_cycle_push, mark for don't pop");
 	}
-	else
-	  CYCLE_DEBUG_MSG(m, "gc_cycle_push, forward ref");
+      }
     }
   }
 
@@ -1297,8 +1318,8 @@ int gc_cycle_push(void *x, struct marker *m, int weak)
 #endif
 
       p = gc_rec_last;
-      if (gc_rec_last->cycle)
-	for (; p->link && p->link->cycle == gc_rec_last->cycle; p = p->link) {}
+      if (p->cycle)
+	for (; p->link && p->link->cycle == p->cycle; p = p->link) {}
       m->link = p->link;
       p->link = m;
       m->flags |= GC_CYCLE_CHECKED|GC_ON_STACK|GC_IN_REC_LIST;
@@ -1364,7 +1385,7 @@ live_recurse:
 
 void gc_cycle_pop(void *a)
 {
-  struct marker *m = find_marker(a), *p, *q;
+  struct marker *m = find_marker(a), *p, *q, *base;
 
 #ifdef PIKE_DEBUG
   if (Pike_in_gc != GC_PASS_CYCLE)
@@ -1393,13 +1414,14 @@ void gc_cycle_pop(void *a)
   if (m->flags & GC_GOT_DEAD_REF)
     gc_fatal(a, 0, "Didn't expect a dead extra ref.\n");
 #endif
+  m->flags &= ~GC_ON_STACK;
 
   if (m->flags & GC_DONT_POP) {
 #ifdef PIKE_DEBUG
     if (!m->link)
-      gc_fatal(a, 0, "Found GC_DONT_POP marker on top of stack.\n");
+      gc_fatal(a, 0, "Found GC_DONT_POP marker on top of list.\n");
 #endif
-    CYCLE_DEBUG_MSG(m, "gc_cycle_pop, keep on stack");
+    CYCLE_DEBUG_MSG(m, "gc_cycle_pop, keep in list");
     if (!(m->flags & GC_MOVED_BACK)) {
       gc_rec_last->flags |= GC_DONT_POP;
       CYCLE_DEBUG_MSG(gc_rec_last, "gc_cycle_pop, propagate don't pop");
@@ -1407,78 +1429,66 @@ void gc_cycle_pop(void *a)
     return;
   }
 
-  q = gc_rec_last;
+  for (base = gc_rec_last, p = base->link;; base = p, p = p->link) {
+    if (base == m) {
+#ifdef GC_CYCLE_DEBUG
+      CYCLE_DEBUG_MSG(m, "gc_cycle_pop, keep cycle");
+#endif
+      return;
+    }
+    if (!(p->cycle && p->cycle == base->cycle))
+      break;
+  }
+
+  q = base;
   while (1) {
     p = q->link;
 #ifdef PIKE_DEBUG
-    if (p->flags & GC_LIVE_RECURSE)
-      gc_fatal(p->data, 0, "Marker still got GC_LIVE_RECURSE at pop.\n");
+    if (p->flags & (GC_ON_STACK|GC_LIVE_RECURSE))
+      gc_fatal(p->data, 0, "Marker still got an on-stack flag at pop.\n");
 #endif
 
     if (!p->cycle && !(p->flags & GC_LIVE_OBJ)) {
       ADD_REF_IF_DEAD(p);
-      p->flags &= ~(GC_ON_STACK|GC_IN_REC_LIST|GC_DONT_POP|
-		    GC_WEAK_REF|GC_STRONG_REF);
+      p->flags &= ~(GC_IN_REC_LIST|GC_DONT_POP|GC_WEAK_REF|GC_STRONG_REF);
       q->link = p->link;
       DO_IF_DEBUG(p->link = (struct marker *) -1);
       CYCLE_DEBUG_MSG(p, "gc_cycle_pop, pop off");
     }
+    else q = p;
 
-    else {
-      p->flags &= ~(GC_ON_STACK|GC_DONT_POP|
-		    GC_WEAK_REF|GC_STRONG_REF);
-      q = p;
-    }
     if (p == m) break;
   }
 
-  if (gc_rec_last != q) {
-    if (!q->cycle || gc_rec_last->cycle != q->cycle) {
-      /* If the thing(s) are part of a cycle that we aren't leaving,
-       * we let them stay on the list so the whole cycle is popped at
-       * once. Otherwise it's time to move live objects to the kill
-       * list. */
-      struct marker *base;
+  if (base != q) {
+    q = base;
+    while ((p = q->link)) {
 #ifdef PIKE_DEBUG
-      int cycle = q->cycle;
+      if (!(p->flags & GC_IN_REC_LIST))
+	gc_fatal(p->data, 0, "Marker being cycle popped doesn't have GC_IN_REC_LIST.\n");
+      if (p->flags & GC_GOT_DEAD_REF)
+	gc_fatal(p->data, 0, "Didn't expect a dead extra ref.\n");
 #endif
-
-      base = gc_rec_last;
-      if (gc_rec_last->cycle)
-	for (; base->link->cycle == gc_rec_last->cycle; base = base->link) {}
-
-      q = base;
-      while ((p = q->link)) {
-#ifdef PIKE_DEBUG
-	if (p->cycle && cycle && p->cycle != cycle)
-	  gc_fatal(p->data, 0, "Popping more than one cycle from rec_list.\n");
-	if (!(p->flags & GC_IN_REC_LIST))
-	  gc_fatal(p->data, 0, "Marker being cycle popped doesn't have GC_IN_REC_LIST.\n");
-	if (p->flags & GC_GOT_DEAD_REF)
-	  gc_fatal(p->data, 0, "Didn't expect a dead extra ref.\n");
-#endif
-	p->flags &= ~GC_IN_REC_LIST;
-
-	if (p->flags & GC_LIVE_OBJ) {
-	  /* This extra ref is taken away in the kill pass. */
-	  gc_add_extra_ref(p->data);
-	  q = p;
-	  CYCLE_DEBUG_MSG(p, "gc_cycle_pop, put on kill list");
-	}
-	else {
-	  ADD_REF_IF_DEAD(p);
-	  q->link = p->link;
-	  DO_IF_DEBUG(p->link = (struct marker *) -1);
-	  CYCLE_DEBUG_MSG(p, "gc_cycle_pop, cycle pop off");
-	}
+      p->flags &= ~GC_IN_REC_LIST;
+
+      p->flags &= ~(GC_DONT_POP|GC_WEAK_REF|GC_STRONG_REF);
+      if (p->flags & GC_LIVE_OBJ) {
+	/* This extra ref is taken away in the kill pass. */
+	gc_add_extra_ref(p->data);
+	q = p;
+	CYCLE_DEBUG_MSG(p, "gc_cycle_pop, put on kill list");
+      }
+      else {
+	ADD_REF_IF_DEAD(p);
+	q->link = p->link;
+	DO_IF_DEBUG(p->link = (struct marker *) -1);
+	CYCLE_DEBUG_MSG(p, "gc_cycle_pop, cycle pop off");
       }
-
-      q->link = kill_list;
-      kill_list = base->link;
-      base->link = p;
     }
-    else
-      CYCLE_DEBUG_MSG(m, "gc_cycle_pop, keep in cycle");
+
+    q->link = kill_list;
+    kill_list = base->link;
+    base->link = p;
   }
 }
 
diff --git a/src/gc.h b/src/gc.h
index 74acc78ba2..9eb74ed7e2 100644
--- a/src/gc.h
+++ b/src/gc.h
@@ -1,5 +1,5 @@
 /*
- * $Id: gc.h,v 1.50 2000/06/12 21:41:41 mast Exp $
+ * $Id: gc.h,v 1.51 2000/07/02 02:24:06 mast Exp $
  */
 #ifndef GC_H
 #define GC_H
@@ -96,7 +96,7 @@ struct marker
 #define GC_XREFERENCED		0x040000
 #define GC_DO_FREE		0x080000
 #define GC_GOT_EXTRA_REF	0x100000
-#define GC_FOLLOWED_NONSTRONG	0x200000
+#define GC_FOLLOWED_STRONG	0x200000
 
 #include "block_alloc_h.h"
 PTR_HASH_ALLOC(marker,MARKER_CHUNK_SIZE)
@@ -218,8 +218,8 @@ extern int gc_in_cycle_check;
 
 /* Use WEAK < 0 for strong links. The gc makes these assumptions about
  * those:
- * 1.  All strong links are recursed before any other links, i.e.
- *     strong links should be pushed last into the lifo queue.
+ * 1.  All strong links are recursed after any other links, i.e.
+ *     strong links should be pushed first into the lifo queue.
  * 2.  There can never be a cycle consisting of only strong links.
  */
 
-- 
GitLab