[asterisk-commits] rmudgett: branch rmudgett/ao2_red_black r371659 - /team/rmudgett/ao2_red_blac...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Aug 24 23:25:46 CDT 2012


Author: rmudgett
Date: Fri Aug 24 23:25:43 2012
New Revision: 371659

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=371659
Log:
Node deletion and rbtree deletion fixup.

Modified:
    team/rmudgett/ao2_red_black/main/astobj2.c

Modified: team/rmudgett/ao2_red_black/main/astobj2.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/ao2_red_black/main/astobj2.c?view=diff&rev=371659&r1=371658&r2=371659
==============================================================================
--- team/rmudgett/ao2_red_black/main/astobj2.c (original)
+++ team/rmudgett/ao2_red_black/main/astobj2.c Fri Aug 24 23:25:43 2012
@@ -1943,6 +1943,9 @@
  * of its destruction.  The node must be destroyed while the
  * container is already locked.
  *
+ * \note The container must be locked when the node is
+ * unreferenced.
+ *
  * \return Nothing
  */
 static void hash_ao2_node_destructor(void *v_doomed)
@@ -1982,7 +1985,7 @@
 	 * destroyed or the node had not been linked in yet.
 	 */
 	if (doomed->common.obj) {
-		ao2_ref(doomed->common.obj, -1);
+		__ao2_ref(doomed->common.obj, -1);
 		doomed->common.obj = NULL;
 	}
 }
@@ -3067,6 +3070,9 @@
 	struct ao2_container common;
 	/*! Root node of the tree.  NULL if the tree is empty. */
 	struct rbtree_node *root;
+/* BUGBUG need to add stats counters for fixup insert left/right cases 1-3 */
+/* BUGBUG need to add stats counters for fixup delete left/right cases 1-4 */
+/* BUGBUG need to add stats counter for delete in middle of tree. */
 };
 
 /*!
@@ -3531,6 +3537,210 @@
 
 /*!
  * \internal
+ * \brief Fixup the rbtree after deleting a node.
+ * \since 12.0.0
+ *
+ * \param self Container to operate upon.
+ * \param child Child of the node just deleted from the container.
+ *
+ * \note The child must be a dummy black node if there really
+ * was no child of the deleted node.  Otherwise, the caller must
+ * pass in the parent node and which child was deleted.  In
+ * addition, the fixup routine would be more complicated.
+ *
+ * \return Nothing
+ */
+static void rb_delete_fixup(struct ao2_container_rbtree *self, struct rbtree_node *child)
+{
+	struct rbtree_node *sibling;
+
+	while (self->root != child && !child->is_red) {
+		if (child->parent->left == child) {
+			/* Child is a left child. */
+			sibling = child->parent->right;
+			ast_assert(sibling != NULL);
+			if (sibling->is_red) {
+				/* Case 1: The child's sibling is red. */
+				sibling->is_red = 0;
+				child->parent->is_red = 1;
+				rb_rotate_left(self, child->parent);
+				sibling = child->parent->right;
+				ast_assert(sibling != NULL);
+			}
+			if ((!sibling->left || !sibling->left->is_red)
+				&& (!sibling->right || !sibling->right->is_red)) {
+				/* Case 2: The sibling is black and both of its children are black. */
+				sibling->is_red = 1;
+				child = child->parent;
+			} else {
+				if (!sibling->right || !sibling->right->is_red) {
+					/* Case 3: The sibling is black, its left child is red, and its right child is black. */
+					if (sibling->left) {
+						sibling->left->is_red = 0;
+					}
+					sibling->is_red = 1;
+					rb_rotate_right(self, sibling);
+					sibling = child->parent->right;
+					ast_assert(sibling != NULL);
+				}
+				/* Case 4: The sibling is black and its right child is red. */
+				sibling->is_red = child->parent->is_red;
+				child->parent->is_red = 0;
+				if (sibling->right) {
+					sibling->is_red = 0;
+				}
+				rb_rotate_left(self, child->parent);
+				child = self->root;
+			}
+		} else {
+			/* Child is a right child. */
+			sibling = child->parent->left;
+			ast_assert(sibling != NULL);
+			if (sibling->is_red) {
+				/* Case 1: The child's sibling is red. */
+				sibling->is_red = 0;
+				child->parent->is_red = 1;
+				rb_rotate_right(self, child->parent);
+				sibling = child->parent->left;
+				ast_assert(sibling != NULL);
+			}
+			if ((!sibling->right || !sibling->right->is_red)
+				&& (!sibling->left || !sibling->left->is_red)) {
+				/* Case 2: The sibling is black and both of its children are black. */
+				sibling->is_red = 1;
+				child = child->parent;
+			} else {
+				if (!sibling->left || !sibling->left->is_red) {
+					/* Case 3: The sibling is black, its right child is red, and its left child is black. */
+					if (sibling->right) {
+						sibling->right->is_red = 0;
+					}
+					sibling->is_red = 1;
+					rb_rotate_left(self, sibling);
+					sibling = child->parent->left;
+					ast_assert(sibling != NULL);
+				}
+				/* Case 4: The sibling is black and its left child is red. */
+				sibling->is_red = child->parent->is_red;
+				child->parent->is_red = 0;
+				if (sibling->left) {
+					sibling->is_red = 0;
+				}
+				rb_rotate_right(self, child->parent);
+				child = self->root;
+			}
+		}
+	}
+	child->is_red = 0;
+
+	/*! \todo BUGBUG rb_delete_fixup() not written */
+}
+
+/*!
+ * \internal
+ * \brief Delete the doomed node from this container.
+ * \since 12.0.0
+ *
+ * \param self Container to operate upon.
+ * \param doomed Container node to delete from the container.
+ *
+ * \return Nothing
+ */
+static void rb_delete_node(struct ao2_container_rbtree *self, struct rbtree_node *doomed)
+{
+	struct rbtree_node *child;
+	int need_fixup;
+
+	if (doomed->left && doomed->right) {
+		struct rbtree_node *next;
+		int is_red;
+
+		/*
+		 * The doomed node has two children.
+		 *
+		 * Find the next child node and swap it with the doomed node in
+		 * the tree.
+		 */
+		next = rb_node_most_left(doomed->right);
+		SWAP(doomed->parent, next->parent);
+		SWAP(doomed->left, next->left);
+		SWAP(doomed->right, next->right);
+		is_red = doomed->is_red;
+		doomed->is_red = next->is_red;
+		next->is_red = is_red;
+
+		/* Link back in the next node. */
+		if (!next->parent) {
+			/* Doomed was the root so we get a new root node. */
+			self->root = next;
+		} else if (next->parent->left == doomed) {
+			/* Doomed was the left child. */
+			next->parent->left = next;
+		} else {
+			/* Doomed was the right child. */
+			next->parent->right = next;
+		}
+		next->left->parent = next;
+		next->right->parent = next;
+
+		/* Link back in the doomed node.  (It has no left child) */
+		if (doomed->parent->left == next) {
+			/* The next node was not the right child of doomed. */
+			doomed->parent->left = doomed;
+		}
+
+		/*
+		 * We don't have to link the right child back in with doomed
+		 * since we are going to link it with doomed's parent anyway.
+		 */
+		child = doomed->right;
+	} else {
+		/* Doomed has at most one child. */
+		child = doomed->left;
+		if (!child) {
+			child = doomed->right;
+		}
+	}
+
+	need_fixup = (!doomed->is_red && !self->common.destroying);
+	if (need_fixup && !child) {
+		/*
+		 * Use the doomed node as a place holder node for the
+		 * nonexistent child so we also don't have to pass to the fixup
+		 * routine the parent and which child the deleted node came
+		 * from.
+		 */
+		rb_delete_fixup(self, doomed);
+		ast_assert(doomed->left == NULL);
+		ast_assert(doomed->right == NULL);
+		ast_assert(!doomed->is_red);
+	}
+
+	/* Link the child in place of doomed. */
+	if (!doomed->parent) {
+		/* Doomed was the root so we get a new root node. */
+		self->root = child;
+	} else if (doomed->parent->left == doomed) {
+		/* Doomed was the left child. */
+		doomed->parent->left = child;
+	} else {
+		/* Doomed was the right child. */
+		doomed->parent->right = child;
+	}
+	if (child) {
+		child->parent = doomed->parent;
+		if (need_fixup) {
+			rb_delete_fixup(self, child);
+		}
+	}
+
+#if defined(AST_DEVMODE)
+	--self->common.nodes;
+#endif	/* defined(AST_DEVMODE) */
+}
+
+/*!
+ * \internal
  * \brief Destroy a rbtree container node.
  * \since 12.0.0
  *
@@ -3541,13 +3751,14 @@
  * of its destruction.  The node must be destroyed while the
  * container is already locked.
  *
+ * \note The container must be locked when the node is
+ * unreferenced.
+ *
  * \return Nothing
  */
 static void rb_ao2_node_destructor(void *v_doomed)
 {
 	struct rbtree_node *doomed = v_doomed;
-
-/* BUGBUG rb_ao2_node_destructor not written */
 
 	if (doomed->common.is_linked) {
 		struct ao2_container_rbtree *my_container;
@@ -3568,12 +3779,7 @@
 		my_container = (struct ao2_container_rbtree *) doomed->common.my_container;
 		adjust_lock(my_container, AO2_LOCK_REQ_WRLOCK, 1);
 
-/* BUGBUG remove the node from the tree and rebalance if the container is not being destroyed. */
-/* BUGBUG rb_delete_fixup not written */
-
-#if defined(AST_DEVMODE)
-		--my_container->common.nodes;
-#endif	/* defined(AST_DEVMODE) */
+		rb_delete_node(my_container, doomed);
 	}
 
 	/*
@@ -3581,7 +3787,7 @@
 	 * destroyed or the node had not been linked in yet.
 	 */
 	if (doomed->common.obj) {
-		ao2_ref(doomed->common.obj, -1);
+		__ao2_ref(doomed->common.obj, -1);
 		doomed->common.obj = NULL;
 	}
 }




More information about the asterisk-commits mailing list