]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
WIP/MEDIUM: mt-list: Add more try-lock functions
authorChristopher Faulet <cfaulet@haproxy.com>
Thu, 16 Oct 2025 09:21:28 +0000 (11:21 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Tue, 21 Oct 2025 13:18:12 +0000 (15:18 +0200)
include/import/mt_list.h

index b1eb7244f39352a510251f1fbe9af939ca00097a..ff5642ba6e080a4523f0a37089a53e1109d13064 100644 (file)
@@ -771,6 +771,27 @@ static MT_INLINE struct mt_list mt_list_lock_next(struct mt_list *lh)
        return el;
 }
 
+/*
+ * Same as mt_list_lock_next(), except it doesn't wait if the next
+ * is locked already, and just returns { NULL, NULL }
+ */
+static MT_INLINE struct mt_list mt_list_try_lock_next(struct mt_list *lh)
+{
+       struct mt_list el;
+       struct mt_list missed = { NULL, NULL };
+
+       el.next = __atomic_exchange_n(&lh->next, MT_LIST_BUSY, __ATOMIC_RELAXED);
+       if (el.next == MT_LIST_BUSY)
+               return missed;
+
+       el.prev = __atomic_exchange_n(&el.next->prev, MT_LIST_BUSY, __ATOMIC_RELAXED);
+       if (el.prev == MT_LIST_BUSY) {
+               lh->next = el.next;
+               __atomic_thread_fence(__ATOMIC_RELEASE);
+               return missed;
+       }
+       return el;
+}
 
 /* Opens the list just before <lh> which usually is the list's head, but not
  * necessarily. The link between <lh> and its prev element is cut and replaced
@@ -861,6 +882,28 @@ static MT_INLINE struct mt_list mt_list_lock_elem(struct mt_list *el)
        return ret;
 }
 
+/*
+ * Same as mt_list_lock_elem(), except it doesn't wait if the element
+ * is locked already, and just returns { NULL, NULL }
+ */
+static MT_INLINE struct mt_list mt_list_try_lock_elem(struct mt_list *el)
+{
+       struct mt_list ret;
+       struct mt_list missed = { NULL, NULL };
+
+       ret.next = __atomic_exchange_n(&el->next, MT_LIST_BUSY, __ATOMIC_RELAXED);
+       if (ret.next == MT_LIST_BUSY)
+               return missed;
+
+       ret.prev = __atomic_exchange_n(&el->prev, MT_LIST_BUSY, __ATOMIC_RELAXED);
+       if (ret.prev == MT_LIST_BUSY) {
+               el->next = ret.next;
+               __atomic_thread_fence(__ATOMIC_RELEASE);
+               return missed;
+       }
+       return ret;
+}
+
 
 /* Restores element <el> to its previous copy <back>, effectively unlocking it.
  * This is to be used with the returned element from mt_list_lock_elem().
@@ -952,6 +995,51 @@ static MT_INLINE struct mt_list mt_list_lock_full(struct mt_list *el)
        return ret;
 }
 
+/*
+ * Same as mt_list_lock_full(), except it doesn't wait if the element
+ * is locked already, and just returns { NULL, NULL }
+ */
+static MT_INLINE struct mt_list mt_list_try_lock_full(struct mt_list *el)
+{
+       struct mt_list *n2;
+       struct mt_list *p2;
+       struct mt_list ret;
+       struct mt_list missed = { NULL, NULL };
+
+       p2 = NULL;
+       ret.next = __atomic_exchange_n(&el->next, MT_LIST_BUSY, __ATOMIC_RELAXED);
+       if (ret.next == MT_LIST_BUSY)
+               return missed;
+
+       ret.prev = __atomic_exchange_n(&el->prev, MT_LIST_BUSY, __ATOMIC_RELAXED);
+       if (ret.prev == MT_LIST_BUSY) {
+               el->next = ret.next;
+               __atomic_thread_fence(__ATOMIC_RELEASE);
+               return missed;
+       }
+
+       if (ret.prev != el) {
+               p2 = __atomic_exchange_n(&ret.prev->next, MT_LIST_BUSY, __ATOMIC_RELAXED);
+               if (p2 == MT_LIST_BUSY) {
+                       *el = ret;
+                       __atomic_thread_fence(__ATOMIC_RELEASE);
+                       return missed;
+               }
+       }
+
+       if (ret.next != el) {
+               n2 = __atomic_exchange_n(&ret.next->prev, MT_LIST_BUSY, __ATOMIC_RELAXED);
+               if (n2 == MT_LIST_BUSY) {
+                       if (p2 != NULL)
+                               ret.prev->next = p2;
+                       *el = ret;
+                       __atomic_thread_fence(__ATOMIC_RELEASE);
+                       return missed;
+               }
+       }
+       return ret;
+}
+
 /* Connects two ends in a list together, effectively unlocking the list if it
  * was locked. It takes a list head which contains a pointer to the prev and
  * next elements to connect together. It normally is a copy of a previous link