Signed-off-by: Junchang Wang <junchangw...@gmail.com>
---
 include/urcu/rculflist.h | 284 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 284 insertions(+)
 create mode 100644 include/urcu/rculflist.h

diff --git a/include/urcu/rculflist.h b/include/urcu/rculflist.h
new file mode 100644
index 0000000..35c08aa
--- /dev/null
+++ b/include/urcu/rculflist.h
@@ -0,0 +1,284 @@
+/*
+ * rculflist.c
+ *
+ * Userspace RCU library - Lock-Free and ordered singly-linked list
+ *
+ * Copyright (c) 2019 Junchang Wang
+ * Copyright (c) 2019 Paul E. McKenney
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can access it online at
+ * http://www.gnu.org/licenses/gpl-2.0.html.
+ *
+ *
+ * Based on the following research paper:
+ * - Maged M. Michael. High performance dynamic lock-free hash tables
+ *   and list-based sets. In Proceedings of the fourteenth annual ACM
+ *   symposium on Parallel algorithms and architectures, ACM Press,
+ *   (2002), 73-82.
+ *
+ * Some specificities of this Lock-free linked list implementation:
+ * - The original algorithm prevents the ABA problem by adding a tag field
+ *   in each hash-table node, whereas this implementation addresses this
+ *   issue by using the RCU mechanism.
+ */
+
+#ifndef _URCU_RCULFLIST_H
+#define _URCU_RCULFLIST_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <errno.h>
+#include <urcu/uatomic.h>
+#include <urcu-pointer.h>
+#include <urcu-call-rcu.h>
+
+#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))
+#define READ_ONCE(x) \
+            ({ typeof(x) ___x = ACCESS_ONCE(x); ___x; })
+#define WRITE_ONCE(x, val) ({ ACCESS_ONCE(x) = (val); })
+
+/*
+ * lflist reserves the least-significant 2 bits to record control information.
+ * Currently, it only uses the least-significant 1 bit to indicate if a node
+ * has been logically removed.
+ */
+#define RESERVED_BITS_LEN (2)
+#define REMOVED_FLAG   (1UL << 0)
+#define FLAGS_MASK     ((1UL << RESERVED_BITS_LEN) - 1)
+
+/*
+ * Define the structure of list node and related functions.
+ */
+struct cds_lflist_node {
+       struct rcu_head rcu_head;
+       unsigned long key;
+       struct cds_lflist_node *next; /* (pointer | xxx_FLAG) */
+} __attribute__((aligned(CAA_CACHE_LINE_SIZE)));
+
+static
+unsigned long get_flag(struct cds_lflist_node *ptr)
+{
+       return (unsigned long)((uintptr_t)ptr & FLAGS_MASK);
+}
+
+static
+struct cds_lflist_node * get_ptr(struct cds_lflist_node *ptr)
+{
+       return (struct cds_lflist_node *)((uintptr_t)ptr & ~FLAGS_MASK);
+}
+
+static
+struct cds_lflist_node * set_flag(struct cds_lflist_node *ptr,
+                                      unsigned long mask)
+{
+       return (struct cds_lflist_node *)
+                    (((uintptr_t)ptr & ~FLAGS_MASK) | mask);
+}
+
+static
+int is_removed(struct cds_lflist_node *ptr)
+{
+       return ((uintptr_t)ptr & FLAGS_MASK) != 0;
+}
+
+void cds_lflist_node_init_rcu(struct cds_lflist_node *node)
+{
+       node->next = NULL;
+       node->key = 0UL;
+}
+
+void cds_lflist_node_set_key(struct cds_lflist_node *node, unsigned long key)
+{
+       node->key = key;
+}
+
+/*
+ * Struct cds_lflist_snapshot and its helper functions.
+ * Before invoking cds_lflist_find_rcu, the caller must first allocates
+ * an instance of struct cds_lflist_snapshot and passs the pointer
+ * to cds_lflist_find_rcu. By its completion, cds_lflist_find_rcu
+ * guarantees that (1) cur points to the node that contains the
+ * lowest key value that is greater than or equal to the input key
+ * and (2) prev is the predecessor pointer of cur.
+ */
+struct cds_lflist_snapshot {
+       struct cds_lflist_node **prev;
+       struct cds_lflist_node *cur;
+       struct cds_lflist_node *next;
+};
+
+static inline
+void set_snapshot(struct cds_lflist_snapshot *ssp,struct cds_lflist_node 
**prev,
+                    struct cds_lflist_node *cur, struct cds_lflist_node *next)
+{
+       ssp->prev = prev;
+       ssp->cur = get_ptr(cur);
+       ssp->next = get_ptr(next);
+}
+
+/*
+ * Define the struct of lock-free list and its helper functions.
+ */
+struct cds_lflist_rcu {
+       struct cds_lflist_node *head;
+       void (*delete_node)(struct cds_lflist_node *);
+};
+
+int cds_lflist_init_rcu(struct cds_lflist_rcu *list,
+                       void (*node_free)(struct cds_lflist_node *))
+{
+       list->head = NULL;
+       list->delete_node = node_free;
+       return 0;
+}
+
+/*
+ * Function cds_lflist_find_rcu() returns success(0) if the node with
+ * a matching key was found in the list, or returns failure(-Exxx) otherwise.
+ * No matter if find_rcu() returns success or failure, by its completion,
+ * it guarantees that *ssp* points to the snapshot of a segment of the list.
+ * Within the snapshot, field *cur* points to the node that contains
+ * the lowest key value greater than or equal to the input key,
+ * and *prev* is the predecessor pointer of *cur*.
+ */
+int cds_lflist_find_rcu(struct cds_lflist_rcu *list, unsigned long key,
+                            struct cds_lflist_snapshot *ssp)
+{
+       /* Local variables to record the snapshot */
+       struct cds_lflist_node **prev, *cur, *next;
+
+       struct cds_lflist_node *cur_ptr;
+       struct cds_lflist_node *next_ptr;
+       unsigned long cur_key;
+
+try_again:
+       prev = &list->head;
+       cur = rcu_dereference(*prev);
+       cur_ptr = get_ptr(cur);
+
+       for (;;) {
+               if (cur_ptr == NULL) {
+                       /* Have reached the end of the list. */
+                       set_snapshot(ssp, prev, NULL, NULL);
+                       return -ENOENT;
+               }
+               cur_key = cur_ptr->key;
+               next = cur_ptr->next;
+               next_ptr = get_ptr(next);
+
+               /* If a new node has been added before cur, go to retry. */
+               if (READ_ONCE(*prev) != cur_ptr)
+                       goto try_again;
+               if (!is_removed(next)) {
+                       if (cur_key >= key) {
+                               /* The key value of node cur_ptr is equal to
+                                * or larger than the input key value. */
+                               set_snapshot(ssp, prev, cur_ptr, next);
+                               if (cur_key == key)
+                                       return 0;
+                               else
+                                       return -ENOENT;
+                       }
+                       prev = &cur_ptr->next;
+               } else {
+                       /* If the node cur_ptr has been logically deleted,
+                        * try to physically delete it. */
+                       if (uatomic_cmpxchg(prev, cur_ptr, next_ptr) == 
cur_ptr){
+                               /* Some applications (e.g., hashtorture) manages
+                                * (destroy) nodes by themselves. For these 
cases,
+                                * list->delete_node is initialized to NULL.  */
+                               if(list->delete_node)
+                                       list->delete_node(cur_ptr);
+                       } else {
+                               /* One of other threads has physically delete
+                                * the node. Retry. */
+                               goto try_again;
+                       }
+               }
+               cur = rcu_dereference(next);
+               cur_ptr = get_ptr(cur);
+       }
+}
+
+/*
+ * Function cds_lflist_insert_rcu returns -EINVAL if a node with a matching key
+ * is found in the list. Otherwise, this function inserts the new node before
+ * the node ss.cur and returns 0.
+ */
+int cds_lflist_insert_rcu(struct cds_lflist_rcu *list,
+                              struct cds_lflist_node *node)
+{
+       unsigned long key = node->key;
+       struct cds_lflist_snapshot ss;
+
+       for (;;) {
+               if (!cds_lflist_find_rcu(list, key, &ss))
+                       return -EINVAL;
+               rcu_assign_pointer(node->next, set_flag(ss.cur, 
get_flag(node->next)));
+               if (uatomic_cmpxchg(ss.prev, set_flag(ss.cur, 0UL),
+                               set_flag(node,0UL)) == set_flag(ss.cur, 0UL)) {
+                       return 0;
+               }
+       }
+}
+
+/*
+ * Function cds_lflist_delete_rcu returns -EINVAL if the key is not found.
+ * Otherwise, the key value of the node pointed to by ss.cur must be equal to
+ * the input key. The function deletes the node by (1) marking the node pointed
+ * to by ss.cur as deleted, and (2) swinging prev->next to point to next.
+ */
+int cds_lflist_delete_rcu(struct cds_lflist_rcu *list, unsigned long key,
+                             struct cds_lflist_snapshot *ss)
+{
+       /* Local variables to record the snapshot */
+       struct cds_lflist_node *cur, *next;
+
+       for (;;) {
+               if (cds_lflist_find_rcu(list, key, ss))
+                       return -EINVAL;
+               cur = READ_ONCE(ss->cur);
+               next = READ_ONCE(ss->next);
+
+               /* The node to be deleted is pointed to by ss->cur.
+                * We first logically deleted it by setting its REMOVED_FLAG.
+                */
+               if (uatomic_cmpxchg(&cur->next, set_flag(next, 0UL),
+                               set_flag(next, REMOVED_FLAG)) != set_flag(next, 
0UL))
+                       continue;
+               /* If node pointed to by ss->cur has been logically deleted,
+                * try to physically delete it.
+                */
+               if (uatomic_cmpxchg(ss->prev, set_flag(cur, 0UL),
+                               set_flag(next, 0UL)) == set_flag(cur, 0UL)) {
+                       /* Some applications (e.g., hashtorture) manages
+                        * (destroy) nodes by themselves. For these cases,
+                        * list->delete_node is initialized to NULL.  */
+                       if (list->delete_node)
+                               list->delete_node(cur);
+               } else {
+                       /* Physical deletion failed. Retry.  */
+                       cds_lflist_find_rcu(list, key, ss);
+               }
+               return 0;
+       }
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*_URCU_RCULFLIST_H */
-- 
1.8.3.1

_______________________________________________
lttng-dev mailing list
lttng-dev@lists.lttng.org
https://lists.lttng.org/cgi-bin/mailman/listinfo/lttng-dev

Reply via email to