Add assertions in fixup
[userspace-rcu.git] / urcu-rbtree.c
index e1df343e9cd058f2ee9cc2a8b869918c009ee585..ac55fd8768c74d180f65e7666cd533ddb1928e92 100644 (file)
 #include <urcu-rbtree.h>
 #include <urcu-pointer.h>
 
+#define DEBUG
+
+#ifdef DEBUG
+#define dbg_printf(args...)    printf(args)
+#else
+#define dbg_printf(args...)
+#endif
+
 /*
  * TODO
  * Deal with memory allocation errors.
@@ -45,7 +53,8 @@
  * transplantations/rotations required for the operation.
  */
 
-/* Sentinel (bottom nodes). Don't care about p, left, right and key values */
+/* Sentinel (bottom nodes).
+ * Don't care about p, left, right, pos and key values */
 struct rcu_rbtree_node rcu_rbtree_nil = {
        .color = COLOR_BLACK,
 };
@@ -91,6 +100,13 @@ struct rcu_rbtree_node *rcu_rbtree_max(struct rcu_rbtree_node *x,
        return x;
 }
 
+/*
+ * FIXME:
+ * Updates should wait for a grace period between update of the
+ * redirect pointer and update of the parent child pointer. This is to make sure
+ * that no reference to the old entry exist.
+ */
+
 /*
  * next and prev need to have mutex held to ensure that parent pointer is
  * coherent.
@@ -98,20 +114,16 @@ struct rcu_rbtree_node *rcu_rbtree_max(struct rcu_rbtree_node *x,
 struct rcu_rbtree_node *rcu_rbtree_next(struct rcu_rbtree_node *x,
                                        rcu_rbtree_comp comp)
 {
-       struct rcu_rbtree_node *xr, *y, *yredir;
+       struct rcu_rbtree_node *xr, *y, *yr;
 
        x = rcu_dereference(x);
 
        if ((xr = rcu_dereference(x->right)) != &rcu_rbtree_nil)
                return rcu_rbtree_min(xr, comp);
        y = rcu_dereference(x->p);
-       while ((yredir = rcu_dereference(y->redir)) != NULL)
-               y = yredir;
-       while (y != &rcu_rbtree_nil && x == rcu_dereference(y->right)) {
+       while (y != &rcu_rbtree_nil && x->pos == IS_RIGHT) {
                x = y;
                y = rcu_dereference(y->p);
-               while ((yredir = rcu_dereference(y->redir)) != NULL)
-                       y = yredir;
        }
        return y;
 }
@@ -119,20 +131,16 @@ struct rcu_rbtree_node *rcu_rbtree_next(struct rcu_rbtree_node *x,
 struct rcu_rbtree_node *rcu_rbtree_prev(struct rcu_rbtree_node *x,
                                        rcu_rbtree_comp comp)
 {
-       struct rcu_rbtree_node *xl, *y, *yredir;
+       struct rcu_rbtree_node *xl, *y, *yl;
 
        x = rcu_dereference(x);
 
        if ((xl = rcu_dereference(x->left)) != &rcu_rbtree_nil)
-               return rcu_rbtree_max(xl, comp);
+               return rcu_rbtree_min(xl, comp);
        y = rcu_dereference(x->p);
-       while ((yredir = rcu_dereference(y->redir)) != NULL)
-               y = yredir;
-       while (y != &rcu_rbtree_nil && x == rcu_dereference(y->left)) {
+       while (y != &rcu_rbtree_nil && x->pos == IS_LEFT) {
                x = y;
                y = rcu_dereference(y->p);
-               while ((yredir = rcu_dereference(y->redir)) != NULL)
-                       y = yredir;
        }
        return y;
 }
@@ -156,29 +164,46 @@ struct rcu_rbtree_node *rcu_rbtree_prev(struct rcu_rbtree_node *x,
 
 /* RCU: copy x and y, atomically point to new versions. GC old. */
 /* Should be eventually followed by a smp_wmc() */
-/* Returns the new x. Previous x->right references are changed to yc. */
+/* Returns the new x. Previous x->right references are changed to yc.
+ * Previous y->left->right is changed to bc. */
 static struct rcu_rbtree_node *left_rotate(struct rcu_rbtree_node **root,
                                           struct rcu_rbtree_node *x,
                                           rcu_rbtree_alloc rballoc,
                                           rcu_rbtree_free rbfree)
 {
-       struct rcu_rbtree_node *xc, *y, *yc;
+       struct rcu_rbtree_node *xc, *y, *yc, *b, *bc;
+
+       dbg_printf("left rotate %p\n", x->key);
 
        y = x->right;
 
        if (x != &rcu_rbtree_nil) {
                xc = rballoc();
                *xc = *x;
+               xc->pos = IS_LEFT;
        }
 
        if (y != &rcu_rbtree_nil) {
                yc = rballoc();
                *yc = *y;
+               yc->pos = x->pos;
+               b = y->left;
+       } else {
+               b = &rcu_rbtree_nil;
        }
 
+       if (b != &rcu_rbtree_nil) {
+               /* Beta position changes from left to right. */
+               bc = rballoc();
+               *bc = *b;
+               assert(b->pos == IS_LEFT);
+               bc->pos = IS_RIGHT;
+       } else
+               bc = &rcu_rbtree_nil;
+
        /* Modify children and parents in the node copies */
        if (x != &rcu_rbtree_nil) {
-               xc->right = y->left;
+               xc->right = bc;
                xc->p = yc;
        } else
                xc = &rcu_rbtree_nil;
@@ -195,37 +220,29 @@ static struct rcu_rbtree_node *left_rotate(struct rcu_rbtree_node **root,
         */
        smp_wmb();
 
-       /*
-        * redirect old nodes to new.
-        */
-       x->redir = xc;
-       y->redir = yc;
-
-       /*
-        * Ensure that redirections are visible before updating external
-        * pointers.
-        */
-       smp_wmb();
-
        /* Make parents point to the copies */
        if (x->p == &rcu_rbtree_nil)
                _STORE_SHARED(*root, yc);
-       else if (x == x->p->left)
+       else if (x->pos == IS_LEFT)
                _STORE_SHARED(x->p->left, yc);
        else
                _STORE_SHARED(x->p->right, yc);
 
        /* Assign children parents to copies */
        if (x != &rcu_rbtree_nil) {
-               _STORE_SHARED(xc->right->p, xc);
-               _STORE_SHARED(xc->left->p, xc);
+               _STORE_SHARED(xc->left->p, xc); /* alpha stays left */
                defer_rcu(rbfree, x);
        }
        if (y != &rcu_rbtree_nil) {
-               _STORE_SHARED(yc->right->p, yc);
+               _STORE_SHARED(yc->right->p, yc);/* gamma stays right */
                defer_rcu(rbfree, y);
                /* yc->left is xc, its parent is already set in node copy */
        }
+       if (b != &rcu_rbtree_nil) {
+               _STORE_SHARED(bc->right->p, bc);
+               _STORE_SHARED(bc->left->p, bc);
+               defer_rcu(rbfree, b);
+       }
        return xc;
 }
 
@@ -254,36 +271,53 @@ static void left_rotate(struct rcu_rbtree_node **root,
 
 /* RCU: copy x and y, atomically point to new versions. GC old. */
 /* Should be eventually followed by a smp_wmc() */
-/* Returns the new x. Previous x->left references are changed to yc. */
+/* Returns the new y. Previous y->left references are changed to xc.
+ * Previous y->left->right is changed to bc */
 static struct rcu_rbtree_node *right_rotate(struct rcu_rbtree_node **root,
-                                           struct rcu_rbtree_node *x,
+                                           struct rcu_rbtree_node *y,
                                            rcu_rbtree_alloc rballoc,
                                            rcu_rbtree_free rbfree)
 {
-       struct rcu_rbtree_node *xc, *y, *yc;
+       struct rcu_rbtree_node *x, *xc, *yc, *b, *bc;;
+
+       dbg_printf("right rotate %p\n", y->key);
 
-       y = x->left;
+       x = y->left;
 
        if (x != &rcu_rbtree_nil) {
                xc = rballoc();
                *xc = *x;
+               xc->pos = y->pos;
+               b = x->right;
+       } else {
+               b = &rcu_rbtree_nil;
        }
 
        if (y != &rcu_rbtree_nil) {
                yc = rballoc();
                *yc = *y;
+               yc->pos = IS_RIGHT;
        }
 
+       if (b != &rcu_rbtree_nil) {
+               /* Beta position changes from right to left. */
+               bc = rballoc();
+               *bc = *b;
+               assert(b->pos == IS_RIGHT);
+               bc->pos = IS_LEFT;
+       } else
+               bc = &rcu_rbtree_nil;
+
        /* Modify children and parents in the node copies */
        if (x != &rcu_rbtree_nil) {
-               xc->left = y->right;
-               xc->p = yc;
+               xc->right = yc;
+               xc->p = y->p;
        } else
                xc = &rcu_rbtree_nil;
 
        if (y != &rcu_rbtree_nil) {
-               yc->right = xc;
-               yc->p = x->p;
+               yc->left = bc;
+               yc->p = xc;
        } else
                yc = &rcu_rbtree_nil;
 
@@ -293,62 +327,31 @@ static struct rcu_rbtree_node *right_rotate(struct rcu_rbtree_node **root,
         */
        smp_wmb();
 
-       /*
-        * redirect old nodes to new.
-        */
-       x->redir = xc;
-       y->redir = yc;
-
-       /*
-        * Ensure that redirections are visible before updating external
-        * pointers.
-        */
-       smp_wmb();
-
        /* Make parents point to the copies */
-       if (x->p == &rcu_rbtree_nil)
-               _STORE_SHARED(*root, yc);
-       else if (x == x->p->right)
-               _STORE_SHARED(x->p->right, yc);
+       if (y->p == &rcu_rbtree_nil)
+               _STORE_SHARED(*root, xc);
+       else if (y->pos == IS_RIGHT)
+               _STORE_SHARED(y->p->right, xc);
        else
-               _STORE_SHARED(x->p->left, yc);
+               _STORE_SHARED(y->p->left, xc);
 
        /* Assign children parents to copies */
        if (x != &rcu_rbtree_nil) {
-               _STORE_SHARED(xc->left->p, xc);
-               _STORE_SHARED(xc->right->p, xc);
+               _STORE_SHARED(xc->left->p, xc); /* alpha stays left */
+               /* xc->right is yc, its parent is already set in node copy */
                defer_rcu(rbfree, x);
        }
        if (y != &rcu_rbtree_nil) {
-               _STORE_SHARED(yc->left->p, yc);
+               _STORE_SHARED(yc->right->p, yc);/* gamma stays right */
                defer_rcu(rbfree, y);
-               /* yc->right is xc, its parent is already set in node copy */
        }
-       return xc;
-}
-
-#if 0 //orig
-static void right_rotate(struct rcu_rbtree_node **root,
-                        struct rcu_rbtree_node *x,
-                        rcu_rbtree_alloc rballoc)
-{
-       struct rcu_rbtree_node *y;
-
-       y = x->left;
-       x->left = y->right;
-       if (y->right != &rcu_rbtree_nil)
-               y->right->p = x;
-       y->p = x->p;
-       if (x->p == &rcu_rbtree_nil)
-               *root = y;
-       else if (x == x->p->right)
-               x->p->right = y;
-       else
-               x->p->left = y;
-       y->right = x;
-       x->p = y;
+       if (b != &rcu_rbtree_nil) {
+               _STORE_SHARED(bc->right->p, bc);
+               _STORE_SHARED(bc->left->p, bc);
+               defer_rcu(rbfree, b);
+       }
+       return yc;
 }
-#endif //0
 
 static void rcu_rbtree_insert_fixup(struct rcu_rbtree_node **root,
                                    struct rcu_rbtree_node *z,
@@ -357,6 +360,8 @@ static void rcu_rbtree_insert_fixup(struct rcu_rbtree_node **root,
 {
        struct rcu_rbtree_node *y;
 
+       dbg_printf("insert fixup %p\n", z->key);
+
        while (z->p->color == COLOR_RED) {
                if (z->p == z->p->p->left) {
                        y = z->p->p->right;
@@ -410,6 +415,8 @@ int rcu_rbtree_insert(struct rcu_rbtree_node **root,
 {
        struct rcu_rbtree_node *x, *y;
 
+       dbg_printf("insert %p\n", z->key);
+
        y = &rcu_rbtree_nil;
        x = *root;
 
@@ -425,7 +432,13 @@ int rcu_rbtree_insert(struct rcu_rbtree_node **root,
        z->left = &rcu_rbtree_nil;
        z->right = &rcu_rbtree_nil;
        z->color = COLOR_RED;
-       z->redir = NULL;
+
+       if (y == &rcu_rbtree_nil)
+               z->pos = IS_RIGHT;      /* arbitrary for root node */
+       else if (comp(z->key, y->key) < 0)
+               z->pos = IS_LEFT;
+       else
+               z->pos = IS_RIGHT;
 
        /*
         * Order stores to z (children/parents) before stores that will make it
@@ -461,29 +474,21 @@ rcu_rbtree_transplant(struct rcu_rbtree_node **root,
 {
        struct rcu_rbtree_node *vc;
 
+       dbg_printf("transplant %p\n", v->key);
+
        if (v != &rcu_rbtree_nil) {
                vc = rballoc();
                *vc = *v;
 
                /* Change vc parent pointer */
                vc->p = u->p;
+               vc->pos = u->pos;
 
                /*
                 * Order stores to node copies (children/parents) before stores
                 * that will make the copies visible to the rest of the tree.
                 */
                smp_wmb();
-
-               /*
-                * redirect old node to new.
-                */
-               v->redir = vc;
-
-               /*
-                * Ensure that redirections are visible before updating external
-                * pointers.
-                */
-               smp_wmb();
        } else {
                vc = &rcu_rbtree_nil;
        }
@@ -501,8 +506,10 @@ rcu_rbtree_transplant(struct rcu_rbtree_node **root,
                 * The children pointers in vc are the same as v. We can
                 * therefore reparent v's children to vc safely.
                 */
-               _STORE_SHARED(vc->right->p, vc);
-               _STORE_SHARED(vc->left->p, vc);
+               if (vc->right != &rcu_rbtree_nil)
+                       _STORE_SHARED(vc->right->p, vc);
+               if (vc->left != &rcu_rbtree_nil)
+                       _STORE_SHARED(vc->left->p, vc);
 
                defer_rcu(rbfree, v);
        }
@@ -514,11 +521,15 @@ static void rcu_rbtree_remove_fixup(struct rcu_rbtree_node **root,
                                    rcu_rbtree_alloc rballoc,
                                    rcu_rbtree_free rbfree)
 {
+       dbg_printf("remove fixup %p\n", x->key);
+
        while (x != *root && x->color == COLOR_BLACK) {
                if (x == x->p->left) {
                        struct rcu_rbtree_node *w, *t;
 
                        w = x->p->right;
+                       assert(w != &rcu_rbtree_nil);
+
                        if (w->color == COLOR_RED) {
                                w->color == COLOR_BLACK;
                                x->p->color = COLOR_RED;
@@ -545,13 +556,16 @@ static void rcu_rbtree_remove_fixup(struct rcu_rbtree_node **root,
                                x = *root;
                        }
                } else {
-                       struct rcu_rbtree_node *w;
+                       struct rcu_rbtree_node *w, *t;
 
                        w = x->p->left;
+                       assert(w != &rcu_rbtree_nil);
+
                        if (w->color == COLOR_RED) {
                                w->color == COLOR_BLACK;
                                x->p->color = COLOR_RED;
-                               right_rotate(root, x->p, rballoc, rbfree);
+                               t = right_rotate(root, x->p, rballoc, rbfree);
+                               assert(x->p->right == t->right);
                                /* x is a right node, not copied by rotation */
                                w = x->p->left;
                        }
@@ -577,7 +591,9 @@ static void rcu_rbtree_remove_fixup(struct rcu_rbtree_node **root,
        x->color = COLOR_BLACK;
 }
 
-/* Returns the new copy of y->right */
+/*
+ * Returns the new copy of y->right.
+ * Delete z. All non-copied children left/right positions are unchanged. */
 static struct rcu_rbtree_node *
 rcu_rbtree_remove_nonil(struct rcu_rbtree_node **root,
                        struct rcu_rbtree_node *z,
@@ -588,6 +604,8 @@ rcu_rbtree_remove_nonil(struct rcu_rbtree_node **root,
 {
        struct rcu_rbtree_node *x, *xc, *yc;
 
+       dbg_printf("remove nonil %p\n", z->key);
+
        x = y->right;
 
        if (x != &rcu_rbtree_nil) {
@@ -620,19 +638,6 @@ rcu_rbtree_remove_nonil(struct rcu_rbtree_node **root,
         */
        smp_wmb();
 
-       /*
-        * redirect old nodes to new.
-        */
-       if (x != &rcu_rbtree_nil)
-               x->redir = xc;
-       y->redir = yc;
-
-       /*
-        * Ensure that redirections are visible before updating external
-        * pointers.
-        */
-       smp_wmb();
-
        /* Update external pointers */
 
        if (y->p != z) {
@@ -658,13 +663,19 @@ rcu_rbtree_remove_nonil(struct rcu_rbtree_node **root,
        if (x != &rcu_rbtree_nil) {
                /* Reparent xc's children to xc. */
                _STORE_SHARED(xc->right->p, xc);
+               assert(yc->right == &rcu_rbtree_nil
+                      || xc->right->pos == IS_RIGHT);
                _STORE_SHARED(xc->left->p, xc);
+               assert(yc->right == &rcu_rbtree_nil
+                      || xc->left->pos == IS_LEFT);
                defer_rcu(rbfree, x);
        }
 
        /* Reparent yc's children to yc */
        _STORE_SHARED(yc->right->p, yc);
+       assert(yc->right == &rcu_rbtree_nil || yc->right->pos == IS_RIGHT);
        _STORE_SHARED(yc->left->p, yc);
+       assert(yc->right == &rcu_rbtree_nil || yc->left->pos == IS_LEFT);
        defer_rcu(rbfree, y);
 
        return xc;
@@ -679,6 +690,8 @@ int rcu_rbtree_remove(struct rcu_rbtree_node **root,
        struct rcu_rbtree_node *x, *y;
        unsigned int y_original_color;
 
+       dbg_printf("remove %p\n", z->key);
+
        y = z;
        y_original_color = y->color;
 
This page took 0.030275 seconds and 4 git commands to generate.