commit d66183efc6ee57c86400afba3b9b3f8635d3245e
|
|
Author: Olivier Houchard <ohouchard@haproxy.com>
|
|
Date: Fri Jan 18 17:26:26 2019 +0100
|
|
|
|
MINOR: lists: Implement locked variations.
|
|
|
|
Implement LIST_ADD_LOCKED(), LIST_ADDQ_LOCKED(), LIST_DEL_LOCKED() and
|
|
LIST_POP_LOCKED().
|
|
|
|
LIST_ADD_LOCKED, LIST_ADDQ_LOCKED and LIST_DEL_LOCKED work the same as
|
|
LIST_ADD, LIST_ADDQ and LIST_DEL, except before any manipulation it locks
|
|
the relevant elements of the list, so it's safe to manipulate the list
|
|
with multiple threads.
|
|
LIST_POP_LOCKED() removes the first element from the list, and returns its
|
|
data.
|
|
|
|
(cherry picked from commit a8434ec14612d9a04e50e1c51e58f663dad46e96)
|
|
Signed-off-by: Willy Tarreau <w@1wt.eu>
|
|
(cherry picked from commit 15a9450b508a3c9fc2ad2463931e89157c4331f1)
|
|
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
|
|
|
|
diff --git a/include/common/mini-clist.h b/include/common/mini-clist.h
|
|
index 2988d2c2..b3f396ef 100644
|
|
--- a/include/common/mini-clist.h
|
|
+++ b/include/common/mini-clist.h
|
|
@@ -163,5 +163,149 @@ struct cond_wordlist {
|
|
&item->member != (list_head); \
|
|
item = back, back = LIST_ELEM(back->member.n, typeof(back), member))
|
|
|
|
+#include <common/hathreads.h>
|
|
+#define LLIST_BUSY ((struct list *)1)
|
|
+
|
|
+/*
|
|
+ * Locked version of list manipulation macros.
|
|
+ * It is OK to use those concurrently from multiple threads, as long as the
|
|
+ * list is only used with the locked variants. The only "unlocked" macro you
|
|
+ * can use with a locked list is LIST_INIT.
|
|
+ */
|
|
+#define LIST_ADD_LOCKED(lh, el) \
|
|
+ do { \
|
|
+ while (1) { \
|
|
+ struct list *n; \
|
|
+ struct list *p; \
|
|
+ n = HA_ATOMIC_XCHG(&(lh)->n, LLIST_BUSY); \
|
|
+ if (n == LLIST_BUSY) \
|
|
+ continue; \
|
|
+ __ha_barrier_store(); \
|
|
+ p = HA_ATOMIC_XCHG(&n->p, LLIST_BUSY); \
|
|
+ if (p == LLIST_BUSY) { \
|
|
+ (lh)->n = n; \
|
|
+ __ha_barrier_store(); \
|
|
+ continue; \
|
|
+ } \
|
|
+ (el)->n = n; \
|
|
+ (el)->p = p; \
|
|
+ n->p = (el); \
|
|
+ __ha_barrier_store(); \
|
|
+ p->n = (el); \
|
|
+ __ha_barrier_store(); \
|
|
+ break; \
|
|
+ } \
|
|
+ } while (0)
|
|
+
|
|
+#define LIST_ADDQ_LOCKED(lh, el) \
|
|
+ do { \
|
|
+ while (1) { \
|
|
+ struct list *n; \
|
|
+ struct list *p; \
|
|
+ p = HA_ATOMIC_XCHG(&(lh)->p, LLIST_BUSY); \
|
|
+ if (p == LLIST_BUSY) \
|
|
+ continue; \
|
|
+ __ha_barrier_store(); \
|
|
+ n = HA_ATOMIC_XCHG(&p->n, LLIST_BUSY); \
|
|
+ if (n == LLIST_BUSY) { \
|
|
+ (lh)->n = p; \
|
|
+ __ha_barrier_store(); \
|
|
+ continue; \
|
|
+ } \
|
|
+ (el)->n = n; \
|
|
+ (el)->p = p; \
|
|
+ n->p = (el); \
|
|
+ __ha_barrier_store(); \
|
|
+ p->n = (el); \
|
|
+ __ha_barrier_store(); \
|
|
+ break; \
|
|
+ } \
|
|
+ } while (0)
|
|
+
|
|
+#define LIST_DEL_LOCKED(el) \
|
|
+ do { \
|
|
+ while (1) { \
|
|
+ struct list *n, *n2; \
|
|
+ struct list *p, *p2; \
|
|
+ n = HA_ATOMIC_XCHG(&(el)->n, LLIST_BUSY); \
|
|
+ if (n == LLIST_BUSY) \
|
|
+ continue; \
|
|
+ p = HA_ATOMIC_XCHG(&(el)->p, LLIST_BUSY); \
|
|
+ if (p == LLIST_BUSY) { \
|
|
+ (el)->n = n; \
|
|
+ __ha_barrier_store(); \
|
|
+ continue; \
|
|
+ } \
|
|
+ if (p != (el)) { \
|
|
+ p2 = HA_ATOMIC_XCHG(&p->n, LLIST_BUSY); \
|
|
+ if (p2 == LLIST_BUSY) { \
|
|
+ (el)->p = p; \
|
|
+ (el)->n = n; \
|
|
+ __ha_barrier_store(); \
|
|
+ continue; \
|
|
+ } \
|
|
+ } \
|
|
+ if (n != (el)) { \
|
|
+ n2 = HA_ATOMIC_XCHG(&n->p, LLIST_BUSY); \
|
|
+ if (n2 == LLIST_BUSY) { \
|
|
+ p2->n = (el); \
|
|
+ (el)->p = p; \
|
|
+ (el)->n = n; \
|
|
+ __ha_barrier_store(); \
|
|
+ continue; \
|
|
+ } \
|
|
+ } \
|
|
+ n->p = p; \
|
|
+ p->n = n; \
|
|
+ __ha_barrier_store(); \
|
|
+ break; \
|
|
+ } \
|
|
+ } while (0)
|
|
+
|
|
+
|
|
+/* Remove the first element from the list, and return it */
|
|
+#define LIST_POP_LOCKED(lh, pt, el) \
|
|
+ ({ \
|
|
+ void *_ret; \
|
|
+ while (1) { \
|
|
+ struct list *n, *n2; \
|
|
+ struct list *p, *p2; \
|
|
+ n = HA_ATOMIC_XCHG(&(lh)->n, LLIST_BUSY); \
|
|
+ if (n == LLIST_BUSY) \
|
|
+ continue; \
|
|
+ if (n == (lh)) { \
|
|
+ (lh)->n = lh; \
|
|
+ _ret = NULL; \
|
|
+ break; \
|
|
+ } \
|
|
+ p = HA_ATOMIC_XCHG(&n->p, LLIST_BUSY); \
|
|
+ if (p == LLIST_BUSY) { \
|
|
+ (lh)->n = n; \
|
|
+ __ha_barrier_store(); \
|
|
+ continue; \
|
|
+ } \
|
|
+ n2 = HA_ATOMIC_XCHG(&n->n, LLIST_BUSY); \
|
|
+ if (n2 == LLIST_BUSY) { \
|
|
+ n->p = p; \
|
|
+ (lh)->n = n; \
|
|
+ __ha_barrier_store(); \
|
|
+ continue; \
|
|
+ } \
|
|
+ p2 = HA_ATOMIC_XCHG(&n2->p, LLIST_BUSY); \
|
|
+ if (p2 == LLIST_BUSY) { \
|
|
+ n->n = n2; \
|
|
+ n->p = p; \
|
|
+ (lh)->n = n; \
|
|
+ __ha_barrier_store(); \
|
|
+ continue; \
|
|
+ } \
|
|
+ (lh)->n = n2; \
|
|
+ (n2)->p = (lh); \
|
|
+ __ha_barrier_store(); \
|
|
+ _ret = LIST_ELEM(n, pt, el); \
|
|
+ break; \
|
|
+ } \
|
|
+ (_ret); \
|
|
+ })
|
|
|
|
#endif /* _COMMON_MINI_CLIST_H */
|