commit d66183efc6ee57c86400afba3b9b3f8635d3245e Author: Olivier Houchard 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 (cherry picked from commit 15a9450b508a3c9fc2ad2463931e89157c4331f1) Signed-off-by: Christopher Faulet 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 +#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 */