return head;
}
-/*
- * cds_ja_lookup_lower_equal() may need to retry if a concurrent removal
- * causes failure to find the previous node.
- */
struct cds_hlist_head cds_ja_lookup_lower_equal(struct cds_ja *ja, uint64_t key)
{
int tree_depth, level;
if (caa_unlikely(key > ja->key_max || !key))
return head;
-retry:
memset(cur_node_depth, 0, sizeof(cur_node_depth));
tree_depth = ja->tree_depth;
node_flag = rcu_dereference(ja->root);
level++;
/*
- * From this point, we should be able to find a "lower than"
- * match. The only reason why we could fail to find such a match
- * would be due to a concurrent removal of the branch that
- * contains the match. If this happens, we have no choice but to
- * retry the entire lookup. Indeed, just because we reached a
- * dead-end due to concurrent removal of the branch does not
- * mean that other match don't exist. However, this requires
- * going up into the tree, hence the retry.
+ * From this point, we are guaranteed to be able to find a
+ * "lower than" match. ja_attach_node() and ja_detach_node()
+ * both guarantee that it is not possible for a lookup to reach
+ * a dead-end.
*/
/* Find rightmost child of rightmost child (recursively). */
break;
}
- if (level != tree_depth) {
- /*
- * We did not get a match. Caused by concurrent removal.
- * We need to retry the entire lookup.
- */
- goto retry;
- }
+ assert(level == tree_depth);
- /* Last level lookup succeded. We got a "lower than" match. */
head.next = (struct cds_hlist_node *) node_flag;
return head;
}
* parent lock (if needed). Then we can proceed to create the new
* branch. Publish the new branch, and release locks.
* TODO: we currently always take the parent lock even when not needed.
+ *
+ * ja_attach_node() ensures that a lookup will _never_ see a branch that
+ * leads to a dead-end: before attaching a branch, the entire content of
+ * the new branch is populated, thus creating a cluster, before
+ * attaching the cluster to the rest of the tree, thus making it visible
+ * to lookups.
*/
static
int ja_attach_node(struct cds_ja *ja,
* However, when a child is removed from "linear" nodes, its pointer
* is set to NULL. We therefore check, while holding the locks, if this
* pointer is NULL, and return -ENOENT to the caller if it is the case.
+ *
+ * ja_detach_node() ensures that a lookup will _never_ see a branch that
+ * leads to a dead-end: when removing branch, it makes sure to perform
+ * the "cut" at the highest node that has only one child, effectively
+ * replacing it with a NULL pointer.
*/
static
int ja_detach_node(struct cds_ja *ja,
__attribute__((visibility("protected")))
void rcuja_free_all_children(struct cds_ja_shadow_node *shadow_node,
struct cds_ja_inode_flag *node_flag,
- void (*free_node_cb)(struct rcu_head *head))
+ void (*rcu_free_node)(struct cds_ja_node *node))
{
- const struct rcu_flavor_struct *flavor;
unsigned int type_index;
struct cds_ja_inode *node;
const struct cds_ja_type *type;
- flavor = cds_lfht_rcu_flavor(shadow_node->ja->ht);
node = ja_node_ptr(node_flag);
assert(node != NULL);
type_index = ja_node_type(node_flag);
continue;
head.next = (struct cds_hlist_node *) iter;
cds_hlist_for_each_entry_safe(entry, pos, tmp, &head, list) {
- flavor->update_call_rcu(&entry->head, free_node_cb);
+ rcu_free_node(entry);
}
}
break;
continue;
head.next = (struct cds_hlist_node *) iter;
cds_hlist_for_each_entry_safe(entry, pos, tmp, &head, list) {
- flavor->update_call_rcu(&entry->head, free_node_cb);
+ rcu_free_node(entry);
}
}
}
continue;
head.next = (struct cds_hlist_node *) iter;
cds_hlist_for_each_entry_safe(entry, pos, tmp, &head, list) {
- flavor->update_call_rcu(&entry->head, free_node_cb);
+ rcu_free_node(entry);
}
}
break;
* being destroyed (ensured by the caller).
*/
int cds_ja_destroy(struct cds_ja *ja,
- void (*free_node_cb)(struct rcu_head *head))
+ void (*rcu_free_node)(struct cds_ja_node *node))
{
const struct rcu_flavor_struct *flavor;
int ret;
flavor = cds_lfht_rcu_flavor(ja->ht);
rcuja_shadow_prune(ja->ht,
RCUJA_SHADOW_CLEAR_FREE_NODE | RCUJA_SHADOW_CLEAR_FREE_LOCK,
- free_node_cb);
+ rcu_free_node);
flavor->thread_offline();
ret = rcuja_delete_ht(ja->ht);
if (ret)