We need to atomically make the parent/child relationship visible atomically to
the rest of the world. Add redirections in the old nodes to the new nodes which
atomically override the old nodes from the prev/next walk point of view.
Note that it does not affect search, because these do not need to consider the
parents.
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
#include <stdio.h>
#include <pthread.h>
#include <stdio.h>
#include <pthread.h>
#include <urcu-rbtree.h>
#include <urcu-pointer.h>
#include <urcu-rbtree.h>
#include <urcu-pointer.h>
struct rcu_rbtree_node *rcu_rbtree_next(struct rcu_rbtree_node *x,
rcu_rbtree_comp comp)
{
struct rcu_rbtree_node *rcu_rbtree_next(struct rcu_rbtree_node *x,
rcu_rbtree_comp comp)
{
- struct rcu_rbtree_node *xr, *y;
+ struct rcu_rbtree_node *xr, *y, *yredir;
x = rcu_dereference(x);
if ((xr = rcu_dereference(x->right)) != &rcu_rbtree_nil)
return rcu_rbtree_min(xr, comp);
y = rcu_dereference(x->p);
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)) {
x = y;
y = rcu_dereference(y->p);
while (y != &rcu_rbtree_nil && x == rcu_dereference(y->right)) {
x = y;
y = rcu_dereference(y->p);
+ while ((yredir = rcu_dereference(y->redir)) != NULL)
+ y = yredir;
struct rcu_rbtree_node *rcu_rbtree_prev(struct rcu_rbtree_node *x,
rcu_rbtree_comp comp)
{
struct rcu_rbtree_node *rcu_rbtree_prev(struct rcu_rbtree_node *x,
rcu_rbtree_comp comp)
{
- struct rcu_rbtree_node *xl, *y;
+ struct rcu_rbtree_node *xl, *y, *yredir;
x = rcu_dereference(x);
if ((xl = rcu_dereference(x->left)) != &rcu_rbtree_nil)
return rcu_rbtree_max(xl, comp);
y = rcu_dereference(x->p);
x = rcu_dereference(x);
if ((xl = rcu_dereference(x->left)) != &rcu_rbtree_nil)
return rcu_rbtree_max(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)) {
x = y;
y = rcu_dereference(y->p);
while (y != &rcu_rbtree_nil && x == rcu_dereference(y->left)) {
x = y;
y = rcu_dereference(y->p);
+ while ((yredir = rcu_dereference(y->redir)) != NULL)
+ y = yredir;
+ /*
+ * 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);
/* Make parents point to the copies */
if (x->p == &rcu_rbtree_nil)
_STORE_SHARED(*root, yc);
+ /*
+ * 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);
/* Make parents point to the copies */
if (x->p == &rcu_rbtree_nil)
_STORE_SHARED(*root, yc);
z->left = &rcu_rbtree_nil;
z->right = &rcu_rbtree_nil;
z->color = COLOR_RED;
z->left = &rcu_rbtree_nil;
z->right = &rcu_rbtree_nil;
z->color = COLOR_RED;
/*
* Order stores to z (children/parents) before stores that will make it
/*
* Order stores to z (children/parents) before stores that will make it
* that will make the copies visible to the rest of the tree.
*/
smp_wmb();
* 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;
}
} else {
vc = &rcu_rbtree_nil;
}
{
while (x != *root && x->color == COLOR_BLACK) {
if (x == x->p->left) {
{
while (x != *root && x->color == COLOR_BLACK) {
if (x == x->p->left) {
- struct rcu_rbtree_node *w;
+ struct rcu_rbtree_node *w, *t;
w = x->p->right;
if (w->color == COLOR_RED) {
w->color == COLOR_BLACK;
x->p->color = COLOR_RED;
w = x->p->right;
if (w->color == COLOR_RED) {
w->color == COLOR_BLACK;
x->p->color = COLOR_RED;
- left_rotate(root, x->p, rballoc, rbfree);
+ t = left_rotate(root, x->p, rballoc, rbfree);
+ assert(x->p->left == t->left);
/* x is a left node, not copied by rotation */
w = x->p->right;
}
/* x is a left node, not copied by rotation */
w = x->p->right;
}
+ /*
+ * 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) {
/* Update external pointers */
if (y->p != z) {
void *key;
/* internally reserved */
void *key;
/* internally reserved */
- struct rcu_rbtree_node *p, *left, *right;
+ struct rcu_rbtree_node *p, *left, *right, *redir;
- * Search key starting from node x. Returns NULL if not found.
+ * Search key starting from node x. Returns &rcu_rbtree_nil if not found.
*/
struct rcu_rbtree_node* rcu_rbtree_search(struct rcu_rbtree_node *x,
void *key, rcu_rbtree_comp comp);
*/
struct rcu_rbtree_node* rcu_rbtree_search(struct rcu_rbtree_node *x,
void *key, rcu_rbtree_comp comp);