From 6d30f5d60a3fe5b4774c46237a2575b5bfe5cf65 Mon Sep 17 00:00:00 2001
From: Martin Stjernholm <mast@lysator.liu.se>
Date: Tue, 11 Jul 2000 05:45:10 +0200
Subject: [PATCH] Fixed bug in gc where things which got only weak refs
 externally but non-weak internal cyclic refs didn't get gc'd.

Rev: src/array.c:1.77
Rev: src/gc.c:1.105
Rev: src/gc.h:1.54
Rev: src/mapping.c:1.92
Rev: src/multiset.c:1.24
Rev: src/object.c:1.135
Rev: src/program.c:1.249
Rev: src/queue.c:1.5
Rev: src/queue.h:1.4
Rev: src/svalue.c:1.83
Rev: src/testsuite.in:1.312
---
 src/array.c      | 15 ++++++++-
 src/gc.c         | 80 ++++++++++++++++++++++++++++++++++++++++++------
 src/gc.h         | 11 ++++---
 src/mapping.c    | 18 +++++++++--
 src/multiset.c   |  5 ++-
 src/object.c     | 16 +++++++++-
 src/program.c    | 17 ++++++++--
 src/queue.c      | 12 ++++++++
 src/queue.h      |  1 +
 src/svalue.c     | 69 +++++++++++++++++++++++++----------------
 src/testsuite.in | 48 ++++++++++++++++++++++++++++-
 11 files changed, 243 insertions(+), 49 deletions(-)

diff --git a/src/array.c b/src/array.c
index e2aadaab90..f62b346c21 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.76 2000/07/07 01:26:39 hubbe Exp $");
+RCSID("$Id: array.c,v 1.77 2000/07/11 03:45:09 mast Exp $");
 
 struct array empty_array=
 {
@@ -1957,6 +1957,19 @@ 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/gc.c b/src/gc.c
index 4168d9ffcd..93926548c9 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.104 2000/07/07 15:33:29 mast Exp $");
+RCSID("$Id: gc.c,v 1.105 2000/07/11 03:45:09 mast Exp $");
 
 /* Run garbage collect approximately every time
  * 20 percent of all arrays, objects and programs is
@@ -102,6 +102,7 @@ static struct marker rec_list = {0, 0, 0};
 struct marker *gc_rec_last = &rec_list;
 static struct marker *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
@@ -770,8 +771,13 @@ void debug_gc_touch(void *a)
       else if (m->weak_refs == -1)
 	gc_fatal(a, 3, "A thing which had only weak references is "
 		 "still around after gc.\n");
-      else if (!(m->flags & GC_LIVE))
-	gc_fatal(a, 3, "A thing to garb is still around.\n");
+      else if (!(m->flags & GC_LIVE)) {
+	if (m->weak_refs > 0)
+	  gc_fatal(a, 3, "A thing to garb is still around. "
+		   "It's probably one with only external weak refs.\n");
+	else
+	  gc_fatal(a, 3, "A thing to garb is still around.\n");
+      }
     }
   }
   else
@@ -847,6 +853,7 @@ INT32 real_gc_check_weak(void *a)
 #endif
 
   m->weak_refs++;
+  gc_ext_weak_refs++;
   if (m->weak_refs >= *(INT32 *) a)
     m->weak_refs = -1;
 
@@ -1022,7 +1029,8 @@ int gc_do_weak_free(void *a)
   struct marker *m;
 
   if (!a) fatal("Got null pointer.\n");
-  if (Pike_in_gc != GC_PASS_MARK && Pike_in_gc != GC_PASS_CYCLE)
+  if (Pike_in_gc != GC_PASS_MARK && Pike_in_gc != GC_PASS_CYCLE &&
+      !(Pike_in_gc == GC_PASS_FREE && gc_ext_weak_refs))
     fatal("gc_do_weak_free() called in invalid gc pass.\n");
   if (gc_debug) {
     if (!(m = find_marker(a)))
@@ -1034,7 +1042,22 @@ int gc_do_weak_free(void *a)
   if (m->weak_refs > m->refs)
     gc_fatal(a, 0, "More weak references than internal references.\n");
 
-  return m->weak_refs == -1;
+  if (Pike_in_gc != GC_PASS_FREE) {
+    if (m->weak_refs == -1) {
+      gc_ext_weak_refs--;
+      return 1;
+    }
+  }
+  else
+    if (!(m->flags & GC_MARKED)) {
+      if (m->weak_refs <= 0)
+	gc_fatal(a, 0, "Too many weak refs cleared to thing with external "
+		 "weak refs.\n");
+      m->weak_refs--;
+      gc_ext_weak_refs--;
+      return 1;
+    }
+  return 0;
 }
 
 #endif /* PIKE_DEBUG */
@@ -1045,16 +1068,42 @@ int gc_mark(void *a)
 
 #ifdef PIKE_DEBUG
   if (!a) fatal("Got null pointer.\n");
-  if (Pike_in_gc != GC_PASS_MARK)
+  if (Pike_in_gc != GC_PASS_MARK &&
+      !(Pike_in_gc == GC_PASS_FREE && gc_ext_weak_refs))
     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 "
+	     "that wasn't marked before.\n");
 #endif
 
-  if(m->flags & GC_MARKED)
-  {
+  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 (m->flags & GC_FREE_VISITED)
+      return 0;
+    else {
+      m->flags |= GC_FREE_VISITED;
+      return 1;
+    }
+
+  else if (m->flags & GC_MARKED) {
+#ifdef PIKE_DEBUG
+    if (m->weak_refs != 0)
+      gc_fatal(a, 0, "weak_refs changed in marker already visited by gc_mark().\n");
+#endif
     return 0;
-  }else{
+  }
+  else {
+    if (m->weak_refs) {
+      gc_ext_weak_refs -= m->weak_refs;
+      m->weak_refs = 0;
+    }
     m->flags = (m->flags & ~GC_NOT_REFERENCED) | GC_MARKED;
     DO_IF_DEBUG(marked++);
     return 1;
@@ -1212,6 +1261,15 @@ int gc_cycle_push(void *x, struct marker *m, int weak)
   if (weak < 0) gc_rec_last->flags |= GC_FOLLOWED_STRONG;
 #endif
 
+  if (weak > 0) {
+#ifdef PIKE_DEBUG
+    if (m->weak_refs <= 0)
+      gc_fatal(x, 0, "Followed weak ref to thing that should have none left.\n");
+    m->weak_refs--;
+#endif
+    gc_ext_weak_refs--;
+  }
+
   if (m->flags & GC_IN_REC_LIST) { /* A cyclic reference is found. */
 #ifdef PIKE_DEBUG
     if (m == &rec_list || gc_rec_last == &rec_list)
@@ -1670,6 +1728,7 @@ int do_gc(void)
 #endif
 
   Pike_in_gc=GC_PASS_CHECK;
+  gc_ext_weak_refs = 0;
   /* First we count internal references */
   gc_check_all_arrays();
   gc_check_all_multisets();
@@ -1835,6 +1894,9 @@ int do_gc(void)
   }
   if (gc_extra_refs)
     fatal("Lost track of %d extra refs to things in gc.\n", gc_extra_refs);
+  if (gc_ext_weak_refs)
+    fatal("Still got %u external weak references to internal things in gc.\n",
+	  gc_ext_weak_refs);
 #endif
 
   Pike_in_gc=0;
diff --git a/src/gc.h b/src/gc.h
index 2cf31512f8..146b2ceddb 100644
--- a/src/gc.h
+++ b/src/gc.h
@@ -1,5 +1,5 @@
 /*
- * $Id: gc.h,v 1.53 2000/07/04 00:43:57 mast Exp $
+ * $Id: gc.h,v 1.54 2000/07/11 03:45:10 mast Exp $
  */
 #ifndef GC_H
 #define GC_H
@@ -93,6 +93,7 @@ struct marker
 #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
@@ -106,6 +107,7 @@ struct marker
 PTR_HASH_ALLOC(marker,MARKER_CHUNK_SIZE)
 
 extern struct marker *gc_rec_last;
+extern size_t gc_ext_weak_refs;
 
 /* Prototypes begin here */
 struct callback *debug_add_gc_callback(callback_func call,
@@ -187,12 +189,11 @@ 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) (get_marker(X)->weak_refs == -1)
+#define gc_do_weak_free(X) (Pike_in_gc != GC_PASS_FREE ?		\
+			    get_marker(X)->weak_refs == -1 :		\
+			    !(get_marker(X)->flags & GC_MARKED))
 #endif
 
-#define gc_do_weak_free_svalue(S) \
-  ((S)->type <= MAX_COMPLEX && gc_do_weak_free((S)->u.refs))
-
 #define gc_external_mark2(X,Y,Z) gc_external_mark3( debug_malloc_pass(X),(Y),(Z))
 #define gc_external_mark(X) gc_external_mark2( (X),"externally", 0)
 
diff --git a/src/mapping.c b/src/mapping.c
index b43d44273d..93b6bd3d19 100644
--- a/src/mapping.c
+++ b/src/mapping.c
@@ -5,7 +5,7 @@
 \*/
 /**/
 #include "global.h"
-RCSID("$Id: mapping.c,v 1.91 2000/07/06 23:25:26 mast Exp $");
+RCSID("$Id: mapping.c,v 1.92 2000/07/11 03:45:10 mast Exp $");
 #include "main.h"
 #include "object.h"
 #include "mapping.h"
@@ -846,7 +846,8 @@ 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_MARK)
+  if (Pike_in_gc > GC_PASS_PREPARE && Pike_in_gc < GC_PASS_FREE &&
+      Pike_in_gc != GC_PASS_MARK)
     fatal("check_mapping_for_destruct called in wrong pass inside gc.\n");
 #endif
 
@@ -2060,6 +2061,19 @@ 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/multiset.c b/src/multiset.c
index 98bc4392a9..8340d086ad 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.23 2000/07/06 23:25:26 mast Exp $");
+RCSID("$Id: multiset.c,v 1.24 2000/07/11 03:45:10 mast Exp $");
 
 struct multiset *first_multiset;
 
@@ -369,6 +369,9 @@ 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/object.c b/src/object.c
index e3e66866fe..13b58c32cc 100644
--- a/src/object.c
+++ b/src/object.c
@@ -5,7 +5,7 @@
 \*/
 /**/
 #include "global.h"
-RCSID("$Id: object.c,v 1.134 2000/07/10 21:05:25 mast Exp $");
+RCSID("$Id: object.c,v 1.135 2000/07/11 03:45:10 mast Exp $");
 #include "object.h"
 #include "dynamic_buffer.h"
 #include "interpret.h"
@@ -1477,6 +1477,20 @@ 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/program.c b/src/program.c
index 3c2f8787c2..037b4d7c21 100644
--- a/src/program.c
+++ b/src/program.c
@@ -5,7 +5,7 @@
 \*/
 /**/
 #include "global.h"
-RCSID("$Id: program.c,v 1.248 2000/07/10 18:21:32 grubba Exp $");
+RCSID("$Id: program.c,v 1.249 2000/07/11 03:45:10 mast Exp $");
 #include "program.h"
 #include "object.h"
 #include "dynamic_buffer.h"
@@ -3728,6 +3728,19 @@ 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);
@@ -3765,7 +3778,7 @@ void gc_free_all_unreferenced_programs(void)
     for (p = first_program; p != gc_internal_program; p = p->next) {
       int e,tmp=0;
       if (!p)
-	fatal("gc_internal_program is bogus.\n");
+	fatal("gc_internal_program was bogus.\n");
       for(e=0;e<p->num_constants;e++)
       {
 	if(p->constants[e].sval.type == T_PROGRAM && p->constants[e].sval.u.program == p)
diff --git a/src/queue.c b/src/queue.c
index dbfb1f2632..41e62d27dc 100644
--- a/src/queue.c
+++ b/src/queue.c
@@ -42,6 +42,18 @@ void run_queue(struct pike_queue *q)
   q->last=0;
 }
 
+void discard_queue(struct pike_queue *q)
+{
+  struct queue_block *b = q->first;
+  while (b)
+  {
+    struct queue_block *next = b->next;
+    free((char *) b);
+    b = next;
+  }
+  q->first = q->last = 0;
+}
+
 void enqueue(struct pike_queue *q, queue_call call, void *data)
 {
   struct queue_block *b;
diff --git a/src/queue.h b/src/queue.h
index fb0bfc4ce5..7d60308e38 100644
--- a/src/queue.h
+++ b/src/queue.h
@@ -12,6 +12,7 @@ typedef void (*queue_call)(void *data);
 struct queue_entry;
 struct queue_block;
 void run_queue(struct pike_queue *q);
+void discard_queue(struct pike_queue *q);
 void enqueue(struct pike_queue *q, queue_call call, void *data);
 void run_lifo_queue(struct pike_queue *q);
 void enqueue_lifo(struct pike_queue *q, queue_call call, void *data);
diff --git a/src/svalue.c b/src/svalue.c
index 5a1852951e..fc7757d9fa 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.82 2000/07/06 22:07:41 grubba Exp $");
+RCSID("$Id: svalue.c,v 1.83 2000/07/11 03:45:10 mast Exp $");
 
 struct svalue dest_ob_zero = { T_INT, 0 };
 
@@ -1392,7 +1392,7 @@ void gc_check_weak_short_svalue(union anything *u, TYPE_T type)
 	u->refs = 0;							\
       } while (0)
 
-#define GC_RECURSE_SWITCH(U,T,ZAP,FREE_WEAK,GC_DO,PRE,DO_FUNC,DO_STR)	\
+#define GC_RECURSE_SWITCH(U,T,ZAP,FREE_WEAK,GC_DO,PRE,DO_FUNC,DO_OBJ,DO_STR) \
   switch (T) {								\
     case T_FUNCTION:							\
       PRE DO_FUNC(U, T, ZAP, GC_DO)					\
@@ -1403,7 +1403,7 @@ void gc_check_weak_short_svalue(union anything *u, TYPE_T type)
 	freed = 1;							\
 	break;								\
       }									\
-      FREE_WEAK(U, T, ZAP) GC_DO(U, object);				\
+      FREE_WEAK(U, T, ZAP) DO_OBJ(U, object);				\
       break;								\
     case T_STRING:							\
       DO_STR(U); break;							\
@@ -1417,6 +1417,22 @@ void gc_check_weak_short_svalue(union anything *u, TYPE_T type)
       PRE FREE_WEAK(U, T, ZAP) GC_DO(U, mapping); break;		\
   }
 
+#define DONT_FREE_WEAK(U, T, ZAP)
+
+#define FREE_WEAK(U, T, ZAP)						\
+      if (gc_do_weak_free(U.refs)) {					\
+	ZAP();								\
+	freed = 1;							\
+	break;								\
+      }
+
+#define GC_DO_MARK(U, TN)						\
+      enqueue(&gc_mark_queue,						\
+	      (queue_call) PIKE_CONCAT3(gc_mark_, TN, _as_referenced),	\
+	      U.TN)
+
+#define GC_DO_NO_WEAK_MARK(U, TN) do {} while (0)
+
 #define DO_MARK_FUNC_SVALUE(U, T, ZAP, GC_DO)				\
       if (s->subtype == FUNCTION_BUILTIN) {				\
 	DO_IF_DEBUG(							\
@@ -1429,23 +1445,14 @@ void gc_check_weak_short_svalue(union anything *u, TYPE_T type)
       }									\
       /* Fall through to T_OBJECT. */
 
+#define DO_MARK_OBJ_WEAK(U, TN)						\
+      if (U.object->prog &&						\
+	  (U.object->prog->flags & PROGRAM_NO_WEAK_FREE))		\
+	GC_DO_MARK(U, TN)
+
 #define DO_MARK_STRING(U)						\
       DO_IF_DEBUG(if (U.refs && d_flag) gc_mark(U.string))
 
-#define GC_DO_MARK(U, TN)						\
-  enqueue(&gc_mark_queue,						\
-	  (queue_call) PIKE_CONCAT3(gc_mark_, TN, _as_referenced),	\
-	  U.TN)
-
-#define DONT_FREE_WEAK(U, T, ZAP)
-
-#define FREE_WEAK(U, T, ZAP)						\
-    if (gc_do_weak_free(U.refs)) {					\
-      ZAP();								\
-      freed = 1;							\
-      break;								\
-    }
-
 TYPE_FIELD real_gc_mark_svalues(struct svalue *s, size_t num)
 {
   TYPE_FIELD t = 0;
@@ -1456,7 +1463,8 @@ TYPE_FIELD real_gc_mark_svalues(struct svalue *s, size_t num)
     dmalloc_touch_svalue(s);
     GC_RECURSE_SWITCH((s->u), (s->type), ZAP_SVALUE, DONT_FREE_WEAK,
 		      GC_DO_MARK, {},
-		      DO_MARK_FUNC_SVALUE, DO_MARK_STRING);
+		      DO_MARK_FUNC_SVALUE, GC_DO_MARK,
+		      DO_MARK_STRING);
     t |= 1 << s->type;
   }
   return freed ? t : 0;
@@ -1471,8 +1479,9 @@ TYPE_FIELD gc_mark_weak_svalues(struct svalue *s, size_t num)
   {
     dmalloc_touch_svalue(s);
     GC_RECURSE_SWITCH((s->u), (s->type), ZAP_SVALUE, FREE_WEAK,
-		      GC_DO_MARK, {},
-		      DO_MARK_FUNC_SVALUE, DO_MARK_STRING);
+		      GC_DO_NO_WEAK_MARK, {},
+		      DO_MARK_FUNC_SVALUE, DO_MARK_OBJ_WEAK,
+		      DO_MARK_STRING);
     t |= 1 << s->type;
   }
   return freed ? t : 0;
@@ -1484,7 +1493,8 @@ int real_gc_mark_short_svalue(union anything *u, TYPE_T type)
   debug_malloc_touch(u);
   GC_RECURSE_SWITCH((*u), type, ZAP_SHORT_SVALUE, DONT_FREE_WEAK,
 		    GC_DO_MARK, {if (!u->refs) return 0;},
-		    DO_FUNC_SHORT_SVALUE, DO_MARK_STRING);
+		    DO_FUNC_SHORT_SVALUE, GC_DO_MARK,
+		    DO_MARK_STRING);
   return freed;
 }
 
@@ -1493,8 +1503,9 @@ int gc_mark_weak_short_svalue(union anything *u, TYPE_T type)
   int freed = 0;
   debug_malloc_touch(u);
   GC_RECURSE_SWITCH((*u), type, ZAP_SHORT_SVALUE, FREE_WEAK,
-		    GC_DO_MARK, {if (!u->refs) return 0;},
-		    DO_FUNC_SHORT_SVALUE, DO_MARK_STRING);
+		    GC_DO_NO_WEAK_MARK, {if (!u->refs) return 0;},
+		    DO_FUNC_SHORT_SVALUE, DO_MARK_OBJ_WEAK,
+		    DO_MARK_STRING);
   return freed;
 }
 
@@ -1517,7 +1528,8 @@ TYPE_FIELD real_gc_cycle_check_svalues(struct svalue *s, size_t num)
     dmalloc_touch_svalue(s);
     GC_RECURSE_SWITCH((s->u), (s->type), ZAP_SVALUE, DONT_FREE_WEAK,
 		      GC_DO_CYCLE_CHECK, {},
-		      DO_CYCLE_CHECK_FUNC_SVALUE, DO_CYCLE_CHECK_STRING);
+		      DO_CYCLE_CHECK_FUNC_SVALUE, GC_DO_CYCLE_CHECK,
+		      DO_CYCLE_CHECK_STRING);
     t |= 1 << s->type;
   }
   return freed ? t : 0;
@@ -1533,7 +1545,8 @@ TYPE_FIELD gc_cycle_check_weak_svalues(struct svalue *s, size_t num)
     dmalloc_touch_svalue(s);
     GC_RECURSE_SWITCH((s->u), (s->type), ZAP_SVALUE, FREE_WEAK,
 		      GC_DO_CYCLE_CHECK_WEAK, {},
-		      DO_CYCLE_CHECK_FUNC_SVALUE, DO_CYCLE_CHECK_STRING);
+		      DO_CYCLE_CHECK_FUNC_SVALUE, GC_DO_CYCLE_CHECK_WEAK,
+		      DO_CYCLE_CHECK_STRING);
     t |= 1 << s->type;
   }
   return freed ? t : 0;
@@ -1545,7 +1558,8 @@ int real_gc_cycle_check_short_svalue(union anything *u, TYPE_T type)
   debug_malloc_touch(u);
   GC_RECURSE_SWITCH((*u), type, ZAP_SHORT_SVALUE, DONT_FREE_WEAK,
 		    GC_DO_CYCLE_CHECK, {if (!u->refs) return 0;},
-		    DO_FUNC_SHORT_SVALUE, DO_CYCLE_CHECK_STRING);
+		    DO_FUNC_SHORT_SVALUE, GC_DO_CYCLE_CHECK,
+		    DO_CYCLE_CHECK_STRING);
   return freed;
 }
 
@@ -1555,7 +1569,8 @@ int gc_cycle_check_weak_short_svalue(union anything *u, TYPE_T type)
   debug_malloc_touch(u);
   GC_RECURSE_SWITCH((*u), type, ZAP_SHORT_SVALUE, FREE_WEAK,
 		    GC_DO_CYCLE_CHECK_WEAK, {if (!u->refs) return 0;},
-		    DO_FUNC_SHORT_SVALUE, DO_CYCLE_CHECK_STRING);
+		    DO_FUNC_SHORT_SVALUE, GC_DO_CYCLE_CHECK_WEAK,
+		    DO_CYCLE_CHECK_STRING);
   return freed;
 }
 
diff --git a/src/testsuite.in b/src/testsuite.in
index f658b77d2e..125b2f07c0 100644
--- a/src/testsuite.in
+++ b/src/testsuite.in
@@ -1,4 +1,4 @@
-test_true([["$Id: testsuite.in,v 1.311 2000/07/10 17:28:28 grubba Exp $"]]);
+test_true([["$Id: testsuite.in,v 1.312 2000/07/11 03:45:10 mast Exp $"]]);
 
 cond([[all_constants()->_verify_internals]],
 [[
@@ -1886,6 +1886,52 @@ ifefun(gc,
     gc();
   }]]);
 
+  test_any([[{
+    array a = ({0}), b = ({a, set_weak_flag (({a}), 1)});
+    array x = set_weak_flag (({a}), 1);
+    a[0] = b;
+    a = b = 0;
+    gc();
+    return !x[0];
+  }]], 1);
+  test_any([[{
+    mapping a = ([]), b = ([a:set_weak_flag (([a:a]), 1)]);
+    mapping x = set_weak_flag (([a:2]), 1);
+    a[b] = b;
+    a = b = 0;
+    gc();
+    return !sizeof (x);
+  }]], 1);
+  test_any([[{
+    multiset a = (<>), b = (<a, set_weak_flag ((<a>), 1)>);
+    multiset x = set_weak_flag ((<a>), 1);
+    a[b] = 1;
+    a = b = 0;
+    gc();
+    return !sizeof (x);
+  }]], 1);
+  test_any([[{
+    class Foo {
+      Foo f = this_object();
+      multiset(Foo) g = set_weak_flag((<this_object()>), 1);
+    };
+    multiset(Foo) x = set_weak_flag ((<Foo()>), 1);
+    gc();
+    return !sizeof (x);
+  }]], 1);
+  test_any([[{
+    class Foo {
+      Foo f = this_object();
+      multiset(Foo) g = set_weak_flag((<this_object()>), 1);
+      void destroy() {add_constant("beltbent_oblivion", 1);}
+    };
+    multiset(Foo) x = set_weak_flag ((<Foo()>), 1);
+    gc();
+    int res = all_constants()->beltbent_oblivion;
+    add_constant("beltbent_oblivion");
+    return res;
+  }]], 1);
+
   test_do([[{
     int got_error = 0;
     array(string) destruct_order;
-- 
GitLab