rcuja: add duplicate at end of list
[userspace-rcu.git] / rcuja / rcuja.c
index fb3e9b11cdca1e0155bbd1cc9a2e51eaae3adb18..a16385b06f0f923dc5e2c603a7d3948057411d09 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Userspace RCU library - RCU Judy Array
  *
- * Copyright 2012 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright (C) 2000 - 2002 Hewlett-Packard Company
+ * Copyright 2012-2013 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
 #include <errno.h>
 #include <limits.h>
 #include <string.h>
+#include <assert.h>
 #include <urcu/rcuja.h>
 #include <urcu/compiler.h>
 #include <urcu/arch.h>
-#include <assert.h>
 #include <urcu-pointer.h>
 #include <urcu/uatomic.h>
-#include <stdint.h>
 
 #include "rcuja-internal.h"
 
@@ -370,7 +370,7 @@ struct cds_ja_inode_flag *ja_linear_node_get_direction(const struct cds_ja_type
        uint8_t nr_child;
        uint8_t *values;
        struct cds_ja_inode_flag **pointers;
-       struct cds_ja_inode_flag *ptr;
+       struct cds_ja_inode_flag *ptr = NULL;
        unsigned int i;
        int match_idx = -1, match_v;
 
@@ -389,10 +389,14 @@ struct cds_ja_inode_flag *ja_linear_node_get_direction(const struct cds_ja_type
        assert(type->type_class != RCU_JA_LINEAR || nr_child >= type->min_child);
 
        values = &node->u.data[1];
+       pointers = (struct cds_ja_inode_flag **) align_ptr_size(&values[type->max_linear_child]);
        for (i = 0; i < nr_child; i++) {
                unsigned int v;
 
                v = CMM_LOAD_SHARED(values[i]);
+               ptr = CMM_LOAD_SHARED(pointers[i]);
+               if (!ptr)
+                       continue;
                if (dir == JA_LEFT) {
                        if ((int) v < n && (int) v > match_v) {
                                match_v = v;
@@ -412,7 +416,6 @@ struct cds_ja_inode_flag *ja_linear_node_get_direction(const struct cds_ja_type
        assert(match_v >= 0 && match_v < JA_ENTRY_PER_NODE);
 
        *result_key = (uint8_t) match_v;
-       pointers = (struct cds_ja_inode_flag **) align_ptr_size(&values[type->max_linear_child]);
        ptr = rcu_dereference(pointers[match_idx]);
        return ptr;
 }
@@ -1883,6 +1886,9 @@ struct cds_ja_node *cds_ja_lookup_inequality(struct cds_ja *ja, uint64_t key,
                iter_key = (uint8_t) (key >> (JA_BITS_PER_BYTE * (tree_depth - level - 1)));
                node_flag = ja_node_get_leftright(cur_node_depth[level - 1],
                                iter_key, &cur_key[level - 1], dir);
+               dbg_printf("cds_ja_lookup_inequality find sibling from %u at %u finds node_flag %p\n",
+                               (unsigned int) iter_key, (unsigned int) cur_key[level - 1],
+                               node_flag);
                /* If found left/right sibling, find rightmost/leftmost child. */
                if (ja_node_ptr(node_flag))
                        break;
@@ -1918,6 +1924,9 @@ struct cds_ja_node *cds_ja_lookup_inequality(struct cds_ja *ja, uint64_t key,
        }
        for (; level < tree_depth; level++) {
                node_flag = ja_node_get_minmax(node_flag, &cur_key[level - 1], dir);
+               dbg_printf("cds_ja_lookup_inequality find minmax at %u finds node_flag %p\n",
+                               (unsigned int) cur_key[level - 1],
+                               node_flag);
                if (!ja_node_ptr(node_flag))
                        break;
        }
@@ -1937,12 +1946,14 @@ struct cds_ja_node *cds_ja_lookup_inequality(struct cds_ja *ja, uint64_t key,
 struct cds_ja_node *cds_ja_lookup_below_equal(struct cds_ja *ja,
                uint64_t key, uint64_t *result_key)
 {
+       dbg_printf("cds_ja_lookup_below_equal key %" PRIu64 "\n", key);
        return cds_ja_lookup_inequality(ja, key, result_key, JA_LOOKUP_BE);
 }
 
 struct cds_ja_node *cds_ja_lookup_above_equal(struct cds_ja *ja,
                uint64_t key, uint64_t *result_key)
 {
+       dbg_printf("cds_ja_lookup_above_equal key %" PRIu64 "\n", key);
        return cds_ja_lookup_inequality(ja, key, result_key, JA_LOOKUP_AE);
 }
 
@@ -2135,25 +2146,41 @@ int ja_chain_node(struct cds_ja *ja,
                struct cds_ja_inode_flag *parent_node_flag,
                struct cds_ja_inode_flag **node_flag_ptr,
                struct cds_ja_inode_flag *node_flag,
+               struct cds_ja_node *last_node,
                struct cds_ja_node *node)
 {
        struct cds_ja_shadow_node *shadow_node;
-       int ret = 0;
+       struct cds_ja_node *iter_node;
+       int ret = 0, found = 0;
 
        shadow_node = rcuja_shadow_lookup_lock(ja->ht, parent_node_flag);
        if (!shadow_node) {
                return -EAGAIN;
        }
-       if (ja_node_ptr(*node_flag_ptr) != ja_node_ptr(node_flag)) {
+       /*
+        * Ensure that previous node is still there at end of list.
+        */
+       iter_node = (struct cds_ja_node *) ja_node_ptr(node_flag);
+       if ((struct cds_ja_node *) ja_node_ptr(*node_flag_ptr) != iter_node) {
+               ret = -EAGAIN;
+               goto end;
+       }
+       cds_ja_for_each_duplicate(iter_node) {
+               if (iter_node == last_node)
+                       found = 1;
+       }
+       if (!found) {
                ret = -EAGAIN;
                goto end;
        }
        /*
-        * Add node to head of list. Safe against concurrent RCU read
-        * traversals.
+        * Add node to tail of list to ensure that RCU traversals will
+        * always see either the prior node or the newly added if
+        * executed concurrently with a sequence of add followed by del
+        * on the same key. Safe against concurrent RCU read traversals.
         */
-       node->next = (struct cds_ja_node *) node_flag;
-       rcu_assign_pointer(*node_flag_ptr, (struct cds_ja_inode_flag *) node);
+       node->next = NULL;
+       rcu_assign_pointer(last_node->next, node);
 end:
        rcuja_shadow_unlock(shadow_node);
        return ret;
@@ -2227,11 +2254,18 @@ retry:
                                node_flag,
                                key, i, node);
        } else {
+               struct cds_ja_node *iter_node, *last_node = NULL;
+
                if (unique_node_ret) {
                        *unique_node_ret = (struct cds_ja_node *) ja_node_ptr(node_flag);
                        return -EEXIST;
                }
 
+               /* Find last duplicate */
+               iter_node = (struct cds_ja_node *) ja_node_ptr(node_flag);
+               cds_ja_for_each_duplicate_rcu(iter_node)
+                       last_node = iter_node;
+
                dbg_printf("cds_ja_add duplicate parent2_node_flag %p parent_node_flag %p node_flag_ptr %p node_flag %p\n",
                                parent2_node_flag, parent_node_flag, node_flag_ptr, node_flag);
 
@@ -2243,6 +2277,7 @@ retry:
                        parent_attach_node_flag,
                        attach_node_flag_ptr,
                        attach_node_flag,
+                       last_node,
                        node);
        }
        if (ret == -EAGAIN || ret == -EEXIST)
This page took 0.025195 seconds and 4 git commands to generate.