X-Git-Url: https://git.liburcu.org/?a=blobdiff_plain;f=urcu-ht.c;h=7213c39ce1414d788149dc864f9114bcb814acb0;hb=3df2df75972d2ccaa1a1fa01bad06b0eda7f76f2;hp=324ebed93420dd7957ffd03963aae07340e5001a;hpb=567d755011a9f52f13c53d27842150fd9564da7a;p=urcu.git diff --git a/urcu-ht.c b/urcu-ht.c index 324ebed..7213c39 100644 --- a/urcu-ht.c +++ b/urcu-ht.c @@ -5,16 +5,20 @@ #define _LGPL_SOURCE #include +#include +#include +#include + #include +#include #include #include -#include #include -#include -#include -#include #include -#include +#include + +/* node flags */ +#define NODE_STOLEN (1 << 0) struct rcu_ht_node; @@ -22,6 +26,7 @@ struct rcu_ht_node { struct rcu_ht_node *next; void *key; void *data; + unsigned int flags; }; struct rcu_ht { @@ -93,6 +98,7 @@ int ht_add(struct rcu_ht *ht, void *key, void *data) new_head = calloc(1, sizeof(struct rcu_ht_node)); new_head->key = key; new_head->data = data; + new_head->flags = 0; /* here comes the fun and tricky part. * Add at the beginning with a cmpxchg. * Hold a read lock between the moment the first element is read @@ -134,8 +140,14 @@ restart: /* * Restart until we successfully remove the entry, or no entry is left * ((void *)(unsigned long)-ENOENT). - * Deal with concurrent stealers by verifying that there are no element - * in the list still pointing to the element stolen. (del_node) + * Deal with concurrent stealers by doing an extra verification pass to check + * that no element in the list are still pointing to the element stolen. + * This could happen if two concurrent steal for consecutive objects are + * executed. A pointer to an object being stolen could be saved by the + * concurrent stealer for the previous object. + * Also, given that in this precise scenario, another stealer can also want to + * delete the doubly-referenced object; use a "stolen" flag to let only one + * stealer delete the object. */ void *ht_steal(struct rcu_ht *ht, void *key) { @@ -155,7 +167,6 @@ retry: if (del_node) { goto end; } else { - data = (void *)(unsigned long)-ENOENT; goto error; } } @@ -165,8 +176,19 @@ retry: prev = &node->next; node = rcu_dereference(*prev); } + + if (!del_node) { + /* + * Another concurrent thread stole it ? If so, let it deal with + * this. Assume NODE_STOLEN is the only flag. If this changes, + * read flags before cmpxchg. + */ + if (cmpxchg(&node->flags, 0, NODE_STOLEN) != 0) + goto error; + } + /* Found it ! pointer to object is in "prev" */ - if (rcu_cmpxchg_pointer(prev, node, node->next) != node) + if (rcu_cmpxchg_pointer(prev, node, node->next) == node) del_node = node; goto restart; @@ -182,6 +204,7 @@ end: return data; error: + data = (void *)(unsigned long)-ENOENT; rcu_read_unlock(); return data;