diff --git a/src/array.c b/src/array.c
index f62b346c21715eef13bab79770e1cb7a77c7c2de..77a8f64ae3b13d6592aed25dd1dece526c18f503 100644
--- a/src/array.c
+++ b/src/array.c
@@ -23,7 +23,7 @@
 #include "stuff.h"
 #include "bignum.h"
 
-RCSID("$Id: array.c,v 1.77 2000/07/11 03:45:09 mast Exp $");
+RCSID("$Id: array.c,v 1.78 2000/07/18 05:48:20 mast Exp $");
 
 struct array empty_array=
 {
@@ -1861,40 +1861,28 @@ void gc_mark_array_as_referenced(struct array *a)
   }
 }
 
-static void low_gc_cycle_check_array(struct array *a)
+void real_gc_cycle_check_array(struct array *a, int weak)
 {
-  int e;
+  GC_CYCLE_ENTER(a, weak) {
+    int e;
 #ifdef PIKE_DEBUG
-  if (a == &empty_array) fatal("Trying to gc cycle check empty_array.\n");
+    if (a == &empty_array) fatal("Trying to gc cycle check empty_array.\n");
 #endif
 
-  if (a->type_field & BIT_COMPLEX)
-  {
-    if (a->flags & ARRAY_WEAK_FLAG)
-      gc_recurse_weak_array(a, gc_cycle_check_weak_svalues);
-    else {
-      TYPE_FIELD t;
-      if ((t = gc_cycle_check_svalues(ITEM(a), a->size))) {
-	if(!(a->type_field & BIT_UNFINISHED) || a->refs!=1)
-	  a->type_field = t;
-	else
-	  a->type_field |= t;
+    if (a->type_field & BIT_COMPLEX)
+    {
+      if (a->flags & ARRAY_WEAK_FLAG)
+	gc_recurse_weak_array(a, gc_cycle_check_weak_svalues);
+      else {
+	TYPE_FIELD t;
+	if ((t = gc_cycle_check_svalues(ITEM(a), a->size))) {
+	  if(!(a->type_field & BIT_UNFINISHED) || a->refs!=1)
+	    a->type_field = t;
+	  else
+	    a->type_field |= t;
+	}
       }
     }
-  }
-}
-
-void real_gc_cycle_check_array(struct array *a)
-{
-  GC_CYCLE_ENTER(a, 0) {
-    low_gc_cycle_check_array(a);
-  } GC_CYCLE_LEAVE;
-}
-
-void real_gc_cycle_check_array_weak(struct array *a)
-{
-  GC_CYCLE_ENTER(a, 1) {
-    low_gc_cycle_check_array(a);
   } GC_CYCLE_LEAVE;
 }
 
@@ -1948,28 +1936,26 @@ void gc_cycle_check_all_arrays(void)
 {
   struct array *a;
   for (a = gc_internal_array; a != &empty_array; a = a->next) {
-    real_gc_cycle_check_array(a);
-    run_lifo_queue(&gc_mark_queue);
+    real_gc_cycle_check_array(a, 0);
+    gc_cycle_run_queue();
   }
 }
 
+void gc_zap_ext_weak_refs_in_arrays(void)
+{
+  gc_mark_array_pos = empty_array.next;
+  while (gc_mark_array_pos != gc_internal_array && gc_ext_weak_refs) {
+    struct array *a = gc_mark_array_pos;
+    gc_mark_array_pos = a->next;
+    gc_mark_array_as_referenced(a);
+  }
+  discard_queue(&gc_mark_queue);
+}
+
 void gc_free_all_unreferenced_arrays(void)
 {
   struct array *a,*next;
 
-  if (gc_ext_weak_refs) {
-    /* Have to go through all marked things if we got external weak
-     * references to otherwise unreferenced things, so the mark
-     * functions can free those references. */
-    gc_mark_array_pos = empty_array.next;
-    while (gc_mark_array_pos != gc_internal_array && gc_ext_weak_refs) {
-      struct array *a = gc_mark_array_pos;
-      gc_mark_array_pos = a->next;
-      gc_mark_array_as_referenced(a);
-    }
-    discard_queue(&gc_mark_queue);
-  }
-
   for (a = gc_internal_array; a != &empty_array; a = next)
   {
 #ifdef PIKE_DEBUG
diff --git a/src/array.h b/src/array.h
index cd662a2ec749ddc9d519a74adfeb8805a93cd7a6..daa5a2d1190e4277b5d848355537a44b09e703b0 100644
--- a/src/array.h
+++ b/src/array.h
@@ -5,7 +5,7 @@
 \*/
 
 /*
- * $Id: array.h,v 1.20 2000/06/09 22:43:04 mast Exp $
+ * $Id: array.h,v 1.21 2000/07/18 05:48:20 mast Exp $
  */
 #ifndef ARRAY_H
 #define ARRAY_H
@@ -165,9 +165,9 @@ void gc_mark_array_as_referenced(struct array *a);
 unsigned gc_touch_all_arrays(void);
 void gc_check_all_arrays(void);
 void gc_mark_all_arrays(void);
-void real_gc_cycle_check_array(struct array *a);
-void real_gc_cycle_check_array_weak(struct array *a);
+void real_gc_cycle_check_array(struct array *a, int weak);
 void gc_cycle_check_all_arrays(void);
+void gc_zap_ext_weak_refs_in_arrays(void);
 void gc_free_all_unreferenced_arrays(void);
 void debug_dump_type_field(TYPE_FIELD t);
 void debug_dump_array(struct array *a);
@@ -177,9 +177,7 @@ struct array *explode_array(struct array *a, struct array *b);
 struct array *implode_array(struct array *a, struct array *b);
 /* Prototypes end here */
 
-#define gc_cycle_check_array(X) \
-  enqueue_lifo(&gc_mark_queue, (queue_call) real_gc_cycle_check_array, (X))
-#define gc_cycle_check_array_weak(X) \
-  enqueue_lifo(&gc_mark_queue, (queue_call) real_gc_cycle_check_array_weak, (X))
+#define gc_cycle_check_array(X, WEAK) \
+  gc_cycle_enqueue((gc_cycle_check_cb *) real_gc_cycle_check_array, (X), (WEAK))
 
 #endif /* ARRAY_H */
diff --git a/src/gc.c b/src/gc.c
index 86c923d79f1246e3b058b8b1d20c1cdeb667df39..59532f62af67d1d74952a938eb10076fbde9d712 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.106 2000/07/11 04:42:37 mast Exp $");
+RCSID("$Id: gc.c,v 1.107 2000/07/18 05:48:20 mast Exp $");
 
 /* Run garbage collect approximately every time
  * 20 percent of all arrays, objects and programs is
@@ -41,7 +41,7 @@ RCSID("$Id: gc.c,v 1.106 2000/07/11 04:42:37 mast Exp $");
 #define MAX_ALLOC_THRESHOLD 10000000
 #define MULTIPLIER 0.9
 #define MARKER_CHUNK_SIZE 1023
-#define REF_CYCLE_CHUNK_SIZE 32
+#define GC_LINK_CHUNK_SIZE 31
 
 /* The gc will free all things with no external references that isn't
  * referenced by undestructed objects with destroy() lfuns (known as
@@ -82,6 +82,8 @@ RCSID("$Id: gc.c,v 1.106 2000/07/11 04:42:37 mast Exp $");
 /* #define GC_VERBOSE */
 /* #define GC_CYCLE_DEBUG */
 
+/* #define GC_STACK_DEBUG */
+
 #if defined(GC_VERBOSE) && !defined(PIKE_DEBUG)
 #undef GC_VERBOSE
 #endif
@@ -98,33 +100,85 @@ int Pike_in_gc = 0;
 struct pike_queue gc_mark_queue;
 time_t last_gc;
 
-static struct marker rec_list = {0, 0, 0};
-struct marker *gc_rec_last = &rec_list;
-static struct marker *kill_list = 0;
+struct gc_frame
+{
+  struct gc_frame *back;	/* Previous stack frame. */
+  void *data;
+  union {
+    struct {			/* Pop frame. */
+      struct gc_frame *prev;	/* Previous frame in rec_list. */
+      struct gc_frame *next;	/* Next pointer in rec_list and kill_list. */
+      unsigned INT16 cycle;	/* Cycle id number. */
+    } pop;
+    struct {			/* Link frame. */
+      gc_cycle_check_cb *checkfn;
+      int weak;
+    } link;
+  } u;
+  unsigned INT16 frameflags;
+};
+
+#define GC_POP_FRAME		0x01
+#define GC_WEAK_REF		0x02
+#define GC_STRONG_REF		0x04
+#define GC_OFF_STACK		0x08
+#ifdef PIKE_DEBUG
+#define GC_LINK_FREED		0x10
+#define GC_FOLLOWED_NONSTRONG	0x20
+#endif
+
+#undef BLOCK_ALLOC_NEXT
+#define BLOCK_ALLOC_NEXT back
+
+BLOCK_ALLOC(gc_frame,GC_LINK_CHUNK_SIZE)
+
+#define PREV(frame) ((frame)->u.pop.prev)
+#define NEXT(frame) ((frame)->u.pop.next)
+#define CYCLE(frame) ((frame)->u.pop.cycle)
+
+#ifdef PIKE_DEBUG
+#define CHECK_POP_FRAME(frame) do {					\
+  if ((frame)->frameflags & GC_LINK_FREED)				\
+    gc_fatal((frame)->data, 0, "Accessing freed gc_frame.\n");		\
+  if (!((frame)->frameflags & GC_POP_FRAME))				\
+    gc_fatal((frame)->data, 0, #frame " is not a pop frame.\n");	\
+  if (NEXT(PREV(frame)) != (frame))					\
+    gc_fatal((frame)->data, 0,						\
+	     "Pop frame pointers are inconsistent.\n");			\
+} while (0)
+#else
+#define CHECK_POP_FRAME(frame) do {} while (0)
+#endif
+
+static struct gc_frame rec_list = {0, 0, {{0, 0, 0}}, GC_POP_FRAME};
+static struct gc_frame *gc_rec_last = &rec_list, *gc_rec_top = 0;
+static struct gc_frame *kill_list = 0;
+
 static unsigned last_cycle;
 size_t gc_ext_weak_refs;
 
-/* rec_list is a linked list of the markers currently being recursed
- * through in the cycle check pass. gc_rec_last points at the
- * innermost marker being visited. A new marker is linked in after
- * gc_rec_last, except when that's inside a cycle, in which case it's
- * linked in after that cycle. A cycle is always treated as one atomic
- * unit, e.g. it's either popped whole or not at all.
+/* gc_frame objects are used as frames in a recursion stack during the
+ * cycle check pass. gc_rec_top points to the current top of the
+ * stack. When a thing is recursed, a pop frame is first pushed on the
+ * stack and then the gc_cycle_check_* function fills in with link
+ * frames for every reference the thing contains.
  *
- * Two ranges of markers next to each other may swap places to break a
- * cyclic reference at a chosen point. Some markers thus get a place
- * earlier on the list even though their corresponding stack frames
- * are later than some other markers they have references to. These
- * markers are flagged to make them stay on the list when they're
- * popped from the stack. They'll get popped eventually since all
- * markers in the gap between the top one and it's previous
- * gc_rec_last point are popped.
+ * rec_list is a double linked list of the pop frames on the stack,
+ * and that list represents the current prospective destruct order.
+ * gc_rec_last points at the last frame in the list and new frames are
+ * linked in after it. A cycle is always treated as one atomic unit,
+ * e.g. it's either popped whole or not at all. That means that
+ * rec_list may contain frames that are no longer on the stack.
  *
- * Markers for live objects are linked into the beginning of kill_list
+ * A range of frames which always ends at the end of the list, may be
+ * rotated a number of slots to break a cyclic reference at a chosen
+ * point. The stack of link frames are rotated simultaneously.
+ *
+ * Frames for live objects are linked into the beginning of kill_list
  * when they're popped from rec_list.
  *
  * The cycle check functions might recurse another round through the
- * markers that have been recursed already, to propagate the GC_LIVE
+ * frames that have been recursed already, to propagate the GC_LIVE
  * flag to things that have been found to be referenced from live
  * objects. rec_list is not touched at all in this extra round.
  */
@@ -141,17 +195,22 @@ struct callback *debug_add_gc_callback(callback_func call,
   return add_to_callback(&gc_callbacks, call, arg, free_func);
 }
 
+static void gc_cycle_pop(void *a);
+
+
+#undef BLOCK_ALLOC_NEXT
+#define BLOCK_ALLOC_NEXT next
 
 #undef INIT_BLOCK
 #ifdef PIKE_DEBUG
 #define INIT_BLOCK(X)					\
   (X)->flags=(X)->refs=(X)->weak_refs=(X)->xrefs=0;	\
   (X)->saved_refs=-1;					\
-  (X)->cycle = (unsigned INT16) -1;			\
-  (X)->link = (struct marker *) -1;
+  (X)->frame = 0;
 #else
 #define INIT_BLOCK(X)					\
-  (X)->flags=(X)->refs=(X)->weak_refs=0;
+  (X)->flags=(X)->refs=(X)->weak_refs=0;		\
+  (X)->frame = 0;
 #endif
 
 PTR_HASH_ALLOC(marker,MARKER_CHUNK_SIZE)
@@ -160,6 +219,7 @@ PTR_HASH_ALLOC(marker,MARKER_CHUNK_SIZE)
 
 int gc_in_cycle_check = 0;
 static unsigned weak_freed, checked, marked, cycle_checked, live_ref;
+static unsigned max_gc_frames, num_gc_frames = 0;
 static unsigned gc_extra_refs = 0;
 
 void dump_gc_info(void)
@@ -407,13 +467,32 @@ void describe_location(void *real_memblock,
 #endif
 }
 
+static void describe_gc_frame(struct gc_frame *l)
+{
+  if (l->frameflags & GC_POP_FRAME)
+    fprintf(stderr, "back=%p, prev=%p, next=%p, data=%p, cycle=%u, flags=0x%02x",
+	    l->back, PREV(l), NEXT(l), l->data, CYCLE(l), l->frameflags);
+  else
+    fprintf(stderr, "LINK back=%p, data=%p, weak=%d, flags=0x%02x",
+	    l->back, l->data, l->u.link.weak, l->frameflags);
+}
+
 static void describe_marker(struct marker *m)
 {
-  if (m)
+  if (m) {
     fprintf(stderr, "marker at %p: flags=0x%06x, refs=%d, weak=%d, "
-	    "xrefs=%d, saved=%d, cycle=%d, link=%p\n",
+	    "xrefs=%d, saved=%d, frame=%p",
 	    m, m->flags, m->refs, m->weak_refs,
-	    m->xrefs, m->saved_refs, m->cycle, m->link);
+	    m->xrefs, m->saved_refs, m->frame);
+#ifdef PIKE_DEBUG
+    if (m->frame) {
+      fputs(" [", stderr);
+      describe_gc_frame(m->frame);
+      putc(']', stderr);
+    }
+#endif
+    putc('\n', stderr);
+  }
   else
     fprintf(stderr, "no marker\n");
 }
@@ -749,9 +828,9 @@ void debug_gc_touch(void *a)
       if (!(m->flags & GC_TOUCHED))
 	gc_fatal(a, 2, "An existing but untouched marker found "
 		 "for object in linked lists.\n");
-      else if (m->flags & (GC_ON_STACK|GC_IN_REC_LIST|GC_DONT_POP|
-			   GC_LIVE_RECURSE|GC_WEAK_REF|GC_STRONG_REF))
-	gc_fatal(a, 2, "Marker still got flag from recurse list.\n");
+      else if (m->flags & GC_LIVE_RECURSE ||
+	       (m->frame && m->frame->frameflags & (GC_WEAK_REF|GC_STRONG_REF)))
+	gc_fatal(a, 2, "Thing still got flag from recurse list.\n");
       else if (m->flags & GC_MARKED)
 	return;
       else if (!(m->flags & GC_NOT_REFERENCED) || m->flags & GC_XREFERENCED)
@@ -866,6 +945,7 @@ INT32 real_gc_check_weak(void *a)
 static void init_gc(void)
 {
   init_marker_hash();
+  get_marker(rec_list.data);	/* Used to simplify fencepost conditions. */
 }
 
 static void exit_gc(void)
@@ -878,6 +958,10 @@ static void exit_gc(void)
       remove_marker(marker_hash_table[e]->data);
 #endif
   exit_marker_hash();
+  free_all_gc_frame_blocks();
+#ifdef GC_VERBOSE
+  num_gc_frames = 0;
+#endif
 }
 
 #ifdef PIKE_DEBUG
@@ -1030,7 +1114,7 @@ int gc_do_weak_free(void *a)
 
   if (!a) fatal("Got null pointer.\n");
   if (Pike_in_gc != GC_PASS_MARK && Pike_in_gc != GC_PASS_CYCLE &&
-      Pike_in_gc != GC_PASS_FREE)
+      Pike_in_gc != GC_PASS_ZAP_WEAK)
     fatal("gc_do_weak_free() called in invalid gc pass.\n");
   if (gc_debug) {
     if (!(m = find_marker(a)))
@@ -1042,7 +1126,7 @@ int gc_do_weak_free(void *a)
   if (m->weak_refs > m->refs)
     gc_fatal(a, 0, "More weak references than internal references.\n");
 
-  if (Pike_in_gc != GC_PASS_FREE) {
+  if (Pike_in_gc != GC_PASS_ZAP_WEAK) {
     if (m->weak_refs == -1) {
       gc_ext_weak_refs--;
       return 1;
@@ -1060,6 +1144,22 @@ int gc_do_weak_free(void *a)
   return 0;
 }
 
+void debug_really_free_gc_frame(struct gc_frame *l)
+{
+  if (l->frameflags & GC_LINK_FREED)
+    gc_fatal(l->data, 0, "Freeing freed gc_frame.\n");
+  l->frameflags |= GC_LINK_FREED;
+  l->back = PREV(l) = NEXT(l) = (struct gc_frame *) -1;
+  really_free_gc_frame(l);
+#ifdef GC_VERBOSE
+  num_gc_frames--;
+#endif
+}
+
+#else  /* PIKE_DEBUG */
+
+#define debug_really_free_gc_frame(l) really_free_gc_frame(l)
+
 #endif /* PIKE_DEBUG */
 
 int gc_mark(void *a)
@@ -1068,22 +1168,22 @@ int gc_mark(void *a)
 
 #ifdef PIKE_DEBUG
   if (!a) fatal("Got null pointer.\n");
-  if (Pike_in_gc != GC_PASS_MARK && Pike_in_gc != GC_PASS_FREE)
+  if (Pike_in_gc != GC_PASS_MARK && Pike_in_gc != GC_PASS_ZAP_WEAK)
     fatal("gc mark attempted in invalid pass.\n");
   if (!*(INT32 *) a)
     gc_fatal(a, 0, "Marked a thing without refs.\n");
   if (m->weak_refs == -1)
     gc_fatal(a, 0, "Marking thing scheduled for weak free.\n");
-  if (Pike_in_gc == GC_PASS_FREE && !(m->flags & GC_MARKED))
-    gc_fatal(a, 0, "gc_mark() called for thing in free pass "
+  if (Pike_in_gc == GC_PASS_ZAP_WEAK && !(m->flags & GC_MARKED))
+    gc_fatal(a, 0, "gc_mark() called for thing in zap weak pass "
 	     "that wasn't marked before.\n");
 #endif
 
-  if (Pike_in_gc == GC_PASS_FREE)
-    /* Things are visited in the free pass through the mark functions
-     * to free refs to internal things that only got weak external
-     * references. That happens only when a thing also have internal
-     * cyclic non-weak refs. */
+  if (Pike_in_gc == GC_PASS_ZAP_WEAK)
+    /* Things are visited in the zap weak pass through the mark
+     * functions to free refs to internal things that only got weak
+     * external references. That happens only when a thing also have
+     * internal cyclic non-weak refs. */
     if (m->flags & GC_FREE_VISITED)
       return 0;
     else {
@@ -1109,10 +1209,75 @@ int gc_mark(void *a)
   }
 }
 
+void gc_cycle_enqueue(gc_cycle_check_cb *checkfn, void *data, int weak)
+{
+  struct gc_frame *l = alloc_gc_frame();
+#ifdef GC_VERBOSE
+  if (++num_gc_frames > max_gc_frames) max_gc_frames = num_gc_frames;
+#endif
+  l->data = data;
+  l->u.link.checkfn = checkfn;
+  l->u.link.weak = weak;
+  l->frameflags = 0;
+  l->back = gc_rec_top;
+#ifdef GC_STACK_DEBUG
+  fprintf(stderr, "enqueue %p [%p]: ", l, gc_rec_top);
+  describe_gc_frame(l);
+  fputc('\n', stderr);
+#endif
+  gc_rec_top = l;
+}
+
+static struct gc_frame *gc_cycle_enqueue_pop(void *data)
+{
+  struct gc_frame *l = alloc_gc_frame();
+#ifdef GC_VERBOSE
+  if (++num_gc_frames > max_gc_frames) max_gc_frames = num_gc_frames;
+#endif
+  l->data = data;
+  PREV(l) = gc_rec_last;
+  NEXT(l) = 0;
+  CYCLE(l) = 0;
+  l->frameflags = GC_POP_FRAME;
+  l->back = gc_rec_top;
+#ifdef GC_STACK_DEBUG
+  fprintf(stderr, "enqueue %p [%p]: ", l, gc_rec_top);
+  describe_gc_frame(l);
+  fputc('\n', stderr);
+#endif
+  gc_rec_top = l;
+  return l;
+}
+
+void gc_cycle_run_queue()
+{
+  while (gc_rec_top) {
+#ifdef GC_STACK_DEBUG
+    fprintf(stderr, "dequeue %p [%p]: ", gc_rec_top, gc_rec_top->back);
+    describe_gc_frame(gc_rec_top);
+    fputc('\n', stderr);
+#endif
+    if (gc_rec_top->frameflags & GC_POP_FRAME) {
+      struct gc_frame *l = gc_rec_top->back;
+      gc_cycle_pop(gc_rec_top->data);
+      gc_rec_top = l;
+    } else {
+      struct gc_frame l = *gc_rec_top;
+#ifdef PIKE_DEBUG
+      if (l.frameflags & GC_LINK_FREED)
+	gc_fatal(l.data, 0, "Accessing freed gc_frame.\n");
+#endif
+      debug_really_free_gc_frame(gc_rec_top);
+      gc_rec_top = l.back;
+      l.u.link.checkfn(l.data, l.u.link.weak);
+    }
+  }
+}
+
 #ifdef GC_CYCLE_DEBUG
 static int gc_cycle_indent = 0;
 #define CYCLE_DEBUG_MSG(M, TXT) do {					\
-  fprintf(stderr, "%*s%-33s %p [%p] ", gc_cycle_indent, "",		\
+  fprintf(stderr, "%*s%-35s %p [%p] ", gc_cycle_indent, "",		\
 	  (TXT), (M) ? (M)->data : 0, gc_rec_last->data);		\
   describe_marker(M);							\
 } while (0)
@@ -1120,59 +1285,99 @@ static int gc_cycle_indent = 0;
 #define CYCLE_DEBUG_MSG(M, TXT) do {} while (0)
 #endif
 
-static struct marker *break_cycle (struct marker *beg, struct marker *pos)
+static void rotate_rec_list (struct gc_frame *beg, struct gc_frame *pos)
+/* Rotates the marker list and the cycle stack so the bit from pos
+ * down to the end gets before the bit from beg down to 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. */
-  struct marker *p, *q;
+  struct gc_frame *l;
+
 #ifdef PIKE_DEBUG
+  CHECK_POP_FRAME(beg);
+  CHECK_POP_FRAME(pos);
   if (beg == pos)
     gc_fatal(beg->data, 0, "Cycle already broken at requested position.\n");
+  if (NEXT(gc_rec_last))
+    gc_fatal(gc_rec_last->data, 0, "gc_rec_last not at end.\n");
 #endif
 
-  if (beg->cycle) {
-    for (q = &rec_list; q->link->cycle != beg->cycle; q = q->link) {}
-    beg = q->link;
-    if (beg->cycle == pos->cycle) {
+#ifdef GC_STACK_DEBUG
+  fprintf(stderr,"Stack before:\n");
+  for (l = gc_rec_top; l; l = l->back) {
+    fprintf(stderr, "  %p ", l);
+    describe_gc_frame(l);
+    fputc('\n', stderr);
+  }
+#endif
+
+  if (CYCLE(beg)) {
+    for (l = beg; CYCLE(PREV(l)) == CYCLE(beg); l = PREV(l))
+      CHECK_POP_FRAME(l);
+    CHECK_POP_FRAME(l);
+    if (CYCLE(l) == CYCLE(pos)) {
       /* 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;
+      unsigned cycle = CYCLE(l);
+      for (; l && CYCLE(l) == cycle; l = NEXT(l)) {
+	CHECK_POP_FRAME(l);
+#ifdef GC_CYCLE_DEBUG
+	if (CYCLE(l))
+	  CYCLE_DEBUG_MSG(find_marker(l->data), "> rotate_rec_list, clear cycle");
+#endif
+	CYCLE(l) = 0;
+      }
     }
+    else beg = l;		/* Keep the cycle continuous. */
   }
-  else
-    for (q = &rec_list; q->link != beg; q = q->link) {}
 
-  q->link = pos;
+  CYCLE_DEBUG_MSG(find_marker(beg->data), "> rotate_rec_list, begin at");
 
-  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");
-    if (q == gc_rec_last) break;
+  {
+    struct gc_frame *b = beg, *p = pos, *old_rec_top;
+    while (b->frameflags & GC_OFF_STACK) {
+      if ((b = NEXT(b)) == pos) goto done;
+      CHECK_POP_FRAME(b);
+    }
+    while (p->frameflags & GC_OFF_STACK) {
+      if (!(p = NEXT(p))) goto done;
+      CHECK_POP_FRAME(p);
+    }
+    old_rec_top = gc_rec_top;
+    gc_rec_top = p->back;
+    p->back = b->back;
+    b->back = old_rec_top;
   }
+done:
 
   {
-    struct marker *m = q->link;
-    q->link = beg;
-    p->link = m;
+    struct gc_frame *new_rec_last = PREV(pos);
+    NEXT(PREV(beg)) = pos;
+    PREV(pos) = PREV(beg);
+    NEXT(gc_rec_last) = beg;
+    PREV(beg) = gc_rec_last;
+    gc_rec_last = new_rec_last;
+    NEXT(gc_rec_last) = 0;
   }
 
-  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");
+  if (beg->frameflags & GC_WEAK_REF) {
+    beg->frameflags &= ~GC_WEAK_REF;
+    pos->frameflags |= GC_WEAK_REF;
+    CYCLE_DEBUG_MSG(get_marker(pos->data), "> rotate_rec_list, moved weak flag");
   }
 
-  return beg;
+#ifdef GC_STACK_DEBUG
+  fprintf(stderr,"Stack after:\n");
+  for (l = gc_rec_top; l; l = l->back) {
+    fprintf(stderr, "  %p ", l);
+    describe_gc_frame(l);
+    fputc('\n', stderr);
+  }
+#endif
 }
 
 int gc_cycle_push(void *x, struct marker *m, int weak)
 {
+  struct marker *last = find_marker(gc_rec_last->data);
+
 #ifdef PIKE_DEBUG
   if (!x) fatal("Got null pointer.\n");
   if (m->data != x) fatal("Got wrong marker.\n");
@@ -1208,9 +1413,9 @@ int gc_cycle_push(void *x, struct marker *m, int weak)
   }
 #endif
 
-  if (gc_rec_last->flags & GC_LIVE_RECURSE) {
+  if (last->flags & GC_LIVE_RECURSE) {
 #ifdef PIKE_DEBUG
-    if (!(gc_rec_last->flags & GC_LIVE))
+    if (!(last->flags & GC_LIVE))
       gc_fatal(x, 0, "Doing live recursion from a dead thing.\n");
 #endif
 
@@ -1232,32 +1437,37 @@ int gc_cycle_push(void *x, struct marker *m, int weak)
       int flags;
       CYCLE_DEBUG_MSG(m, "gc_cycle_push, live rec done");
       do {
-	gc_rec_last->flags &= ~GC_LIVE_RECURSE;
+	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(find_marker(gc_rec_last->data),
+			"> gc_cycle_push, unwinding live");
 #endif
-	gc_rec_last = (struct marker *)
-	  dequeue_lifo(&gc_mark_queue, (queue_call) gc_set_rec_last);
+	while (1) {
+	  struct gc_frame *l = gc_rec_top;
 #ifdef PIKE_DEBUG
-	if (!gc_rec_last)
-	  fatal("Expected a gc_set_rec_last entry in gc_mark_queue.\n");
+	  if (!gc_rec_top)
+	    fatal("Expected a gc_cycle_pop entry in gc_rec_top.\n");
 #endif
-      } while (gc_rec_last->flags & GC_LIVE_RECURSE);
-      if (!dequeue_lifo(&gc_mark_queue, (queue_call) gc_cycle_pop)) {
-#ifdef PIKE_DEBUG
-	fatal("Expected a gc_cycle_pop entry in gc_mark_queue.\n");
-#endif
-      }
+	  gc_rec_top = l->back;
+	  if (l->frameflags & GC_POP_FRAME) {
+	    gc_rec_last = PREV(l);
+	    debug_really_free_gc_frame(l);
+	    break;
+	  }
+	  debug_really_free_gc_frame(l);
+	}
+	last = find_marker(gc_rec_last->data);
+      } while (last->flags & GC_LIVE_RECURSE);
     }
 
     return 0;
   }
 
 #ifdef PIKE_DEBUG
-  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;
+  if (weak < 0 && gc_rec_last->frameflags & GC_FOLLOWED_NONSTRONG)
+    gc_fatal(x, 0, "Followed strong link too late.\n");
+  if (weak >= 0) gc_rec_last->frameflags |= GC_FOLLOWED_NONSTRONG;
 #endif
 
   if (weak > 0) {
@@ -1269,20 +1479,24 @@ int gc_cycle_push(void *x, struct marker *m, int weak)
     gc_ext_weak_refs--;
   }
 
-  if (m->flags & GC_IN_REC_LIST) { /* A cyclic reference is found. */
+  if (m->frame && !(m->frame->frameflags & GC_OFF_STACK)) {
+    /* A cyclic reference is found. */
 #ifdef PIKE_DEBUG
-    if (m == &rec_list || gc_rec_last == &rec_list)
+    if (gc_rec_last == &rec_list)
       gc_fatal(x, 0, "Cyclic ref involves dummy rec_list marker.\n");
+    CHECK_POP_FRAME(gc_rec_last);
+    CHECK_POP_FRAME(m->frame);
 #endif
 
-    if (m != gc_rec_last) {
-      struct marker *p, *weak_ref = 0, *nonstrong_ref = 0;
+    if (m != last) {
+      struct gc_frame *p, *weak_ref = 0, *nonstrong_ref = 0;
       if (!weak) {
-	struct marker *q;
+	struct gc_frame *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;
+	for (q = m->frame, p = NEXT(q);; q = p, p = NEXT(p)) {
+	  CHECK_POP_FRAME(p);
+	  if (p->frameflags & (GC_WEAK_REF|GC_STRONG_REF)) {
+	    if (p->frameflags & GC_WEAK_REF) weak_ref = p;
 	    else if (!nonstrong_ref) nonstrong_ref = q;
 	  }
 	  if (p == gc_rec_last) break;
@@ -1291,9 +1505,10 @@ 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;
+	for (p = NEXT(m->frame);; p = NEXT(p)) {
+	  CHECK_POP_FRAME(p);
+	  if (p->frameflags & GC_WEAK_REF) weak_ref = p;
+	  if (!(p->frameflags & GC_STRONG_REF)) nonstrong_ref = p;
 	  if (p == gc_rec_last) break;
 	}
 #ifdef PIKE_DEBUG
@@ -1303,105 +1518,83 @@ int gc_cycle_push(void *x, struct marker *m, int weak)
       }
 
       else {
-	struct marker *q;
+	struct gc_frame *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;
+	for (q = m->frame, p = NEXT(q);; q = p, p = NEXT(p)) {
+	  CHECK_POP_FRAME(p);
+	  if (!(p->frameflags & GC_WEAK_REF) && !nonstrong_ref)
+	    nonstrong_ref = q;
 	  if (p == gc_rec_last) break;
 	}
       }
 
-      if (p == gc_rec_last) {	/* It was a backward reference. */
-	if (weak_ref) {
-	  /* The backward link is normal or strong and there are one
-	   * 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(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. */
-	  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 (weak_ref) {
+	/* The backward link is normal or strong and there are one
+	 * 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(find_marker(weak_ref->data),
+			"gc_cycle_push, weak break");
+	rotate_rec_list(m->frame, weak_ref);
+      }
 
-	else if (nonstrong_ref) {
-	  /* Either a nonweak cycle with a strong link in it or a 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(nonstrong_ref, "gc_cycle_push, weaker break");
-	    break_cycle (m, nonstrong_ref);
-	  }
-	}
+      else if (weak < 0) {
+	/* The backward link is strong. Must break the cycle at the
+	 * last nonstrong link. */
+	if (m->frame->frameflags & GC_STRONG_REF)
+	  nonstrong_ref->frameflags =
+	    (nonstrong_ref->frameflags & ~GC_WEAK_REF) | GC_STRONG_REF;
+	else
+	  m->frame->frameflags =
+	    (m->frame->frameflags & ~GC_WEAK_REF) | GC_STRONG_REF;
+	CYCLE_DEBUG_MSG(find_marker(nonstrong_ref->data),
+			"gc_cycle_push, nonstrong break");
+	rotate_rec_list(m->frame, nonstrong_ref);
+      }
 
-	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 = p->link) {
-	      p->cycle = cycle;
-	      CYCLE_DEBUG_MSG(p, "> gc_cycle_push, mark cycle");
-	      if (p == gc_rec_last) break;
-	    }
-	    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");
-	      }}}}		/* Mmm.. lisp ;) */
-
-      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 if (nonstrong_ref) {
+	/* Either a nonweak cycle with a strong link in it or a weak
+	 * cycle with a nonweak link in it. Break before the first
+	 * link that's stronger than the others. */
+	if (nonstrong_ref != m->frame) {
+	  CYCLE_DEBUG_MSG(find_marker(nonstrong_ref->data),
+			  "gc_cycle_push, weaker break");
+	  rotate_rec_list(m->frame, nonstrong_ref);
 	}
       }
-    }
-  }
+
+      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 = CYCLE(m->frame) ? CYCLE(m->frame) : ++last_cycle;
+	if (cycle == CYCLE(gc_rec_last))
+	  CYCLE_DEBUG_MSG(m, "gc_cycle_push, old cycle");
+	else {
+	  CYCLE_DEBUG_MSG(m, "gc_cycle_push, cycle");
+	  for (p = m->frame;; p = NEXT(p)) {
+	    CYCLE(p) = cycle;
+	    CYCLE_DEBUG_MSG(find_marker(p->data), "> gc_cycle_push, mark cycle");
+	    if (p == gc_rec_last) break;
+	  }}}}}			/* Mmm.. lisp ;) */
 
   else
     if (!(m->flags & GC_CYCLE_CHECKED)) {
-      struct marker *p;
-      m->flags |= gc_rec_last->flags & GC_LIVE;
-      if (weak) {
-	if (weak > 0) m->flags |= GC_WEAK_REF;
-	else m->flags |= GC_STRONG_REF;
-      }
+      struct gc_frame *l, *prev_rec_last = gc_rec_last;
 #ifdef PIKE_DEBUG
       cycle_checked++;
-      if (m->flags & GC_ON_STACK)
-	gc_fatal(x, 0, "Recursing twice into thing.\n");
-      if (m->flags & GC_LIVE_RECURSE)
-	gc_fatal(x, 0, "GC_LIVE_RECURSE set in normal recursion.\n");
+      if (m->frame)
+	gc_fatal(x, 0, "Marker already got a frame.\n");
+      if (NEXT(gc_rec_last))
+	gc_fatal(gc_rec_last->data, 0, "Not at end of list.\n");
 #endif
 
-      p = gc_rec_last;
-      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;
-      m->cycle = 0;
+      NEXT(gc_rec_last) = m->frame = l = gc_cycle_enqueue_pop(x);
+      m->flags |= GC_CYCLE_CHECKED | (last->flags & GC_LIVE);
+      if (weak) {
+	if (weak > 0) l->frameflags |= GC_WEAK_REF;
+	else l->frameflags |= GC_STRONG_REF;
+      }
 
 #ifdef GC_CYCLE_DEBUG
       if (weak > 0) CYCLE_DEBUG_MSG(m, "gc_cycle_push, recurse weak");
@@ -1409,13 +1602,13 @@ int gc_cycle_push(void *x, struct marker *m, int weak)
       else CYCLE_DEBUG_MSG(m, "gc_cycle_push, recurse");
       gc_cycle_indent += 2;
 #endif
-      gc_rec_last = m;
+      gc_rec_last = l;
       return 1;
     }
 
   /* Should normally not recurse now, but got to do that anyway if we
    * must mark live things. */
-  if (!(gc_rec_last->flags & GC_LIVE) || m->flags & GC_LIVE) {
+  if (!(last->flags & GC_LIVE) || m->flags & GC_LIVE) {
     CYCLE_DEBUG_MSG(m, "gc_cycle_push, no recurse");
     return 0;
   }
@@ -1438,34 +1631,26 @@ live_recurse:
     }
   }
 
-  /* Recurse without linking m onto rec_list. */
+  {
+    /* Recurse without linking onto rec_list. */
+    struct gc_frame *l = gc_cycle_enqueue_pop(x);
 #ifdef GC_CYCLE_DEBUG
-  CYCLE_DEBUG_MSG(m, "gc_cycle_push, live recurse");
-  gc_cycle_indent += 2;
+    CYCLE_DEBUG_MSG(m, "gc_cycle_push, live recurse");
+    gc_cycle_indent += 2;
 #endif
-  gc_rec_last = m;
+    gc_rec_last = l;
+  }
+
   return 1;
 }
 
-/* Add an extra ref when a dead thing is popped. It's taken away in
- * the free pass. This is done to not refcount garb the cycles
- * themselves recursively, which in bad cases can consume a lot of C
- * stack. */
-#define ADD_REF_IF_DEAD(M)						\
-  if (!(M->flags & GC_LIVE)) {						\
-    DO_IF_DEBUG(							\
-      if (M->flags & GC_GOT_DEAD_REF)					\
-	gc_fatal(M->data, 0, "A thing already got an extra dead cycle ref.\n"); \
-    );									\
-    gc_add_extra_ref(M->data);						\
-    M->flags |= GC_GOT_DEAD_REF;					\
-  }
-
-void gc_cycle_pop(void *a)
+static void gc_cycle_pop(void *a)
 {
-  struct marker *m = find_marker(a), *p, *q, *base;
+  struct marker *m = find_marker(a);
+  struct gc_frame *here, *base, *p;
 
 #ifdef PIKE_DEBUG
+  if (!a) fatal("Got null pointer.\n");
   if (Pike_in_gc != GC_PASS_CYCLE)
     fatal("GC cycle pop attempted in invalid pass.\n");
   if (!(m->flags & GC_CYCLE_CHECKED))
@@ -1483,97 +1668,80 @@ void gc_cycle_pop(void *a)
 
   if (m->flags & GC_LIVE_RECURSE) {
     m->flags &= ~GC_LIVE_RECURSE;
-    CYCLE_DEBUG_MSG(m, "gc_cycle_pop, live pop");
+    CYCLE_DEBUG_MSG(m, "gc_cycle_pop_live");
+    gc_rec_last = PREV(gc_rec_top);
+    debug_really_free_gc_frame(gc_rec_top);
     return;
   }
 
+  here = m->frame;
 #ifdef PIKE_DEBUG
-  if (!(m->flags & GC_ON_STACK))
+  if (!here || here->data != a)
+    gc_fatal(a, 0, "Marker being popped has no or invalid frame.\n");
+  CHECK_POP_FRAME(here);
+  CHECK_POP_FRAME(gc_rec_last);
+  if (here->frameflags & GC_OFF_STACK)
     gc_fatal(a, 0, "Marker being popped isn't on stack.\n");
-  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 list.\n");
+  here->back = (struct gc_frame *) -1;
 #endif
-    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");
-    }
-    return;
-  }
+  here->frameflags |= GC_OFF_STACK;
 
-  for (base = gc_rec_last, p = base->link;; base = p, p = p->link) {
-    if (base == m) {
-#ifdef GC_CYCLE_DEBUG
+  for (base = PREV(here), p = here;; base = p, p = NEXT(p)) {
+    if (base == here) {
+      /* Part of a cycle; wait until the cycle is complete before
+       * unlinking it from rec_list. */
+      DO_IF_DEBUG(m->frame->back = (struct gc_frame *) -1);
       CYCLE_DEBUG_MSG(m, "gc_cycle_pop, keep cycle");
-#endif
       return;
     }
-    if (!(p->cycle && p->cycle == base->cycle))
+    CHECK_POP_FRAME(p);
+    if (!(CYCLE(p) && CYCLE(p) == CYCLE(base)))
       break;
   }
 
-  q = base;
-  while (1) {
-    p = q->link;
+  gc_rec_last = base;
+  while ((p = NEXT(base))) {
+    struct marker *pm = find_marker(p->data);
 #ifdef PIKE_DEBUG
-    if (p->flags & (GC_ON_STACK|GC_LIVE_RECURSE))
-      gc_fatal(p->data, 0, "Marker still got an on-stack flag at pop.\n");
+    if (pm->frame != p)
+      gc_fatal(p->data, 0, "Bogus marker for thing being popped.\n");
+    if (pm->flags & GC_GOT_DEAD_REF)
+      gc_fatal(p->data, 0, "Didn't expect a dead extra ref.\n");
 #endif
-
-    if (!p->cycle && !(p->flags & GC_LIVE_OBJ)) {
-      ADD_REF_IF_DEAD(p);
-      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");
+    p->frameflags &= ~(GC_WEAK_REF|GC_STRONG_REF);
+    if (pm->flags & GC_LIVE_OBJ) {
+      /* This extra ref is taken away in the kill pass. */
+      gc_add_extra_ref(p->data);
+      base = p;
+      DO_IF_DEBUG(PREV(p) = (struct gc_frame *) -1);
+      CYCLE_DEBUG_MSG(pm, "gc_cycle_pop, put on kill list");
     }
-    else q = p;
-
-    if (p == m) break;
-  }
-
-  if (base != q) {
-    q = base;
-    while ((p = q->link)) {
+    else {
+      if (!(pm->flags & GC_LIVE)) {
+	/* Add an extra ref which is taken away in the free pass. This
+	 * is done to not refcount garb the cycles themselves
+	 * recursively, which in bad cases can consume a lot of C
+	 * stack. */
 #ifdef PIKE_DEBUG
-      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;
-
-      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");
+	if (pm->flags & GC_GOT_DEAD_REF)
+	  gc_fatal(pm->data, 0,
+		   "A thing already got an extra dead cycle ref.\n");
+#endif
+	gc_add_extra_ref(pm->data);
+	pm->flags |= GC_GOT_DEAD_REF;
       }
+      NEXT(base) = NEXT(p);
+      CYCLE_DEBUG_MSG(pm, "gc_cycle_pop, pop off");
+      pm->frame = 0;
+      debug_really_free_gc_frame(p);
     }
-
-    q->link = kill_list;
-    kill_list = base->link;
-    base->link = p;
   }
-}
 
-void gc_set_rec_last(struct marker *m)
-{
-  gc_rec_last = m;
+  if (base != gc_rec_last) {
+    NEXT(base) = kill_list;
+    kill_list = NEXT(gc_rec_last);
+    NEXT(gc_rec_last) = 0;
+  }
 }
 
 void do_gc_recurse_svalues(struct svalue *s, int num)
@@ -1622,17 +1790,17 @@ static void warn_bad_cycles()
   struct array *obj_arr = 0;
 
   if (!SETJMP(uwp)) {
-    struct marker *p;
+    struct gc_frame *p;
     unsigned cycle = 0;
     obj_arr = allocate_array(0);
 
     for (p = kill_list; p;) {
-      if ((cycle = p->cycle)) {
+      if ((cycle = CYCLE(p))) {
 	push_object((struct object *) p->data);
 	obj_arr = append_array(obj_arr, --sp);
       }
-      p = p->link;
-      if (p ? p->cycle != cycle : cycle) {
+      p = NEXT(p);
+      if (p ? CYCLE(p) != cycle : cycle) {
 	if (obj_arr->size >= 2) {
 	  push_constant_text("gc");
 	  push_constant_text("bad_cycle");
@@ -1786,6 +1954,7 @@ int do_gc(void)
   Pike_in_gc=GC_PASS_CYCLE;
 #ifdef PIKE_DEBUG
   obj_count = num_objects;
+  max_gc_frames = 0;
 #endif
 
   /* Now find all cycles in the internal structures */
@@ -1799,16 +1968,44 @@ int do_gc(void)
   gc_cycle_check_all_programs();
 
 #ifdef PIKE_DEBUG
-  if (gc_mark_queue.first)
-    fatal("gc_mark_queue not empty at end of cycle check pass.\n");
-  if (rec_list.link || gc_rec_last != &rec_list)
+  if (gc_rec_top)
+    fatal("gc_rec_top not empty at end of cycle check pass.\n");
+  if (NEXT(&rec_list) || gc_rec_last != &rec_list || gc_rec_top)
     fatal("Recurse list not empty or inconsistent after cycle check pass.\n");
 #endif
 
   GC_VERBOSE_DO(fprintf(stderr,
 			"| cycle: %u internal things visited, %u cycle ids used,\n"
-			"|        %u weak references freed, %d things really freed\n",
-			cycle_checked, last_cycle, weak_freed, obj_count - num_objects));
+			"|        %u weak references freed, %d things really freed,\n"
+			"|        space for %u gc frames used\n",
+			cycle_checked, last_cycle, weak_freed, obj_count - num_objects,
+			max_gc_frames));
+
+  if (gc_ext_weak_refs) {
+    size_t to_free = gc_ext_weak_refs;
+#ifdef PIKE_DEBUG
+    obj_count = num_objects;
+#endif
+    Pike_in_gc = GC_PASS_ZAP_WEAK;
+    /* Zap weak references from external to internal things. That
+     * doesn't occur very often; only when something have both
+     * external weak refs and nonweak cyclic refs from internal
+     * things. */
+    gc_zap_ext_weak_refs_in_arrays();
+    /* Multisets handled as arrays. */
+    if (gc_ext_weak_refs) {
+      gc_zap_ext_weak_refs_in_mappings();
+      if (gc_ext_weak_refs) {
+	gc_zap_ext_weak_refs_in_programs();
+	if (gc_ext_weak_refs)
+	  gc_zap_ext_weak_refs_in_objects();
+      }
+    }
+    GC_VERBOSE_DO(
+      fprintf(stderr,
+	      "| zap weak: freed %u external weak refs, %d things really freed\n",
+	      to_free - gc_ext_weak_refs, obj_count - num_objects));
+  }
 
   /* Thread switches, object alloc/free and reference changes are
    * allowed again now. */
@@ -1851,11 +2048,15 @@ int do_gc(void)
 #ifdef PIKE_DEBUG
   destroy_count = 0;
 #endif
-  for (; kill_list; kill_list = kill_list->link) {
+  for (; kill_list; kill_list = NEXT(kill_list)) {
     struct object *o = (struct object *) kill_list->data;
 #ifdef PIKE_DEBUG
-    if ((kill_list->flags & (GC_LIVE|GC_LIVE_OBJ)) != (GC_LIVE|GC_LIVE_OBJ))
+    if ((get_marker(kill_list->data)->flags & (GC_LIVE|GC_LIVE_OBJ)) !=
+	(GC_LIVE|GC_LIVE_OBJ))
       gc_fatal(o, 0, "Invalid thing in kill list.\n");
+    if (o->parent && !o->parent->prog &&
+	get_marker(o->parent)->flags & GC_LIVE_OBJ)
+      gc_fatal(o, 0, "GC destructed parent prematurely.\n");
 #endif
     GC_VERBOSE_DO(fprintf(stderr, "|   Killing %p with %d refs\n",
 			  o, o->refs));
diff --git a/src/gc.h b/src/gc.h
index 146b2ceddbcc4e4c8bf672aa43696671c4a977c6..9194dea97658640e0ccf73cfd05d5d399d910727 100644
--- a/src/gc.h
+++ b/src/gc.h
@@ -1,5 +1,5 @@
 /*
- * $Id: gc.h,v 1.54 2000/07/11 03:45:10 mast Exp $
+ * $Id: gc.h,v 1.55 2000/07/18 05:48:20 mast Exp $
  */
 #ifndef GC_H
 #define GC_H
@@ -61,23 +61,20 @@ extern void *gc_svalue_location;
   num_objects-- ;							\
 }while(0)
 
+struct gc_frame;
+
 struct marker
 {
   struct marker *next;
-  struct marker *link;		/* Next pointer used in rec_list and destroy_list. */
+  struct gc_frame *frame;	/* Pointer into the cycle check stack. */
   void *data;
   INT32 refs;			/* Internal references. */
   INT32 weak_refs;		/* Weak (implying internal) references. */
 #ifdef PIKE_DEBUG
   INT32 xrefs;			/* Known external references. */
-  INT32 saved_refs;		/* Object refcount during check and mark pass. */
+  INT32 saved_refs;		/* Object refcount during check pass. */
 #endif
-  unsigned INT16 cycle;		/* Cycle id number. */
-#ifdef PIKE_DEBUG
-  unsigned INT32 flags;
-#else
   unsigned INT16 flags;
-#endif
 };
 
 #define GC_MARKED		0x0001
@@ -85,30 +82,25 @@ struct marker
 #define GC_CYCLE_CHECKED	0x0004
 #define GC_LIVE			0x0008
 #define GC_LIVE_OBJ		0x0010
-#define GC_ON_STACK		0x0020
-#define GC_IN_REC_LIST		0x0040
-#define GC_MOVED_BACK		0x0080
-#define GC_DONT_POP		0x0100
-#define GC_LIVE_RECURSE		0x0200
-#define GC_WEAK_REF		0x0400
-#define GC_STRONG_REF		0x0800
-#define GC_GOT_DEAD_REF		0x1000
-#define GC_FREE_VISITED		0x2000
-
-/* Debug mode flags. */
-#define GC_TOUCHED		0x010000
-#define GC_IS_REFERENCED	0x020000
-#define GC_XREFERENCED		0x040000
-#define GC_DO_FREE		0x080000
-#define GC_GOT_EXTRA_REF	0x100000
-#define GC_FOLLOWED_STRONG	0x200000
+#define GC_LIVE_RECURSE		0x0020
+#define GC_GOT_DEAD_REF		0x0040
+#define GC_FREE_VISITED		0x0080
+
+#ifdef PIKE_DEBUG
+#define GC_TOUCHED		0x0100
+#define GC_IS_REFERENCED	0x0200
+#define GC_XREFERENCED		0x0400
+#define GC_DO_FREE		0x0800
+#define GC_GOT_EXTRA_REF	0x1000
+#endif
 
 #include "block_alloc_h.h"
 PTR_HASH_ALLOC(marker,MARKER_CHUNK_SIZE)
 
-extern struct marker *gc_rec_last;
 extern size_t gc_ext_weak_refs;
 
+typedef void gc_cycle_check_cb (void *data, int weak);
+
 /* Prototypes begin here */
 struct callback *debug_add_gc_callback(callback_func call,
 				 void *arg,
@@ -138,7 +130,6 @@ int gc_external_mark3(void *a, void *in, char *where);
 int gc_do_weak_free(void *a);
 int gc_mark(void *a);
 int gc_cycle_push(void *x, struct marker *m, int weak);
-void gc_cycle_pop(void *a);
 void gc_set_rec_last(struct marker *m);
 void do_gc_recurse_svalues(struct svalue *s, int num);
 void do_gc_recurse_short_svalue(union anything *u, TYPE_T type);
@@ -146,6 +137,8 @@ int gc_do_free(void *a);
 int gc_is_internal(void *a);
 int do_gc(void);
 void f__gc_status(INT32 args);
+void gc_cycle_enqueue(gc_cycle_check_cb *checkfn, void *data, int weak);
+void gc_cycle_run_queue();
 /* Prototypes end here */
 
 #define gc_fatal \
@@ -167,9 +160,10 @@ void f__gc_status(INT32 args);
   (Pike_in_gc == GC_PASS_MARK ?						\
    gc_mark_weak_short_svalue((U), (T)) : gc_cycle_check_weak_short_svalue((U), (T)))
 
-#define GC_RECURSE_THING(V,T)						\
+#define GC_RECURSE_THING(V, T)						\
   (Pike_in_gc == GC_PASS_MARK ?						\
-   PIKE_CONCAT3(gc_mark_, T, _as_referenced)(V) : PIKE_CONCAT(gc_cycle_check_, T)(V))
+   PIKE_CONCAT3(gc_mark_, T, _as_referenced)(V) :			\
+   PIKE_CONCAT(gc_cycle_check_, T)(V, 0))
 #define gc_recurse_array(V) GC_RECURSE_THING((V), array)
 #define gc_recurse_mapping(V) GC_RECURSE_THING((V), mapping)
 #define gc_recurse_multiset(V) GC_RECURSE_THING((V), multiset)
@@ -189,7 +183,7 @@ void f__gc_status(INT32 args);
 #define gc_is_referenced(X) debug_gc_is_referenced(debug_malloc_pass(X))
 #else
 #define gc_is_referenced(X) !(get_marker(X)->flags & GC_NOT_REFERENCED)
-#define gc_do_weak_free(X) (Pike_in_gc != GC_PASS_FREE ?		\
+#define gc_do_weak_free(X) (Pike_in_gc != GC_PASS_ZAP_WEAK ?		\
 			    get_marker(X)->weak_refs == -1 :		\
 			    !(get_marker(X)->flags & GC_MARKED))
 #endif
@@ -210,6 +204,7 @@ void f__gc_status(INT32 args);
 #define GC_PASS_CHECK		100
 #define GC_PASS_MARK		200
 #define GC_PASS_CYCLE		250
+#define GC_PASS_ZAP_WEAK	270
 #define GC_PASS_FREE		300
 #define GC_PASS_KILL		400
 #define GC_PASS_DESTRUCT	500
@@ -223,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 after any other links, i.e.
- *     strong links should be pushed first into the lifo queue.
+ * 1.  All strong links are recursed before any other links, i.e.
+ *     strong links must be passed to gc_cycle_check_* last.
  * 2.  There can never be a cycle consisting of only strong links.
  */
 
@@ -232,42 +227,29 @@ extern int gc_in_cycle_check;
   void *_thing_ = (X);							\
   struct marker *_m_ = get_marker(_thing_);				\
   if (!(_m_->flags & GC_MARKED)) {					\
-    struct marker *_old_last_ = gc_rec_last;				\
-    if (gc_cycle_push(_thing_, _m_, (WEAK))) {				\
-      DO_IF_DEBUG(							\
-	if (gc_in_cycle_check)						\
-	  fatal("Recursing immediately in gc cycle check.\n");		\
-	gc_in_cycle_check = 1;						\
-      );								\
-      enqueue_lifo(&gc_mark_queue,					\
-		   (queue_call) gc_cycle_pop, _thing_);			\
-      enqueue_lifo(&gc_mark_queue,					\
-		   (queue_call) gc_set_rec_last, _old_last_);		\
-      {
+    DO_IF_DEBUG(							\
+      if (gc_in_cycle_check)						\
+	fatal("Recursing immediately in gc cycle check.\n");		\
+      gc_in_cycle_check = 1;						\
+    );									\
+    if (gc_cycle_push(_thing_, _m_, (WEAK))) {
 
 #define GC_CYCLE_ENTER_OBJECT(X, WEAK) do {				\
   struct object *_thing_ = (X);						\
   struct marker *_m_ = get_marker(_thing_);				\
   if (!(_m_->flags & GC_MARKED)) {					\
-    struct marker *_old_last_ = gc_rec_last;				\
     if (_thing_->prog && FIND_LFUN(_thing_->prog, LFUN_DESTROY) != -1)	\
       _m_->flags |= GC_LIVE|GC_LIVE_OBJ;				\
-    if (gc_cycle_push(_thing_, _m_, (WEAK))) {				\
-      DO_IF_DEBUG(							\
-	if (gc_in_cycle_check)						\
-	  fatal("Recursing immediately in gc cycle check.\n");		\
-	gc_in_cycle_check = 1;						\
-      );								\
-      enqueue_lifo(&gc_mark_queue,					\
-		   (queue_call) gc_cycle_pop, _thing_);			\
-      enqueue_lifo(&gc_mark_queue,					\
-		   (queue_call) gc_set_rec_last, _old_last_);		\
-      {
+    DO_IF_DEBUG(							\
+      if (gc_in_cycle_check)						\
+	fatal("Recursing immediately in gc cycle check.\n");		\
+      gc_in_cycle_check = 1;						\
+    );									\
+    if (gc_cycle_push(_thing_, _m_, (WEAK))) {
 
 #define GC_CYCLE_LEAVE							\
-      }									\
-      DO_IF_DEBUG(gc_in_cycle_check = 0);				\
     }									\
+    DO_IF_DEBUG(gc_in_cycle_check = 0);					\
   }									\
 } while (0)
 
diff --git a/src/mapping.c b/src/mapping.c
index 93b6bd3d19eb29f25f6980118055ebcaa4f802d8..07c7dfa6983862e06e95fd2961b45d682d53b05d 100644
--- a/src/mapping.c
+++ b/src/mapping.c
@@ -5,7 +5,7 @@
 \*/
 /**/
 #include "global.h"
-RCSID("$Id: mapping.c,v 1.92 2000/07/11 03:45:10 mast Exp $");
+RCSID("$Id: mapping.c,v 1.93 2000/07/18 05:48:20 mast Exp $");
 #include "main.h"
 #include "object.h"
 #include "mapping.h"
@@ -846,7 +846,7 @@ void check_mapping_for_destruct(struct mapping *m)
     fatal("Zero refs in mapping->data\n");
   if(d_flag>1)  check_mapping(m);
   debug_malloc_touch(m);
-  if (Pike_in_gc > GC_PASS_PREPARE && Pike_in_gc < GC_PASS_FREE &&
+  if (Pike_in_gc > GC_PASS_PREPARE && Pike_in_gc < GC_PASS_ZAP_WEAK &&
       Pike_in_gc != GC_PASS_MARK)
     fatal("check_mapping_for_destruct called in wrong pass inside gc.\n");
 #endif
@@ -1910,45 +1910,33 @@ void gc_mark_mapping_as_referenced(struct mapping *m)
   }
 }
 
-void low_gc_cycle_check_mapping(struct mapping *m)
+void real_gc_cycle_check_mapping(struct mapping *m, int weak)
 {
+  GC_CYCLE_ENTER(m, weak) {
 #ifdef PIKE_DEBUG
-  if(m->data->refs <=0)
-    fatal("Zero refs in mapping->data\n");
+    if(m->data->refs <=0)
+      fatal("Zero refs in mapping->data\n");
 #endif
 
-  if((m->data->ind_types | m->data->val_types) & BIT_COMPLEX)
-  {
-    INT32 e;
-    struct keypair *k;
+    if((m->data->ind_types | m->data->val_types) & BIT_COMPLEX)
+    {
+      INT32 e;
+      struct keypair *k;
 
-    if (m->flags & MAPPING_FLAG_WEAK)
-      gc_recurse_weak_mapping(m, gc_cycle_check_weak_svalues);
-    else
-      NEW_MAPPING_LOOP(m->data)
-      {
-	if (gc_cycle_check_svalues(&k->ind, 1) ||
-	    gc_cycle_check_svalues(&k->val, 1)) {
+      if (m->flags & MAPPING_FLAG_WEAK)
+	gc_recurse_weak_mapping(m, gc_cycle_check_weak_svalues);
+      else
+	NEW_MAPPING_LOOP(m->data)
+	{
+	  if (gc_cycle_check_svalues(&k->ind, 1) ||
+	      gc_cycle_check_svalues(&k->val, 1)) {
 #ifdef PIKE_DEBUG
-	  fatal("Looks like check_mapping_for_destruct "
-		"didn't do its job properly.\n");
+	    fatal("Looks like check_mapping_for_destruct "
+		  "didn't do its job properly.\n");
 #endif
+	  }
 	}
-      }
-  }
-}
-
-void real_gc_cycle_check_mapping(struct mapping *m)
-{
-  GC_CYCLE_ENTER(m, 0) {
-    low_gc_cycle_check_mapping(m);
-  } GC_CYCLE_LEAVE;
-}
-
-void real_gc_cycle_check_mapping_weak(struct mapping *m)
-{
-  GC_CYCLE_ENTER(m, 1) {
-    low_gc_cycle_check_mapping(m);
+    }
   } GC_CYCLE_LEAVE;
 }
 
@@ -2049,11 +2037,22 @@ void gc_cycle_check_all_mappings(void)
 {
   struct mapping *m;
   for (m = gc_internal_mapping; m; m = m->next) {
-    real_gc_cycle_check_mapping(m);
-    run_lifo_queue(&gc_mark_queue);
+    real_gc_cycle_check_mapping(m, 0);
+    gc_cycle_run_queue();
   }
 }
 
+void gc_zap_ext_weak_refs_in_mappings(void)
+{
+  gc_mark_mapping_pos = first_mapping;
+  while (gc_mark_mapping_pos != gc_internal_mapping && gc_ext_weak_refs) {
+    struct mapping *m = gc_mark_mapping_pos;
+    gc_mark_mapping_pos = m->next;
+    gc_mark_mapping_as_referenced(m);
+  }
+  discard_queue(&gc_mark_queue);
+}
+
 void gc_free_all_unreferenced_mappings(void)
 {
   INT32 e;
@@ -2061,19 +2060,6 @@ void gc_free_all_unreferenced_mappings(void)
   struct mapping *m,*next;
   struct mapping_data *md;
 
-  if (gc_ext_weak_refs) {
-    /* Have to go through all marked things if we got external weak
-     * references to otherwise unreferenced things, so the mark
-     * functions can free those references. */
-    gc_mark_mapping_pos = first_mapping;
-    while (gc_mark_mapping_pos != gc_internal_mapping && gc_ext_weak_refs) {
-      struct mapping *m = gc_mark_mapping_pos;
-      gc_mark_mapping_pos = m->next;
-      gc_mark_mapping_as_referenced(m);
-    }
-    discard_queue(&gc_mark_queue);
-  }
-
   for(m=gc_internal_mapping;m;m=next)
   {
     if(gc_do_free(m))
diff --git a/src/mapping.h b/src/mapping.h
index 0bd51726ba47c19057af9cb98f3e09f0d8f1d6b0..b2c3ae334d717251d695dc19e322bffe6f706077 100644
--- a/src/mapping.h
+++ b/src/mapping.h
@@ -5,7 +5,7 @@
 \*/
 
 /*
- * $Id: mapping.h,v 1.26 2000/07/06 23:25:26 mast Exp $
+ * $Id: mapping.h,v 1.27 2000/07/18 05:48:20 mast Exp $
  */
 #ifndef MAPPING_H
 #define MAPPING_H
@@ -150,9 +150,9 @@ void gc_mark_mapping_as_referenced(struct mapping *m);
 unsigned gc_touch_all_mappings(void);
 void gc_check_all_mappings(void);
 void gc_mark_all_mappings(void);
-void real_gc_cycle_check_mapping(struct mapping *m);
-void real_gc_cycle_check_mapping_weak(struct mapping *m);
+void real_gc_cycle_check_mapping(struct mapping *m, int weak);
 void gc_cycle_check_all_mappings(void);
+void gc_zap_ext_weak_refs_in_mappings(void);
 void gc_free_all_unreferenced_mappings(void);
 void simple_describe_mapping(struct mapping *m);
 void debug_dump_mapping(struct mapping *m);
@@ -161,9 +161,7 @@ void zap_all_mappings(void);
 
 #define allocate_mapping(X) dmalloc_touch(struct mapping *,debug_allocate_mapping(X))
 
-#define gc_cycle_check_mapping(X) \
-  enqueue_lifo(&gc_mark_queue, (queue_call) real_gc_cycle_check_mapping, (X))
-#define gc_cycle_check_mapping_weak(X) \
-  enqueue_lifo(&gc_mark_queue, (queue_call) real_gc_cycle_check_mapping_weak, (X))
+#define gc_cycle_check_mapping(X, WEAK) \
+  gc_cycle_enqueue((gc_cycle_check_cb *) real_gc_cycle_check_mapping, (X), (WEAK))
 
 #endif /* MAPPING_H */
diff --git a/src/multiset.c b/src/multiset.c
index 8340d086adc207a8e398885b3def62e9ad4e1b2e..7041a49da918781c49ba38cc6ba11aa27a1f3515 100644
--- a/src/multiset.c
+++ b/src/multiset.c
@@ -16,7 +16,7 @@
 #include "gc.h"
 #include "security.h"
 
-RCSID("$Id: multiset.c,v 1.24 2000/07/11 03:45:10 mast Exp $");
+RCSID("$Id: multiset.c,v 1.25 2000/07/18 05:48:20 mast Exp $");
 
 struct multiset *first_multiset;
 
@@ -307,17 +307,10 @@ void gc_mark_multiset_as_referenced(struct multiset *l)
   }
 }
 
-void real_gc_cycle_check_multiset(struct multiset *l)
+void real_gc_cycle_check_multiset(struct multiset *l, int weak)
 {
-  GC_CYCLE_ENTER(l, 0) {
-    gc_cycle_check_array(l->ind);
-  } GC_CYCLE_LEAVE;
-}
-
-void real_gc_cycle_check_multiset_weak(struct multiset *l)
-{
-  GC_CYCLE_ENTER(l, 1) {
-    gc_cycle_check_array(l->ind);
+  GC_CYCLE_ENTER(l, weak) {
+    gc_cycle_check_array(l->ind, 0);
   } GC_CYCLE_LEAVE;
 }
 
@@ -360,8 +353,8 @@ void gc_cycle_check_all_multisets(void)
 {
   struct multiset *l;
   for (l = gc_internal_multiset; l; l = l->next) {
-    real_gc_cycle_check_multiset(l);
-    run_lifo_queue(&gc_mark_queue);
+    real_gc_cycle_check_multiset(l, 0);
+    gc_cycle_run_queue();
   }
 }
 
@@ -369,9 +362,6 @@ void gc_free_all_unreferenced_multisets(void)
 {
   struct multiset *l,*next;
 
-  /* No gc_ext_weak_refs stuff here; it's been taken care of by
-   * gc_free_all_unreferenced_arrays(). */
-
   for(l=gc_internal_multiset;l;l=next)
   {
     if(gc_do_free(l))
diff --git a/src/multiset.h b/src/multiset.h
index 85bb71b1a62d30a5fb5456bb26dc7bfeef402652..b7ee4a003d084bccc022dee3997bb5d82f258845 100644
--- a/src/multiset.h
+++ b/src/multiset.h
@@ -5,7 +5,7 @@
 \*/
 
 /*
- * $Id: multiset.h,v 1.12 2000/07/06 23:25:26 mast Exp $
+ * $Id: multiset.h,v 1.13 2000/07/18 05:48:20 mast Exp $
  */
 #ifndef MULTISET_H
 #define MULTISET_H
@@ -56,16 +56,13 @@ void gc_mark_multiset_as_referenced(struct multiset *l);
 unsigned gc_touch_all_multisets(void);
 void gc_check_all_multisets(void);
 void gc_mark_all_multisets(void);
-void real_gc_cycle_check_multiset(struct multiset *l);
-void real_gc_cycle_check_multiset_weak(struct multiset *l);
+void real_gc_cycle_check_multiset(struct multiset *l, int weak);
 void gc_cycle_check_all_multisets(void);
 void gc_free_all_unreferenced_multisets(void);
 void count_memory_in_multisets(INT32 *num_, INT32 *size_);
 /* Prototypes end here */
 
-#define gc_cycle_check_multiset(X) \
-  enqueue_lifo(&gc_mark_queue, (queue_call) real_gc_cycle_check_multiset, (X))
-#define gc_cycle_check_multiset_weak(X) \
-  enqueue_lifo(&gc_mark_queue, (queue_call) real_gc_cycle_check_multiset_weak, (X))
+#define gc_cycle_check_multiset(X, WEAK) \
+  gc_cycle_enqueue((gc_cycle_check_cb *) real_gc_cycle_check_multiset, (X), (WEAK))
 
 #endif /* MULTISET_H */
diff --git a/src/object.c b/src/object.c
index 13b58c32cc0ffc47f49ffc40cf32b14cf005a3c0..5bd94cfbf7838f76b1d0bbde395f5e5535a45f4f 100644
--- a/src/object.c
+++ b/src/object.c
@@ -5,7 +5,7 @@
 \*/
 /**/
 #include "global.h"
-RCSID("$Id: object.c,v 1.135 2000/07/11 03:45:10 mast Exp $");
+RCSID("$Id: object.c,v 1.136 2000/07/18 05:48:20 mast Exp $");
 #include "object.h"
 #include "dynamic_buffer.h"
 #include "interpret.h"
@@ -1279,80 +1279,61 @@ void gc_mark_object_as_referenced(struct object *o)
   }
 }
 
-static void low_gc_cycle_check_object(struct object *o)
+void real_gc_cycle_check_object(struct object *o, int weak)
 {
-  int e;
-  struct program *p = o->prog;
+  GC_CYCLE_ENTER_OBJECT(o, weak) {
+    int e;
+    struct program *p = o->prog;
 
-  if (p) {
+    if (p) {
 #if 0
-    struct object *o2;
-    for (o2 = gc_internal_object; o2 && o2 != o; o2 = o2->next) {}
-    if (!o2) fatal("Object not on gc_internal_object list.\n");
+      struct object *o2;
+      for (o2 = gc_internal_object; o2 && o2 != o; o2 = o2->next) {}
+      if (!o2) fatal("Object not on gc_internal_object list.\n");
 #endif
 
-    /* This must be first. */
-    if(o->parent)
-      gc_cycle_check_object_strong(o->parent);
+      LOW_PUSH_FRAME(o);
 
-    LOW_PUSH_FRAME(o);
-
-    for(e=p->num_inherits-1; e>=0; e--)
-    {
-      int q;
+      for(e=p->num_inherits-1; e>=0; e--)
+      {
+	int q;
       
-      LOW_SET_FRAME_CONTEXT(p->inherits[e]);
+	LOW_SET_FRAME_CONTEXT(p->inherits[e]);
 
-      if(pike_frame->context.prog->gc_recurse_func)
-	pike_frame->context.prog->gc_recurse_func(o);
+	if(pike_frame->context.prog->gc_recurse_func)
+	  pike_frame->context.prog->gc_recurse_func(o);
 
-      for(q=0;q<(int)pike_frame->context.prog->num_variable_index;q++)
-      {
-	int d=pike_frame->context.prog->variable_index[q];
-	
-	if(pike_frame->context.prog->identifiers[d].run_time_type == T_MIXED)
+	for(q=0;q<(int)pike_frame->context.prog->num_variable_index;q++)
 	{
-	  struct svalue *s;
-	  s=(struct svalue *)(pike_frame->current_storage +
-			      pike_frame->context.prog->identifiers[d].func.offset);
-	  dmalloc_touch_svalue(s);
-	  gc_cycle_check_svalues(s, 1);
-	}else{
-	  union anything *u;
-	  int rtt = pike_frame->context.prog->identifiers[d].run_time_type;
-	  u=(union anything *)(pike_frame->current_storage +
-			       pike_frame->context.prog->identifiers[d].func.offset);
+	  int d=pike_frame->context.prog->variable_index[q];
+	
+	  if(pike_frame->context.prog->identifiers[d].run_time_type == T_MIXED)
+	  {
+	    struct svalue *s;
+	    s=(struct svalue *)(pike_frame->current_storage +
+				pike_frame->context.prog->identifiers[d].func.offset);
+	    dmalloc_touch_svalue(s);
+	    gc_cycle_check_svalues(s, 1);
+	  }else{
+	    union anything *u;
+	    int rtt = pike_frame->context.prog->identifiers[d].run_time_type;
+	    u=(union anything *)(pike_frame->current_storage +
+				 pike_frame->context.prog->identifiers[d].func.offset);
 #ifdef DEBUG_MALLOC
-	  if (rtt <= MAX_REF_TYPE) debug_malloc_touch(u->refs);
+	    if (rtt <= MAX_REF_TYPE) debug_malloc_touch(u->refs);
 #endif
-	  gc_cycle_check_short_svalue(u, rtt);
+	    gc_cycle_check_short_svalue(u, rtt);
+	  }
 	}
+	LOW_UNSET_FRAME_CONTEXT();
       }
-      LOW_UNSET_FRAME_CONTEXT();
-    }
     
-    LOW_POP_FRAME();
-  }
-}
-
-void real_gc_cycle_check_object(struct object *o)
-{
-  GC_CYCLE_ENTER_OBJECT(o, 0) {
-    low_gc_cycle_check_object(o);
-  } GC_CYCLE_LEAVE;
-}
-
-void real_gc_cycle_check_object_weak(struct object *o)
-{
-  GC_CYCLE_ENTER_OBJECT(o, 1) {
-    low_gc_cycle_check_object(o);
-  } GC_CYCLE_LEAVE;
-}
+      LOW_POP_FRAME();
 
-void real_gc_cycle_check_object_strong(struct object *o)
-{
-  GC_CYCLE_ENTER_OBJECT(o, -1) {
-    low_gc_cycle_check_object(o);
+      /* This must be last. */
+      if(o->parent)
+	gc_cycle_check_object(o->parent, -1);
+    }
   } GC_CYCLE_LEAVE;
 }
 
@@ -1468,29 +1449,27 @@ void gc_cycle_check_all_objects(void)
 {
   struct object *o;
   for (o = gc_internal_object; o; o = o->next) {
-    real_gc_cycle_check_object(o);
-    run_lifo_queue(&gc_mark_queue);
+    real_gc_cycle_check_object(o, 0);
+    gc_cycle_run_queue();
   }
 }
 
+void gc_zap_ext_weak_refs_in_objects(void)
+{
+  gc_mark_object_pos = first_object;
+  while (gc_mark_object_pos != gc_internal_object && gc_ext_weak_refs) {
+    struct object *o = gc_mark_object_pos;
+    gc_mark_object_pos = o->next;
+    if (o->refs)
+      gc_mark_object_as_referenced(o);
+  }
+  discard_queue(&gc_mark_queue);
+}
+
 void gc_free_all_unreferenced_objects(void)
 {
   struct object *o,*next;
 
-  if (gc_ext_weak_refs) {
-    /* Have to go through all marked things if we got external weak
-     * references to otherwise unreferenced things, so the mark
-     * functions can free those references. */
-    gc_mark_object_pos = first_object;
-    while (gc_mark_object_pos != gc_internal_object && gc_ext_weak_refs) {
-      struct object *o = gc_mark_object_pos;
-      gc_mark_object_pos = o->next;
-      if (o->refs)
-	gc_mark_object_as_referenced(o);
-    }
-    discard_queue(&gc_mark_queue);
-  }
-
   for(o=gc_internal_object; o; o=next)
   {
     if(gc_do_free(o))
diff --git a/src/object.h b/src/object.h
index 56ce772429e5d1ea20812d3faf2ac753e184eee5..ae0b8cfdae1d6b6bd0a96ebfc62d172696b1b83b 100644
--- a/src/object.h
+++ b/src/object.h
@@ -5,7 +5,7 @@
 \*/
 
 /*
- * $Id: object.h,v 1.48 2000/06/24 00:48:13 hubbe Exp $
+ * $Id: object.h,v 1.49 2000/07/18 05:48:20 mast Exp $
  */
 #ifndef OBJECT_H
 #define OBJECT_H
@@ -100,10 +100,9 @@ void gc_mark_object_as_referenced(struct object *o);
 unsigned gc_touch_all_objects(void);
 void gc_check_all_objects(void);
 void gc_mark_all_objects(void);
-void real_gc_cycle_check_object(struct object *o);
-void real_gc_cycle_check_object_weak(struct object *o);
-void real_gc_cycle_check_object_strong(struct object *o);
+void real_gc_cycle_check_object(struct object *o, int weak);
 void gc_cycle_check_all_objects(void);
+void gc_zap_ext_weak_refs_in_objects(void);
 void gc_free_all_unreferenced_objects(void);
 void count_memory_in_objects(INT32 *num_, INT32 *size_);
 struct magic_index_struct;
@@ -129,12 +128,8 @@ void check_all_objects(void);
 #define master() debug_master()
 #endif
 
-#define gc_cycle_check_object(X) \
-  enqueue_lifo(&gc_mark_queue, (queue_call) real_gc_cycle_check_object, (X))
-#define gc_cycle_check_object_weak(X) \
-  enqueue_lifo(&gc_mark_queue, (queue_call) real_gc_cycle_check_object_weak, (X))
-#define gc_cycle_check_object_strong(X) \
-  enqueue_lifo(&gc_mark_queue, (queue_call) real_gc_cycle_check_object_strong, (X))
+#define gc_cycle_check_object(X, WEAK) \
+  gc_cycle_enqueue((gc_cycle_check_cb *) real_gc_cycle_check_object, (X), (WEAK))
 
 #endif /* OBJECT_H */
 
diff --git a/src/program.c b/src/program.c
index cc64155704c4a49f0ba25eca6f93ce67b091a527..c56866e3af0b284d097e99ae0766381ff2106256 100644
--- a/src/program.c
+++ b/src/program.c
@@ -5,7 +5,7 @@
 \*/
 /**/
 #include "global.h"
-RCSID("$Id: program.c,v 1.250 2000/07/12 12:38:41 grubba Exp $");
+RCSID("$Id: program.c,v 1.251 2000/07/18 05:48:20 mast Exp $");
 #include "program.h"
 #include "object.h"
 #include "dynamic_buffer.h"
@@ -3586,40 +3586,28 @@ void gc_mark_program_as_referenced(struct program *p)
   }
 }
 
-void low_gc_cycle_check_program(struct program *p)
+void real_gc_cycle_check_program(struct program *p, int weak)
 {
-  int e;
-
-  if (p->flags & PROGRAM_AVOID_CHECK)
-    /* Program is in an inconsistent state.
-     * don't look closer at it.
-     */
-    return;
-
-  for(e=0;e<p->num_constants;e++)
-    gc_cycle_check_svalues(& p->constants[e].sval, 1);
+  GC_CYCLE_ENTER(p, weak) {
+    int e;
 
-  for(e=0;e<p->num_inherits;e++)
-  {
-    if(p->inherits[e].parent)
-      gc_cycle_check_object(p->inherits[e].parent);
+    if (p->flags & PROGRAM_AVOID_CHECK)
+      /* Program is in an inconsistent state.
+       * don't look closer at it.
+       */
+      return;
 
-    if(e && p->inherits[e].prog)
-      gc_cycle_check_program(p->inherits[e].prog);
-  }
-}
+    for(e=0;e<p->num_constants;e++)
+      gc_cycle_check_svalues(& p->constants[e].sval, 1);
 
-void real_gc_cycle_check_program(struct program *p)
-{
-  GC_CYCLE_ENTER(p, 0) {
-    low_gc_cycle_check_program(p);
-  } GC_CYCLE_LEAVE;
-}
+    for(e=0;e<p->num_inherits;e++)
+    {
+      if(p->inherits[e].parent)
+	gc_cycle_check_object(p->inherits[e].parent, 0);
 
-void real_gc_cycle_check_program_weak(struct program *p)
-{
-  GC_CYCLE_ENTER(p, 1) {
-    low_gc_cycle_check_program(p);
+      if(e && p->inherits[e].prog)
+	gc_cycle_check_program(p->inherits[e].prog, 0);
+    }
   } GC_CYCLE_LEAVE;
 }
 
@@ -3723,28 +3711,26 @@ void gc_cycle_check_all_programs(void)
 {
   struct program *p;
   for (p = gc_internal_program; p; p = p->next) {
-    real_gc_cycle_check_program(p);
-    run_lifo_queue(&gc_mark_queue);
+    real_gc_cycle_check_program(p, 0);
+    gc_cycle_run_queue();
+  }
+}
+
+void gc_zap_ext_weak_refs_in_programs(void)
+{
+  gc_mark_program_pos = first_program;
+  while (gc_mark_program_pos != gc_internal_program && gc_ext_weak_refs) {
+    struct program *p = gc_mark_program_pos;
+    gc_mark_program_pos = p->next;
+    gc_mark_program_as_referenced(p);
   }
+  discard_queue(&gc_mark_queue);
 }
 
 void gc_free_all_unreferenced_programs(void)
 {
   struct program *p,*next;
 
-  if (gc_ext_weak_refs) {
-    /* Have to go through all marked things if we got external weak
-     * references to otherwise unreferenced things, so the mark
-     * functions can free those references. */
-    gc_mark_program_pos = first_program;
-    while (gc_mark_program_pos != gc_internal_program && gc_ext_weak_refs) {
-      struct program *p = gc_mark_program_pos;
-      gc_mark_program_pos = p->next;
-      gc_mark_program_as_referenced(p);
-    }
-    discard_queue(&gc_mark_queue);
-  }
-
   for(p=gc_internal_program;p;p=next)
   {
     debug_malloc_touch(p);
diff --git a/src/program.h b/src/program.h
index 11434bc6581293f439d805ce68fb13423a8807f2..c0b338d047d394eab3df9f80aedba15359f3d3cf 100644
--- a/src/program.h
+++ b/src/program.h
@@ -5,7 +5,7 @@
 \*/
 
 /*
- * $Id: program.h,v 1.95 2000/07/12 16:09:36 grubba Exp $
+ * $Id: program.h,v 1.96 2000/07/18 05:48:20 mast Exp $
  */
 #ifndef PROGRAM_H
 #define PROGRAM_H
@@ -471,9 +471,9 @@ void gc_mark_program_as_referenced(struct program *p);
 unsigned gc_touch_all_programs(void);
 void gc_check_all_programs(void);
 void gc_mark_all_programs(void);
-void real_gc_cycle_check_program(struct program *p);
-void real_gc_cycle_check_program_weak(struct program *p);
+void real_gc_cycle_check_program(struct program *p, int weak);
 void gc_cycle_check_all_programs(void);
+void gc_zap_ext_weak_refs_in_programs(void);
 void gc_free_all_unreferenced_programs(void);
 void count_memory_in_programs(INT32 *num_, INT32 *size_);
 void push_compiler_frame(int lexical_scope);
@@ -542,10 +542,8 @@ void *parent_storage(int depth);
 #define start_new_program() debug_start_new_program()
 #endif
 
-#define gc_cycle_check_program(X) \
-  enqueue_lifo(&gc_mark_queue, (queue_call) real_gc_cycle_check_program, (X))
-#define gc_cycle_check_program_weak(X) \
-  enqueue_lifo(&gc_mark_queue, (queue_call) real_gc_cycle_check_program_weak, (X))
+#define gc_cycle_check_program(X, WEAK) \
+  gc_cycle_enqueue((gc_cycle_check_cb *) real_gc_cycle_check_program, (X), (WEAK))
 
 /* This can be used for backwards compatibility
  *  (if added to program.h in Pike 0.6 and Pike 7.0
diff --git a/src/svalue.c b/src/svalue.c
index fc7757d9fa54ee084ac8777a1612cea708580434..0a401a80c1b5ad6586830f66d513816002ca72fa 100644
--- a/src/svalue.c
+++ b/src/svalue.c
@@ -24,7 +24,7 @@
 #include "queue.h"
 #include "bignum.h"
 
-RCSID("$Id: svalue.c,v 1.83 2000/07/11 03:45:10 mast Exp $");
+RCSID("$Id: svalue.c,v 1.84 2000/07/18 05:48:20 mast Exp $");
 
 struct svalue dest_ob_zero = { T_INT, 0 };
 
@@ -1515,8 +1515,8 @@ int gc_mark_weak_short_svalue(union anything *u, TYPE_T type)
 
 #define DO_CYCLE_CHECK_STRING(U)
 
-#define GC_DO_CYCLE_CHECK(U, TN) PIKE_CONCAT(gc_cycle_check_, TN)(U.TN)
-#define GC_DO_CYCLE_CHECK_WEAK(U, TN) PIKE_CONCAT3(gc_cycle_check_, TN, _weak)(U.TN)
+#define GC_DO_CYCLE_CHECK(U, TN) PIKE_CONCAT(gc_cycle_check_, TN)(U.TN, 0)
+#define GC_DO_CYCLE_CHECK_WEAK(U, TN) PIKE_CONCAT(gc_cycle_check_, TN)(U.TN, 1)
 
 TYPE_FIELD real_gc_cycle_check_svalues(struct svalue *s, size_t num)
 {
diff --git a/src/testsuite.in b/src/testsuite.in
index d549826ad0f976364547089d9a574978d1f3c381..c721eb4290c84d18592cbfb5195d474b5e01832d 100644
--- a/src/testsuite.in
+++ b/src/testsuite.in
@@ -1,4 +1,4 @@
-test_true([["$Id: testsuite.in,v 1.313 2000/07/11 20:42:58 hubbe Exp $"]]);
+test_true([["$Id: testsuite.in,v 1.314 2000/07/18 05:48:20 mast Exp $"]]);
 
 cond([[all_constants()->_verify_internals]],
 [[
@@ -1945,7 +1945,7 @@ ifefun(gc,
       string id;
       void create (int i) {id = sprintf (\"dead[%d]\", i);}
       mixed a = 1, b = 1; // Mustn't be zero at destruct time.
-      mixed x, y;
+      mixed x, y, z;
       array v = set_weak_flag (({1}), 1); // Mustn't be zero at destruct time.
       array w = set_weak_flag (({0, 0}), 1);
       function(object:mixed) checkfn;
@@ -1962,6 +1962,7 @@ ifefun(gc,
 	  if (objectp (b) && !checked[b]) b->check_live (checked);
 	  if (objectp (x) && !checked[x]) x->check_live (checked);
 	  if (objectp (y) && !checked[y]) y->check_live (checked);
+	  if (objectp (z) && !checked[z]) z->check_live (checked);
 	  if (objectp (v[0]) && !checked[v[0] ]) v[0]->check_live (checked);
 	  if (objectp (w[0]) && !checked[w[0] ]) w[0]->check_live (checked);
 	  if (objectp (w[1]) && !checked[w[1] ]) w[1]->check_live (checked);
@@ -2476,6 +2477,34 @@ ifefun(gc,
 		      live_nested[1]->x = dead_nested[0];
 		      dead_nested[0]->x = live_nested[0];
 		    }}),
+      ({2, 0, 2, 0, lambda() {	// 72
+		      live[0]->x = live[1];
+		      live[1]->x = live_nested[1];
+		      live_nested[1]->x = live[0];
+		      live_nested[0]->x = live[1];
+		    }}),
+      ({2, 0, 4, 0, lambda() {	// 73
+		      live[0]->x = live[1], live[0]->y = live_nested[2], live[0]->z = live_nested[3];
+		      live[1]->x = live[0];
+		      live_nested[1]->x = live[0];
+		    }}),
+      ({2, 0, 4, 0, lambda() {	// 74
+		      live[0]->y = live[1], live[0]->z = live_nested[2], live[0]->x = live_nested[3];
+		      live[1]->x = live[0];
+		      live_nested[1]->x = live[0];
+		    }}),
+      ({2, 0, 4, 0, lambda() {	// 75
+		      live[0]->z = live[1], live[0]->x = live_nested[2], live[0]->y = live_nested[3];
+		      live[1]->x = live[0];
+		      live_nested[1]->x = live[0];
+		    }}),
+      ({2, 1, 2, 0, lambda() {	// 76
+		      dead[0]->x = dead[0], dead[0]->a = live_nested[0];
+		      live_nested[0]->y = live_nested[1], live_nested[0]->x = live[1];
+		      live_nested[1]->x = live[0];
+		      live[0]->x = live_nested[0];
+		      live[1]->x = live[0];
+		    }}),
       //       ({3, 0, 0, 0, lambda() { // Not possible without weak refs directly in objects.
       // 		   live[0]->x = live[0], live[0]->v[0] = live[1];
       // 		   live[1]->x = live[1], live[1]->w[0] = live[2];
@@ -2541,8 +2570,8 @@ ifefun(gc,
 	}
       }
     }
-    werror ("%60s\r", "");
     if (test_failed) error ("GC destruct order test failed.\n");
+    werror ("%60s\r", "");
   }]])
 
   test_any([[mapping q=([ "t":class {} ()]); gc(); if(!objectp(q->t)) return -1; set_weak_flag(q,1); gc(); if(objectp(q->t)) return -2; return 0;]],0);