From a503493eb1ed2be1460af61224aa10dc2cb0074e Mon Sep 17 00:00:00 2001
From: Martin Stjernholm <mast@lysator.liu.se>
Date: Mon, 6 Apr 2009 02:14:54 +0200
Subject: [PATCH] Fixed handling of destructed indices.

---
 src/multiset.c | 388 +++++++++++++++++++++++++++++++++----------------
 1 file changed, 266 insertions(+), 122 deletions(-)

diff --git a/src/multiset.c b/src/multiset.c
index f987571785..6ce74ae13a 100644
--- a/src/multiset.c
+++ b/src/multiset.c
@@ -913,11 +913,12 @@ PMOD_EXPORT void do_sub_msnode_ref (struct multiset *l)
 
 enum find_types {
   FIND_EQUAL,
-  FIND_NOEQUAL,
+  /* FIND_NOEQUAL, */
   FIND_LESS,
   FIND_GREATER,
   FIND_NOROOT,
-  FIND_DESTRUCTED
+  FIND_DESTRUCTED,
+  FIND_KEY_DESTRUCTED
 };
 
 static enum find_types low_multiset_find_le_gt (
@@ -1112,35 +1113,44 @@ again:
 
     do {
       low_use_multiset_index (new.list, ind);
-      /* FIXME: Handle destructed object in ind. */
       next = low_multiset_next (new.list);
 
       /* Note: Similar code in mkmultiset_2 and copy_multiset_recursively. */
 
-      while (1) {
-	RBSTACK_INIT (rbstack);
+      if (IS_DESTRUCTED (&ind))
+	new.msd->size--;
+      else {
+	while (1) {
+	  RBSTACK_INIT (rbstack);
 
-	if (!new.msd->root) {
-	  low_rb_init_root (HDR (new.msd->root = new.list));
-	  goto node_added;
-	}
+	  if (!new.msd->root) {
+	    low_rb_init_root (HDR (new.msd->root = new.list));
+	    goto node_done;
+	  }
 
-	switch (low_multiset_track_le_gt (new.msd, &ind, &rbstack)) {
-	  case FIND_LESS:
-	    low_rb_link_at_next (PHDR (&new.msd->root), rbstack, HDR (new.list));
-	    goto node_added;
-	  case FIND_GREATER:
-	    low_rb_link_at_prev (PHDR (&new.msd->root), rbstack, HDR (new.list));
-	    goto node_added;
-	  case FIND_DESTRUCTED:
-	    midflight_remove_node_faster (new.msd, rbstack);
-	    new.msd->size--;
-	    break;
-	  default: DO_IF_DEBUG (Pike_fatal ("Invalid find_type.\n"));
+	  switch (low_multiset_track_le_gt (new.msd, &ind, &rbstack)) {
+	    case FIND_LESS:
+	      low_rb_link_at_next (PHDR (&new.msd->root),
+				   rbstack, HDR (new.list));
+	      goto node_done;
+	    case FIND_GREATER:
+	      low_rb_link_at_prev (PHDR (&new.msd->root),
+				   rbstack, HDR (new.list));
+	      goto node_done;
+	    case FIND_DESTRUCTED:
+	      midflight_remove_node_faster (new.msd, rbstack);
+	      new.msd->size--;
+	      break;
+	    case FIND_KEY_DESTRUCTED:
+	      new.msd->size--;
+	      goto node_done;
+	    default: DO_IF_DEBUG (Pike_fatal ("Invalid find_type.\n"));
+	  }
 	}
+	/* NOT REACHED */
       }
 
-    node_added:
+    node_done:
       if (l->msd != old) {
 	/* l changed. Have to start over to guarantee no loss of data. */
 	CALL_AND_UNSET_ONERROR (uwp);
@@ -1214,42 +1224,51 @@ PMOD_EXPORT struct multiset *mkmultiset_2 (struct array *indices,
 	INODE (NODE_AT (new.msd, msnode_ind, pos));
       if (values) assign_svalue_no_free (&new.node->iv.val, &ITEM (values)[pos]);
       assign_svalue_no_free (&new.node->i.ind, &ITEM (indices)[pos]);
-      /* FIXME: Handle destructed objects in new.node->i.ind. */
 
       /* Note: Similar code in multiset_set_cmp_less and
        * copy_multiset_recursively. */
 
       /* Note: It would perhaps be a bit faster to use quicksort. */
 
-      while (1) {
-	RBSTACK_INIT (rbstack);
-
-	if (!new.msd->root) {
-	  low_rb_init_root (HDR (new.msd->root = new.node));
-	  goto node_added;
-	}
+      if (!IS_DESTRUCTED (&new.node->i.ind)) {
+	while (1) {
+	  RBSTACK_INIT (rbstack);
 
-	switch (low_multiset_track_le_gt (new.msd,
-					  &new.node->i.ind, /* Not clobbered yet. */
-					  &rbstack)) {
-	  case FIND_LESS:
-	    low_rb_link_at_next (PHDR (&new.msd->root), rbstack, HDR (new.node));
-	    goto node_added;
-	  case FIND_GREATER:
-	    low_rb_link_at_prev (PHDR (&new.msd->root), rbstack, HDR (new.node));
+	  if (!new.msd->root) {
+	    low_rb_init_root (HDR (new.msd->root = new.node));
 	    goto node_added;
-	  case FIND_DESTRUCTED:
-	    midflight_remove_node_faster (new.msd, rbstack);
-	    break;
-	  default: DO_IF_DEBUG (Pike_fatal ("Invalid find_type.\n"));
+	  }
+
+	  switch (low_multiset_track_le_gt (new.msd,
+					    /* Not clobbered yet. */
+					    &new.node->i.ind,
+					    &rbstack)) {
+	    case FIND_LESS:
+	      low_rb_link_at_next (PHDR (&new.msd->root),
+				   rbstack, HDR (new.node));
+	      goto node_added;
+	    case FIND_GREATER:
+	      low_rb_link_at_prev (PHDR (&new.msd->root),
+				   rbstack, HDR (new.node));
+	      goto node_added;
+	    case FIND_DESTRUCTED:
+	      midflight_remove_node_faster (new.msd, rbstack);
+	      break;
+	    case FIND_KEY_DESTRUCTED:
+	      goto node_skipped;
+	    default: DO_IF_DEBUG (Pike_fatal ("Invalid find_type.\n"));
+	  }
 	}
-      }
+	/* NOT REACHED */
 
-    node_added:
+      node_added:
 #ifdef PIKE_DEBUG
-      new.node->i.ind.type |= MULTISET_FLAG_MARKER;
+	new.node->i.ind.type |= MULTISET_FLAG_MARKER;
 #endif
-      new.msd->size++;
+	new.msd->size++;
+      }
+
+    node_skipped:;
     }
 
     UNSET_ONERROR (uwp);
@@ -1295,10 +1314,14 @@ union msnode *low_multiset_find_eq (struct multiset *l, struct svalue *key)
   struct rb_node_hdr *node;
   ONERROR uwp;
 
-  /* FIXME: Handle destructed object in key? */
-
   /* Note: Similar code in low_multiset_track_eq. */
 
+  if (IS_DESTRUCTED (key)) {
+    /* We only check for a destructed key initially - the result will
+     * be bogus if it's destructed during the search. */
+    return NULL;
+  }
+
   add_ref (msd);
   SET_ONERROR (uwp, free_indirect_multiset_data, &msd);
 
@@ -1408,6 +1431,7 @@ static enum find_types low_multiset_find_le_gt (
   struct multiset_data *msd, struct svalue *key, union msnode **found)
 {
   struct rb_node_hdr *node = HDR (msd->root);
+  enum find_types find_type;
 
   /* Note: Similar code in low_multiset_track_le_gt. */
 
@@ -1416,7 +1440,12 @@ static enum find_types low_multiset_find_le_gt (
   if (msd->refs == 1) Pike_fatal ("Copy-on-write assumed here.\n");
 #endif
 
-  if ((node = HDR (msd->root))) {
+  if (!(node = HDR (msd->root))) {
+    *found = NULL;
+    find_type = FIND_NOROOT;
+  }
+
+  else {
     if (msd->cmp_less.type == T_INT) {
       struct svalue tmp;
       LOW_RB_FIND_NEQ (
@@ -1432,8 +1461,9 @@ static enum find_types low_multiset_find_le_gt (
 	  INTERNAL_CMP (key, &tmp, cmp_res);
 	  cmp_res = cmp_res >= 0 ? 1 : -1;
 	},
-	{*found = RBNODE (node); return FIND_LESS;},
-	{*found = RBNODE (node); return FIND_GREATER;});
+	{*found = RBNODE (node); find_type = FIND_LESS; goto done;},
+	{*found = RBNODE (node); find_type = FIND_GREATER; goto done;});
+      /* NOT REACHED */
     }
 
     else {
@@ -1451,19 +1481,25 @@ static enum find_types low_multiset_find_le_gt (
 	  cmp_res = UNSAFE_IS_ZERO (sp - 1) ? 1 : -1;
 	  pop_stack();
 	},
-	{*found = RBNODE (node); return FIND_LESS;},
-	{*found = RBNODE (node); return FIND_GREATER;});
+	{*found = RBNODE (node); find_type = FIND_LESS; goto done;},
+	{*found = RBNODE (node); find_type = FIND_GREATER; goto done;});
+      /* NOT REACHED */
     }
   }
 
-  *found = NULL;
-  return FIND_NOROOT;
+done:
+  if (IS_DESTRUCTED (key)) {
+    *found = NULL;
+    return FIND_KEY_DESTRUCTED;
+  }
+  return find_type;
 }
 
 static enum find_types low_multiset_find_lt_ge (
   struct multiset_data *msd, struct svalue *key, union msnode **found)
 {
   struct rb_node_hdr *node = HDR (msd->root);
+  enum find_types find_type;
 
   /* Note: Similar code in low_multiset_track_lt_ge. */
 
@@ -1472,7 +1508,12 @@ static enum find_types low_multiset_find_lt_ge (
   if (msd->refs == 1) Pike_fatal ("Copy-on-write assumed here.\n");
 #endif
 
-  if ((node = HDR (msd->root))) {
+  if (!(node = HDR (msd->root))) {
+    *found = NULL;
+    find_type = FIND_NOROOT;
+  }
+
+  else {
     if (msd->cmp_less.type == T_INT) {
       struct svalue tmp;
       LOW_RB_FIND_NEQ (
@@ -1488,8 +1529,9 @@ static enum find_types low_multiset_find_lt_ge (
 	  INTERNAL_CMP (key, &tmp, cmp_res);
 	  cmp_res = cmp_res <= 0 ? -1 : 1;
 	},
-	{*found = RBNODE (node); return FIND_LESS;},
-	{*found = RBNODE (node); return FIND_GREATER;});
+	{*found = RBNODE (node); find_type = FIND_LESS; goto done;},
+	{*found = RBNODE (node); find_type = FIND_GREATER; goto done;});
+      /* NOT REACHED */
     }
 
     else {
@@ -1507,13 +1549,18 @@ static enum find_types low_multiset_find_lt_ge (
 	  cmp_res = UNSAFE_IS_ZERO (sp - 1) ? -1 : 1;
 	  pop_stack();
 	},
-	{*found = RBNODE (node); return FIND_LESS;},
-	{*found = RBNODE (node); return FIND_GREATER;});
+	{*found = RBNODE (node); find_type = FIND_LESS; goto done;},
+	{*found = RBNODE (node); find_type = FIND_GREATER; goto done;});
+      /* NOT REACHED */
     }
   }
 
-  *found = NULL;
-  return FIND_NOROOT;
+done:
+  if (IS_DESTRUCTED (key)) {
+    *found = NULL;
+    return FIND_KEY_DESTRUCTED;
+  }
+  return find_type;
 }
 
 PMOD_EXPORT ptrdiff_t multiset_find_lt (struct multiset *l, struct svalue *key)
@@ -1526,8 +1573,6 @@ PMOD_EXPORT ptrdiff_t multiset_find_lt (struct multiset *l, struct svalue *key)
   debug_malloc_touch (msd);
   check_svalue (key);
 
-  /* FIXME: Handle destructed object in key? */
-
   add_ref (msd);
   SET_ONERROR (uwp, free_indirect_multiset_data, &msd);
 
@@ -1543,6 +1588,7 @@ PMOD_EXPORT ptrdiff_t multiset_find_lt (struct multiset *l, struct svalue *key)
       switch (find_type) {
 	case FIND_LESS:
 	case FIND_NOROOT:
+	case FIND_KEY_DESTRUCTED:
 	  goto done;
 	case FIND_GREATER:	/* Got greater or equal - step back one. */
 	  node = INODE (node->i.prev);
@@ -1560,6 +1606,7 @@ PMOD_EXPORT ptrdiff_t multiset_find_lt (struct multiset *l, struct svalue *key)
 	      switch (find_type) {
 		case FIND_LESS:
 		case FIND_NOROOT:
+		case FIND_KEY_DESTRUCTED:
 		  node = RBNODE (RBSTACK_PEEK (rbstack));
 		  RBSTACK_FREE (rbstack);
 		  goto done;
@@ -1605,8 +1652,6 @@ PMOD_EXPORT ptrdiff_t multiset_find_ge (struct multiset *l, struct svalue *key)
   debug_malloc_touch (msd);
   check_svalue (key);
 
-  /* FIXME: Handle destructed object in key? */
-
   add_ref (msd);
   SET_ONERROR (uwp, free_indirect_multiset_data, &msd);
 
@@ -1624,6 +1669,7 @@ PMOD_EXPORT ptrdiff_t multiset_find_ge (struct multiset *l, struct svalue *key)
 	  node = INODE (node->i.next);
 	  goto done;
 	case FIND_NOROOT:
+	case FIND_KEY_DESTRUCTED:
 	case FIND_GREATER:
 	  goto done;
 
@@ -1642,6 +1688,7 @@ PMOD_EXPORT ptrdiff_t multiset_find_ge (struct multiset *l, struct svalue *key)
 		  RBSTACK_FREE (rbstack);
 		  goto done;
 		case FIND_NOROOT:
+		case FIND_KEY_DESTRUCTED:
 		case FIND_GREATER:
 		  node = RBNODE (RBSTACK_PEEK (rbstack));
 		  RBSTACK_FREE (rbstack);
@@ -1684,8 +1731,6 @@ PMOD_EXPORT ptrdiff_t multiset_find_le (struct multiset *l, struct svalue *key)
   debug_malloc_touch (msd);
   check_svalue (key);
 
-  /* FIXME: Handle destructed object in key? */
-
   add_ref (msd);
   SET_ONERROR (uwp, free_indirect_multiset_data, &msd);
 
@@ -1701,6 +1746,7 @@ PMOD_EXPORT ptrdiff_t multiset_find_le (struct multiset *l, struct svalue *key)
       switch (find_type) {
 	case FIND_LESS:
 	case FIND_NOROOT:
+	case FIND_KEY_DESTRUCTED:
 	  goto done;
 	case FIND_GREATER:	/* Got greater - step back one. */
 	  node = INODE (node->i.prev);
@@ -1718,6 +1764,7 @@ PMOD_EXPORT ptrdiff_t multiset_find_le (struct multiset *l, struct svalue *key)
 	      switch (find_type) {
 		case FIND_LESS:
 		case FIND_NOROOT:
+		case FIND_KEY_DESTRUCTED:
 		  node = RBNODE (RBSTACK_PEEK (rbstack));
 		  RBSTACK_FREE (rbstack);
 		  goto done;
@@ -1763,8 +1810,6 @@ PMOD_EXPORT ptrdiff_t multiset_find_gt (struct multiset *l, struct svalue *key)
   debug_malloc_touch (msd);
   check_svalue (key);
 
-  /* FIXME: Handle destructed object in key? */
-
   add_ref (msd);
   SET_ONERROR (uwp, free_indirect_multiset_data, &msd);
 
@@ -1782,6 +1827,7 @@ PMOD_EXPORT ptrdiff_t multiset_find_gt (struct multiset *l, struct svalue *key)
 	  node = INODE (node->i.next);
 	  goto done;
 	case FIND_NOROOT:
+	case FIND_KEY_DESTRUCTED:
 	case FIND_GREATER:
 	  goto done;
 
@@ -1800,6 +1846,7 @@ PMOD_EXPORT ptrdiff_t multiset_find_gt (struct multiset *l, struct svalue *key)
 		  RBSTACK_FREE (rbstack);
 		  goto done;
 		case FIND_NOROOT:
+		case FIND_KEY_DESTRUCTED:
 		case FIND_GREATER:
 		  node = RBNODE (RBSTACK_PEEK (rbstack));
 		  RBSTACK_FREE (rbstack);
@@ -1993,12 +2040,14 @@ static enum find_types low_multiset_track_eq (
 {
   struct rb_node_hdr *node = HDR (msd->root);
   struct rbstack_ptr rbstack = *track;
+  enum find_types find_type;
 
   /* Note: Similar code in multiset_find_eq. */
 
 #ifdef PIKE_DEBUG
   /* Allow zero refs too since that's used during initial building. */
   if (msd->refs == 1) Pike_fatal ("Copy-on-write assumed here.\n");
+  if (!msd->root) Pike_fatal ("Tree assumed to not be empty here.\n");
 #endif
 
   if (msd->cmp_less.type == T_INT) {
@@ -2015,14 +2064,14 @@ static enum find_types low_multiset_track_eq (
 	 * have to copy the index svalues. */
 	INTERNAL_CMP (key, &tmp, cmp_res);
       },
-      {*track = rbstack; return FIND_LESS;},
-      {*track = rbstack; return FIND_EQUAL;},
-      {*track = rbstack; return FIND_GREATER;});
+      {find_type = FIND_LESS; goto done;},
+      {find_type = FIND_EQUAL; goto done;},
+      {find_type = FIND_GREATER; goto done;});
+    /* NOT REACHED */
   }
 
   else {
     /* Find the biggest node less or order-wise equal to key. */
-    enum find_types find_type;
     struct rb_node_hdr *found_node;
     int step_count;
 
@@ -2050,15 +2099,25 @@ static enum find_types low_multiset_track_eq (
 	step_count = 1;
       });
 
+    if (IS_DESTRUCTED (key)) {
+      /* An extra check for a destructed key before entering the loop below,
+       * lest we might go backwards through the whole multiset. */
+      RBSTACK_FREE (rbstack);
+      *track = rbstack;
+      return FIND_KEY_DESTRUCTED;
+    }
+
     /* Step backwards until a less or really equal node is found. */
     while (1) {
-      if (!node) {*track = rbstack; return find_type;}
+      if (!node) goto done;
       low_push_multiset_index (RBNODE (node));
-      if (IS_DESTRUCTED (sp - 1)) {pop_stack(); find_type = FIND_DESTRUCTED; break;}
-      if (is_eq (sp - 1, key)) {pop_stack(); find_type = FIND_EQUAL; break;}
+      if (IS_DESTRUCTED (sp - 1))
+	{pop_stack(); find_type = FIND_DESTRUCTED; break;}
+      if (is_eq (sp - 1, key))
+	{pop_stack(); find_type = FIND_EQUAL; break;}
       push_svalue (key);
       EXTERNAL_CMP (&msd->cmp_less);
-      if (!UNSAFE_IS_ZERO (sp - 1)) {pop_stack(); *track = rbstack; return find_type;}
+      if (!UNSAFE_IS_ZERO (sp - 1)) {pop_stack(); goto done;}
       pop_stack();
       node = rb_prev (node);
       step_count++;
@@ -2069,10 +2128,16 @@ static enum find_types low_multiset_track_eq (
 #ifdef PIKE_DEBUG
     if (node != RBSTACK_PEEK (rbstack)) Pike_fatal ("Stack stepping failed.\n");
 #endif
+  }
 
+done:
+  if (IS_DESTRUCTED (key) && find_type != FIND_DESTRUCTED) {
+    RBSTACK_FREE (rbstack);
     *track = rbstack;
-    return find_type;
+    return FIND_KEY_DESTRUCTED;
   }
+  *track = rbstack;
+  return find_type;
 }
 
 static enum find_types low_multiset_track_le_gt (
@@ -2080,12 +2145,14 @@ static enum find_types low_multiset_track_le_gt (
 {
   struct rb_node_hdr *node = HDR (msd->root);
   struct rbstack_ptr rbstack = *track;
+  enum find_types find_type;
 
   /* Note: Similar code in low_multiset_find_le_gt. */
 
 #ifdef PIKE_DEBUG
   /* Allow zero refs too since that's used during initial building. */
   if (msd->refs == 1) Pike_fatal ("Copy-on-write assumed here.\n");
+  if (!msd->root) Pike_fatal ("Tree assumed to not be empty here.\n");
 #endif
 
   if (msd->cmp_less.type == T_INT) {
@@ -2103,8 +2170,9 @@ static enum find_types low_multiset_track_le_gt (
 	INTERNAL_CMP (key, low_use_multiset_index (RBNODE (node), tmp), cmp_res);
 	cmp_res = cmp_res >= 0 ? 1 : -1;
       },
-      {*track = rbstack; return FIND_LESS;},
-      {*track = rbstack; return FIND_GREATER;});
+      {find_type = FIND_LESS; goto done;},
+      {find_type = FIND_GREATER; goto done;});
+    /* NOT REACHED */
   }
 
   else {
@@ -2122,9 +2190,19 @@ static enum find_types low_multiset_track_le_gt (
 	cmp_res = UNSAFE_IS_ZERO (sp - 1) ? 1 : -1;
 	pop_stack();
       },
-      {*track = rbstack; return FIND_LESS;},
-      {*track = rbstack; return FIND_GREATER;});
+      {find_type = FIND_LESS; goto done;},
+      {find_type = FIND_GREATER; goto done;});
+    /* NOT REACHED */
   }
+
+done:
+  if (IS_DESTRUCTED (key)) {
+    RBSTACK_FREE (rbstack);
+    *track = rbstack;
+    return FIND_KEY_DESTRUCTED;
+  }
+  *track = rbstack;
+  return find_type;
 }
 
 static enum find_types low_multiset_track_lt_ge (
@@ -2132,6 +2210,7 @@ static enum find_types low_multiset_track_lt_ge (
 {
   struct rb_node_hdr *node = HDR (msd->root);
   struct rbstack_ptr rbstack = *track;
+  enum find_types find_type;
 
   /* Note: Similar code in low_multiset_find_lt_ge. */
 
@@ -2156,8 +2235,9 @@ static enum find_types low_multiset_track_lt_ge (
 	INTERNAL_CMP (key, low_use_multiset_index (RBNODE (node), tmp), cmp_res);
 	cmp_res = cmp_res <= 0 ? -1 : 1;
       },
-      {*track = rbstack; return FIND_LESS;},
-      {*track = rbstack; return FIND_GREATER;});
+      {find_type = FIND_LESS; goto done;},
+      {find_type = FIND_GREATER; goto done;});
+    /* NOT REACHED */
   }
 
   else {
@@ -2175,9 +2255,19 @@ static enum find_types low_multiset_track_lt_ge (
 	cmp_res = UNSAFE_IS_ZERO (sp - 1) ? -1 : 1;
 	pop_stack();
       },
-      {*track = rbstack; return FIND_LESS;},
-      {*track = rbstack; return FIND_GREATER;});
+      {find_type = FIND_LESS; goto done;},
+      {find_type = FIND_GREATER; goto done;});
+    /* NOT REACHED */
   }
+
+done:
+  if (IS_DESTRUCTED (key)) {
+    RBSTACK_FREE (rbstack);
+    *track = rbstack;
+    return FIND_KEY_DESTRUCTED;
+  }
+  *track = rbstack;
+  return find_type;
 }
 
 PMOD_EXPORT void multiset_fix_type_field (struct multiset *l)
@@ -2288,7 +2378,8 @@ PMOD_EXPORT void multiset_insert (struct multiset *l,
  * used as value in that case. val is ignored if the multiset has no
  * values. The value of an existing entry will be replaced iff replace
  * is nonzero (done under the assumption the caller has one value
- * lock), otherwise nothing will be done in that case. */
+ * lock), otherwise nothing will be done in that case. Nothing is
+ * done if ind is destructed. */
 PMOD_EXPORT ptrdiff_t multiset_insert_2 (struct multiset *l,
 					 struct svalue *ind,
 					 struct svalue *val,
@@ -2310,12 +2401,14 @@ PMOD_EXPORT ptrdiff_t multiset_insert_2 (struct multiset *l,
   if (val) check_svalue (val);
 #endif
 
-  /* FIXME: Handle destructed object in ind. */
-
   SET_ONERROR (uwp, free_indirect_multiset_data, &msd);
 
   while (1) {
     if (!msd->root) {
+      if (IS_DESTRUCTED (ind)) {
+	UNSET_ONERROR (uwp);
+	return -1;
+      }
       if (prepare_for_add (l, l->node_refs)) msd = l->msd;
       ALLOC_MSNODE (msd, l->node_refs, new);
       find_type = FIND_NOROOT;
@@ -2360,8 +2453,7 @@ PMOD_EXPORT ptrdiff_t multiset_insert_2 (struct multiset *l,
 	  goto insert;
 
 	case FIND_EQUAL: {
-	  struct rb_node_hdr *node;
-	  RBSTACK_POP (rbstack, node);
+	  struct rb_node_hdr *node = RBSTACK_PEEK (rbstack);
 	  RBSTACK_FREE (rbstack);
 	  UNSET_ONERROR (uwp);
 	  sub_extra_ref (msd);
@@ -2391,6 +2483,11 @@ PMOD_EXPORT ptrdiff_t multiset_insert_2 (struct multiset *l,
 	  msd = l->msd;
 	  break;
 
+	case FIND_KEY_DESTRUCTED:
+	  sub_extra_ref (msd);
+	  UNSET_ONERROR (uwp);
+	  return -1;
+
 	default: DO_IF_DEBUG (Pike_fatal ("Invalid find_type.\n"));
       }
   }
@@ -2402,8 +2499,8 @@ insert:
 }
 
 /* val may be zero. If the multiset has values, the integer 1 will be
- * used as value then. val is ignored if the multiset has no
- * values. */
+ * used as value then. val is ignored if the multiset has no values.
+ * Nothing is done if ind is destructed. */
 PMOD_EXPORT ptrdiff_t multiset_add (struct multiset *l,
 				    struct svalue *ind,
 				    struct svalue *val)
@@ -2424,12 +2521,14 @@ PMOD_EXPORT ptrdiff_t multiset_add (struct multiset *l,
   if (val) check_svalue (val);
 #endif
 
-  /* FIXME: Handle destructed object in ind. */
-
   SET_ONERROR (uwp, free_indirect_multiset_data, &msd);
 
   while (1) {
     if (!msd->root) {
+      if (IS_DESTRUCTED (ind)) {
+	UNSET_ONERROR (uwp);
+	return -1;
+      }
       if (prepare_for_add (l, l->node_refs)) msd = l->msd;
       ALLOC_MSNODE (msd, l->node_refs, new);
       find_type = FIND_NOROOT;
@@ -2482,6 +2581,11 @@ PMOD_EXPORT ptrdiff_t multiset_add (struct multiset *l,
 	  msd = l->msd;
 	  break;
 
+	case FIND_KEY_DESTRUCTED:
+	  sub_extra_ref (msd);
+	  UNSET_ONERROR (uwp);
+	  return -1;
+
 	default: DO_IF_DEBUG (Pike_fatal ("Invalid find_type.\n"));
       }
   }
@@ -2512,6 +2616,9 @@ add:
  * node because that would break the order. This is always checked,
  * since it might occur due to concurrent changes of the multiset.
  *
+ * Nothing is done if ind is destructed. -1 is returned in that case
+ * too.
+ *
  * Otherwise the offset of the new node is returned (as usual). */
 PMOD_EXPORT ptrdiff_t multiset_add_after (struct multiset *l,
 					  ptrdiff_t nodepos,
@@ -2538,12 +2645,14 @@ PMOD_EXPORT ptrdiff_t multiset_add_after (struct multiset *l,
   if (nodepos >= 0) check_msnode (l, nodepos, 1);
 #endif
 
-  /* FIXME: Handle destructed object in ind. */
-
   SET_ONERROR (uwp, free_indirect_multiset_data, &msd);
 
   while (1) {
     if (!(node = HDR (msd->root))) {
+      if (IS_DESTRUCTED (ind)) {
+	UNSET_ONERROR (uwp);
+	return -1;
+      }
       if (prepare_for_add (l, l->node_refs)) msd = l->msd;
       ALLOC_MSNODE (msd, l->node_refs, new);
       find_type = FIND_NOROOT;
@@ -2554,13 +2663,22 @@ PMOD_EXPORT ptrdiff_t multiset_add_after (struct multiset *l,
       ONERROR uwp2;
 
     add_node_first:
-      add_ref (msd);
-      add_msnode_ref (l);
       SET_ONERROR (uwp2, do_sub_msnode_ref, l);
 
-      LOW_RB_TRACK_FIRST (rbstack, node);
-      low_use_multiset_index (RBNODE (node), tmp);
-      /* FIXME: Handle destructed object in tmp. */
+      while (1) {
+	add_ref (msd);
+	add_msnode_ref (l);
+
+	LOW_RB_TRACK_FIRST (rbstack, node);
+	low_use_multiset_index (RBNODE (node), tmp);
+	if (!IS_DESTRUCTED (&tmp)) break;
+
+	sub_msnode_ref (l);
+	assert (l->msd == msd);
+	sub_extra_ref (msd);
+	midflight_remove_node_fast (l, &rbstack, 0);
+      }
+
       TEST_LESS (msd, &tmp, ind, cmp_res);
 
       if (l->msd != msd) {
@@ -2603,10 +2721,13 @@ PMOD_EXPORT ptrdiff_t multiset_add_after (struct multiset *l,
       add_ref (msd);
 
       {				/* Compare against the following node. */
-	union msnode *next = low_multiset_next (existing);
-	if (next) {
+	union msnode *next = existing;
+	do {
+	  next = low_multiset_next (next);
+	  if (!next) break;
 	  low_use_multiset_index (next, tmp);
-	  /* FIXME: Handle destructed object in tmp. */
+	} while (IS_DESTRUCTED (&tmp));
+	if (next) {
 	  TEST_LESS (msd, &tmp, ind, cmp_res);
 	  if (l->msd != msd) {
 	    if (!sub_ref (msd)) free_multiset_data (msd);
@@ -2625,11 +2746,19 @@ PMOD_EXPORT ptrdiff_t multiset_add_after (struct multiset *l,
 
       if (l->msd != msd) goto multiset_changed;
 
-      if (find_type == FIND_DESTRUCTED) {
-	sub_extra_ref (msd);
-	midflight_remove_node_fast (l, &rbstack, 0);
-	msd = l->msd;
-	continue;
+      switch (find_type) {
+	case FIND_DESTRUCTED:
+	  sub_extra_ref (msd);
+	  midflight_remove_node_fast (l, &rbstack, 0);
+	  msd = l->msd;
+	  continue;
+
+	case FIND_KEY_DESTRUCTED:
+	  sub_extra_ref (msd);
+	  UNSET_ONERROR (uwp);
+	  return -1;
+
+	default: break;		/* Avoid compiler warning. */
       }
 
       /* Step backwards until the existing node is found, or until
@@ -2638,10 +2767,17 @@ PMOD_EXPORT ptrdiff_t multiset_add_after (struct multiset *l,
       cmp_res = 0;
       while (RBNODE (node) != existing) {
 	low_use_multiset_index (RBNODE (node), tmp);
-	/* FIXME: Handle destructed object in tmp. */
-	TEST_LESS (msd, &tmp, ind, cmp_res);
-	if (cmp_res < 0) break;
-	LOW_RB_TRACK_PREV (rbstack, node);
+	if (IS_DESTRUCTED (&tmp)) {
+	  sub_extra_ref (msd);
+	  midflight_remove_node_fast (l, &rbstack, 1);
+	  msd = l->msd;
+	  add_ref (msd);
+	}
+	else {
+	  TEST_LESS (msd, &tmp, ind, cmp_res);
+	  if (cmp_res < 0) break;
+	  LOW_RB_TRACK_PREV (rbstack, node);
+	}
 	if (!node) {cmp_res = -1; break;}
       }
 
@@ -2712,12 +2848,10 @@ PMOD_EXPORT int multiset_delete_2 (struct multiset *l,
   debug_malloc_touch (msd);
   check_svalue (ind);
 
-  /* FIXME: Handle destructed object in ind. */
-
   SET_ONERROR (uwp, free_indirect_multiset_data, &msd);
 
   while (1) {
-    if (!msd->root) goto not_found;
+    if (!msd->root) goto not_found; /* No difference if ind is destructed. */
 
     add_ref (msd);
     find_type = low_multiset_track_eq (msd, ind, &rbstack);
@@ -2732,6 +2866,7 @@ PMOD_EXPORT int multiset_delete_2 (struct multiset *l,
       switch (find_type) {
 	case FIND_LESS:
 	case FIND_GREATER:
+	case FIND_KEY_DESTRUCTED:
 	  RBSTACK_FREE (rbstack);
 	  sub_extra_ref (msd);
 	  goto not_found;
@@ -2792,7 +2927,7 @@ PMOD_EXPORT void multiset_delete_node (struct multiset *l,
   struct multiset_data *msd = l->msd;
   enum find_types find_type;
   ONERROR uwp;
-  RBSTACK_INIT (rbstack);
+  RBSTACK_INIT (rbstack);	/* FIXME: Is this always freed properly? */
 
   /* Note: Similar code in multiset_insert_2, multiset_add,
    * multiset_add_after and multiset_delete_2. */
@@ -4012,20 +4147,27 @@ struct multiset *copy_multiset_recursively (struct multiset *l,
 	}
 
 	switch (low_multiset_track_le_gt (new.msd,
-					  &new.node->i.ind, /* Not clobbered yet. */
+					  /* Not clobbered yet. */
+					  &new.node->i.ind,
 					  &rbstack)) {
 	  case FIND_LESS:
-	    low_rb_link_at_next (PHDR (&new.msd->root), rbstack, HDR (new.node));
+	    low_rb_link_at_next (PHDR (&new.msd->root),
+				 rbstack, HDR (new.node));
 	    goto node_added;
 	  case FIND_GREATER:
-	    low_rb_link_at_prev (PHDR (&new.msd->root), rbstack, HDR (new.node));
+	    low_rb_link_at_prev (PHDR (&new.msd->root),
+				 rbstack, HDR (new.node));
 	    goto node_added;
 	  case FIND_DESTRUCTED:
 	    midflight_remove_node_faster (new.msd, rbstack);
 	    break;
+	  case FIND_KEY_DESTRUCTED:
+	    RBSTACK_FREE (rbstack);
+	    goto node_skipped;
 	  default: DO_IF_DEBUG (Pike_fatal ("Invalid find_type.\n"));
 	}
       }
+      /* NOT REACHED */
 
     node_added:
 #ifdef PIKE_DEBUG
@@ -4033,6 +4175,8 @@ struct multiset *copy_multiset_recursively (struct multiset *l,
 #endif
       new.msd->size++;
     }
+
+  node_skipped:;
   } while ((node = low_multiset_next (node)));
   new.msd->ind_types = ind_types;
   new.msd->val_types = val_types;
-- 
GitLab