[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