]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: thread: add a lock level information in the thread_ctx
authorWilly Tarreau <w@1wt.eu>
Thu, 11 Sep 2025 06:29:25 +0000 (08:29 +0200)
committerWilly Tarreau <w@1wt.eu>
Thu, 11 Sep 2025 14:32:34 +0000 (16:32 +0200)
The new lock_level field indicates the number of cumulated locks that
are held by the current thread. It's fed as soon as DEBUG_THREAD is at
least 1. In addition, thread_isolate() adds 128, so that it's even
possible to check for combinations of both. The value is also reported
in thread dumps (warnings and panics).

include/haproxy/thread.h
include/haproxy/tinfo-t.h
src/debug.c
src/thread.c

index c030c5e0f0d1a34987a35899dbef945895b08e20..c5a4857973d868af33f85ce290b135cc9ec40e28 100644 (file)
@@ -331,8 +331,8 @@ static inline unsigned long thread_isolated()
 
 #if (DEBUG_THREAD < 1) && !defined(DEBUG_FULL)
 
-#define _lock_wait(_LK_, lbl, expr) do { (void)(expr); } while (0)
-#define _lock_cond(_LK_, lbl, expr) ({ typeof(expr) _expr = (expr); _expr; })
+#define _lock_wait(_LK_, bal, lbl, expr) do { (void)(expr); } while (0)
+#define _lock_cond(_LK_, bal, lbl, expr) ({ typeof(expr) _expr = (expr); _expr; })
 
 #else
 
@@ -359,21 +359,26 @@ static inline unsigned long thread_isolated()
                        th_ctx->lock_history = (th_ctx->lock_history << 8) + _lck; \
        } while (0)
 
-#define _lock_wait(_LK_, lbl, expr) do {                               \
+#define _lock_wait(_LK_, bal, lbl, expr) do {                          \
                uint64_t lock_start = 0;                                \
                extern uint64_t now_mono_time(void);                    \
                if (_LK_ != _LK_UN) {                                   \
+                       th_ctx->lock_level += bal;                      \
                        if (unlikely(th_ctx->flags & TH_FL_TASK_PROFILING)) \
                                lock_start = now_mono_time();           \
                }                                                       \
                (void)(expr);                                           \
-               if (_LK_ != _LK_UN && unlikely(lock_start))             \
+               if (_LK_ == _LK_UN)                                     \
+                       th_ctx->lock_level += bal;                      \
+               else if (unlikely(lock_start))                          \
                        th_ctx->lock_wait_total += now_mono_time() - lock_start; \
                if (lbl != OTHER_LOCK)                                  \
                        _lock_wait_common(_LK_, lbl);                   \
        } while (0)
-#define _lock_cond(_LK_, lbl, expr) ({                                 \
+#define _lock_cond(_LK_, bal, lbl, expr) ({                            \
                typeof(expr) _expr = (expr);                            \
+               if (_expr == 0)                                         \
+                       th_ctx->lock_level += bal;                      \
                if (lbl != OTHER_LOCK && !_expr)                        \
                        _lock_wait_common(_LK_, lbl);                   \
                _expr; \
@@ -414,29 +419,29 @@ static inline uint64_t cshared_read(struct cshared *ctr)
 
 #define HA_SPIN_INIT(l)            ({ (*l) = 0; })
 #define HA_SPIN_DESTROY(l)         ({ (*l) = 0; })
-#define HA_SPIN_LOCK(lbl, l)       _lock_wait(_LK_SK, lbl, pl_take_s(l))
-#define HA_SPIN_TRYLOCK(lbl, l)    _lock_cond(_LK_SK, lbl, !pl_try_s(l))
-#define HA_SPIN_UNLOCK(lbl, l)     _lock_wait(_LK_UN, lbl, pl_drop_s(l))
+#define HA_SPIN_LOCK(lbl, l)       _lock_wait(_LK_SK,  1, lbl, pl_take_s(l))
+#define HA_SPIN_TRYLOCK(lbl, l)    _lock_cond(_LK_SK,  1, lbl, !pl_try_s(l))
+#define HA_SPIN_UNLOCK(lbl, l)     _lock_wait(_LK_UN, -1, lbl, pl_drop_s(l))
 
 #define HA_RWLOCK_INIT(l)          ({ (*l) = 0; })
 #define HA_RWLOCK_DESTROY(l)       ({ (*l) = 0; })
-#define HA_RWLOCK_WRLOCK(lbl,l)    _lock_wait(_LK_WR, lbl, pl_take_w(l))
-#define HA_RWLOCK_TRYWRLOCK(lbl,l) _lock_cond(_LK_WR, lbl, !pl_try_w(l))
-#define HA_RWLOCK_WRUNLOCK(lbl,l)  _lock_wait(_LK_UN, lbl, pl_drop_w(l))
-#define HA_RWLOCK_RDLOCK(lbl,l)    _lock_wait(_LK_RD, lbl, pl_take_r(l))
-#define HA_RWLOCK_TRYRDLOCK(lbl,l) _lock_cond(_LK_RD, lbl, (!pl_try_r(l)))
-#define HA_RWLOCK_RDUNLOCK(lbl,l)  _lock_wait(_LK_UN, lbl, pl_drop_r(l))
+#define HA_RWLOCK_WRLOCK(lbl,l)    _lock_wait(_LK_WR,  1, lbl, pl_take_w(l))
+#define HA_RWLOCK_TRYWRLOCK(lbl,l) _lock_cond(_LK_WR,  1, lbl, !pl_try_w(l))
+#define HA_RWLOCK_WRUNLOCK(lbl,l)  _lock_wait(_LK_UN, -1, lbl, pl_drop_w(l))
+#define HA_RWLOCK_RDLOCK(lbl,l)    _lock_wait(_LK_RD,  1, lbl, pl_take_r(l))
+#define HA_RWLOCK_TRYRDLOCK(lbl,l) _lock_cond(_LK_RD,  1, lbl, (!pl_try_r(l)))
+#define HA_RWLOCK_RDUNLOCK(lbl,l)  _lock_wait(_LK_UN, -1, lbl, pl_drop_r(l))
 
 /* rwlock upgrades via seek locks */
-#define HA_RWLOCK_SKLOCK(lbl,l)         _lock_wait(_LK_SK, lbl, pl_take_s(l))      /* N --> S */
-#define HA_RWLOCK_SKTOWR(lbl,l)         _lock_wait(_LK_WR, lbl, pl_stow(l))        /* S --> W */
-#define HA_RWLOCK_WRTOSK(lbl,l)         _lock_wait(_LK_SK, lbl, pl_wtos(l))        /* W --> S */
-#define HA_RWLOCK_SKTORD(lbl,l)         _lock_wait(_LK_RD, lbl, pl_stor(l))        /* S --> R */
-#define HA_RWLOCK_WRTORD(lbl,l)         _lock_wait(_LK_RD, lbl, pl_wtor(l))        /* W --> R */
-#define HA_RWLOCK_SKUNLOCK(lbl,l)       _lock_wait(_LK_UN, lbl, pl_drop_s(l))      /* S --> N */
-#define HA_RWLOCK_TRYSKLOCK(lbl,l)      _lock_cond(_LK_SK, lbl, !pl_try_s(l))      /* N -?> S */
-#define HA_RWLOCK_TRYRDTOSK(lbl,l)      _lock_cond(_LK_SK, lbl, !pl_try_rtos(l))   /* R -?> S */
-#define HA_RWLOCK_TRYRDTOWR(lbl, l)     _lock_cond(_LK_WR, lbl, !pl_try_rtow(l))   /* R -?> W */
+#define HA_RWLOCK_SKLOCK(lbl,l)         _lock_wait(_LK_SK,  1, lbl, pl_take_s(l))      /* N --> S */
+#define HA_RWLOCK_SKTOWR(lbl,l)         _lock_wait(_LK_WR,  0, lbl, pl_stow(l))        /* S --> W */
+#define HA_RWLOCK_WRTOSK(lbl,l)         _lock_wait(_LK_SK,  0, lbl, pl_wtos(l))        /* W --> S */
+#define HA_RWLOCK_SKTORD(lbl,l)         _lock_wait(_LK_RD,  0, lbl, pl_stor(l))        /* S --> R */
+#define HA_RWLOCK_WRTORD(lbl,l)         _lock_wait(_LK_RD,  0, lbl, pl_wtor(l))        /* W --> R */
+#define HA_RWLOCK_SKUNLOCK(lbl,l)       _lock_wait(_LK_UN, -1, lbl, pl_drop_s(l))      /* S --> N */
+#define HA_RWLOCK_TRYSKLOCK(lbl,l)      _lock_cond(_LK_SK,  1, lbl, !pl_try_s(l))      /* N -?> S */
+#define HA_RWLOCK_TRYRDTOSK(lbl,l)      _lock_cond(_LK_SK,  0, lbl, !pl_try_rtos(l))   /* R -?> S */
+#define HA_RWLOCK_TRYRDTOWR(lbl, l)     _lock_cond(_LK_WR,  0, lbl, !pl_try_rtow(l))   /* R -?> W */
 
 #else /* (DEBUG_THREAD < 2) && !defined(DEBUG_FULL) */
 
@@ -471,28 +476,28 @@ static inline uint64_t cshared_read(struct cshared *ctr)
 #define HA_SPIN_INIT(l)            __spin_init(l)
 #define HA_SPIN_DESTROY(l)         __spin_destroy(l)
 
-#define HA_SPIN_LOCK(lbl, l)       _lock_wait(_LK_SK, lbl, __spin_lock(lbl, l, __func__, __FILE__, __LINE__))
-#define HA_SPIN_TRYLOCK(lbl, l)    _lock_cond(_LK_SK, lbl, __spin_trylock(lbl, l, __func__, __FILE__, __LINE__))
-#define HA_SPIN_UNLOCK(lbl, l)     _lock_wait(_LK_UN, lbl, __spin_unlock(lbl, l, __func__, __FILE__, __LINE__))
+#define HA_SPIN_LOCK(lbl, l)       _lock_wait(_LK_SK,  1, lbl, __spin_lock(lbl, l, __func__, __FILE__, __LINE__))
+#define HA_SPIN_TRYLOCK(lbl, l)    _lock_cond(_LK_SK,  1, lbl, __spin_trylock(lbl, l, __func__, __FILE__, __LINE__))
+#define HA_SPIN_UNLOCK(lbl, l)     _lock_wait(_LK_UN, -1, lbl, __spin_unlock(lbl, l, __func__, __FILE__, __LINE__))
 
 #define HA_RWLOCK_INIT(l)          __ha_rwlock_init((l))
 #define HA_RWLOCK_DESTROY(l)       __ha_rwlock_destroy((l))
-#define HA_RWLOCK_WRLOCK(lbl,l)    _lock_wait(_LK_WR, lbl, __ha_rwlock_wrlock(lbl, l, __func__, __FILE__, __LINE__))
-#define HA_RWLOCK_TRYWRLOCK(lbl,l) _lock_cond(_LK_WR, lbl, __ha_rwlock_trywrlock(lbl, l, __func__, __FILE__, __LINE__))
-#define HA_RWLOCK_WRUNLOCK(lbl,l)  _lock_wait(_LK_UN, lbl, __ha_rwlock_wrunlock(lbl, l, __func__, __FILE__, __LINE__))
-#define HA_RWLOCK_RDLOCK(lbl,l)    _lock_wait(_LK_RD, lbl, __ha_rwlock_rdlock(lbl, l))
-#define HA_RWLOCK_TRYRDLOCK(lbl,l) _lock_cond(_LK_RD, lbl, __ha_rwlock_tryrdlock(lbl, l))
-#define HA_RWLOCK_RDUNLOCK(lbl,l)  _lock_wait(_LK_UN, lbl, __ha_rwlock_rdunlock(lbl, l))
-
-#define HA_RWLOCK_SKLOCK(lbl,l)    _lock_wait(_LK_SK, lbl, __ha_rwlock_sklock(lbl, l, __func__, __FILE__, __LINE__))
-#define HA_RWLOCK_SKTOWR(lbl,l)    _lock_wait(_LK_WR, lbl, __ha_rwlock_sktowr(lbl, l, __func__, __FILE__, __LINE__))
-#define HA_RWLOCK_WRTOSK(lbl,l)    _lock_wait(_LK_SK, lbl, __ha_rwlock_wrtosk(lbl, l, __func__, __FILE__, __LINE__))
-#define HA_RWLOCK_SKTORD(lbl,l)    _lock_wait(_LK_RD, lbl, __ha_rwlock_sktord(lbl, l, __func__, __FILE__, __LINE__))
-#define HA_RWLOCK_WRTORD(lbl,l)    _lock_wait(_LK_RD, lbl, __ha_rwlock_wrtord(lbl, l, __func__, __FILE__, __LINE__))
-#define HA_RWLOCK_SKUNLOCK(lbl,l)  _lock_wait(_LK_UN, lbl, __ha_rwlock_skunlock(lbl, l, __func__, __FILE__, __LINE__))
-#define HA_RWLOCK_TRYSKLOCK(lbl,l) _lock_cond(_LK_SK, lbl, __ha_rwlock_trysklock(lbl, l, __func__, __FILE__, __LINE__))
-#define HA_RWLOCK_TRYRDTOSK(lbl,l) _lock_cond(_LK_RD, lbl, __ha_rwlock_tryrdtosk(lbl, l, __func__, __FILE__, __LINE__))
-#define HA_RWLOCK_TRYRDTOWR(lbl,l) _lock_cond(_LK_WR, lbl, __ha_rwlock_tryrdtowr(lbl, l, __func__, __FILE__, __LINE__))
+#define HA_RWLOCK_WRLOCK(lbl,l)    _lock_wait(_LK_WR,  1, lbl, __ha_rwlock_wrlock(lbl, l, __func__, __FILE__, __LINE__))
+#define HA_RWLOCK_TRYWRLOCK(lbl,l) _lock_cond(_LK_WR,  1, lbl, __ha_rwlock_trywrlock(lbl, l, __func__, __FILE__, __LINE__))
+#define HA_RWLOCK_WRUNLOCK(lbl,l)  _lock_wait(_LK_UN, -1, lbl, __ha_rwlock_wrunlock(lbl, l, __func__, __FILE__, __LINE__))
+#define HA_RWLOCK_RDLOCK(lbl,l)    _lock_wait(_LK_RD,  1, lbl, __ha_rwlock_rdlock(lbl, l))
+#define HA_RWLOCK_TRYRDLOCK(lbl,l) _lock_cond(_LK_RD,  1, lbl, __ha_rwlock_tryrdlock(lbl, l))
+#define HA_RWLOCK_RDUNLOCK(lbl,l)  _lock_wait(_LK_UN, -1, lbl, __ha_rwlock_rdunlock(lbl, l))
+
+#define HA_RWLOCK_SKLOCK(lbl,l)    _lock_wait(_LK_SK,  1, lbl, __ha_rwlock_sklock(lbl, l, __func__, __FILE__, __LINE__))
+#define HA_RWLOCK_SKTOWR(lbl,l)    _lock_wait(_LK_WR,  0, lbl, __ha_rwlock_sktowr(lbl, l, __func__, __FILE__, __LINE__))
+#define HA_RWLOCK_WRTOSK(lbl,l)    _lock_wait(_LK_SK,  0, lbl, __ha_rwlock_wrtosk(lbl, l, __func__, __FILE__, __LINE__))
+#define HA_RWLOCK_SKTORD(lbl,l)    _lock_wait(_LK_RD,  0, lbl, __ha_rwlock_sktord(lbl, l, __func__, __FILE__, __LINE__))
+#define HA_RWLOCK_WRTORD(lbl,l)    _lock_wait(_LK_RD,  0, lbl, __ha_rwlock_wrtord(lbl, l, __func__, __FILE__, __LINE__))
+#define HA_RWLOCK_SKUNLOCK(lbl,l)  _lock_wait(_LK_UN, -1, lbl, __ha_rwlock_skunlock(lbl, l, __func__, __FILE__, __LINE__))
+#define HA_RWLOCK_TRYSKLOCK(lbl,l) _lock_cond(_LK_SK,  1, lbl, __ha_rwlock_trysklock(lbl, l, __func__, __FILE__, __LINE__))
+#define HA_RWLOCK_TRYRDTOSK(lbl,l) _lock_cond(_LK_RD,  0, lbl, __ha_rwlock_tryrdtosk(lbl, l, __func__, __FILE__, __LINE__))
+#define HA_RWLOCK_TRYRDTOWR(lbl,l) _lock_cond(_LK_WR,  0, lbl, __ha_rwlock_tryrdtowr(lbl, l, __func__, __FILE__, __LINE__))
 
 /* Following functions are used to collect some stats about locks. We wrap
  * pthread functions to known how much time we wait in a lock. */
index 78084c497ac1e179fb1f35de883722be5eb8806d..8ea5898fded5008076c22b188cf74e14ba5d3b2e 100644 (file)
@@ -142,8 +142,7 @@ struct thread_ctx {
        uint8_t tl_class_mask;              /* bit mask of non-empty tasklets classes */
        uint8_t bufq_map;                   /* one bit per non-empty buffer_wq */
        uint8_t trc_disable_ctr;            /* cumulative counter to temporarily disable tracing */
-
-       // 1 byte hole here
+       uint8_t lock_level;                 /* locking level for that thread. 0=unlocked, +128=isolated. */
        unsigned int nb_rhttp_conns;        /* count of current conns used for active reverse HTTP */
        struct sched_activity *sched_profile_entry; /* profile entry in use by the current task/tasklet, only if sched_wake_date>0 */
 
index 381831c9d68ec5935fc9ac47057e09ee5c154745..f921e0d68dcdb76797ab8a5dcbdee33b6b630786 100644 (file)
@@ -348,6 +348,9 @@ void ha_thread_dump_one(struct buffer *buf, int is_caller)
                      !!(_HA_ATOMIC_LOAD(&tg_ctx->threads_harmless) & ti->ltid_bit),
                      isolated_thread == tid);
 #endif
+#if (DEBUG_THREAD > 0) || defined(DEBUG_FULL)
+       chunk_appendf(buf, " locks=%d", th_ctx->lock_level);
+#endif
 
        chunk_appendf(buf, "\n");
        chunk_appendf(buf, "             cpu_ns: poll=%llu now=%llu diff=%llu\n", p, n, n-p);
index 4fc7402ea18b99bb7785bedd1b9720152815a174..a451ad95e743574c75d5b108797e337111d8c915 100644 (file)
@@ -139,6 +139,7 @@ void thread_isolate()
         * 1) reset isolated_thread to ~0;
         * 2) decrement rdv_requests.
         */
+       th_ctx->lock_level += 128;
 }
 
 /* Isolates the current thread : request the ability to work while all other
@@ -212,6 +213,7 @@ void thread_isolate_full()
         * 1) reset isolated_thread to ~0;
         * 2) decrement rdv_requests.
         */
+       th_ctx->lock_level += 128;
 }
 
 /* Cancels the effect of thread_isolate() by resetting the ID of the isolated
@@ -224,6 +226,7 @@ void thread_release()
 {
        HA_ATOMIC_STORE(&isolated_thread, ~0U);
        HA_ATOMIC_DEC(&rdv_requests);
+       th_ctx->lock_level -= 128;
 }
 
 /* Sets up threads, signals and masks, and starts threads 2 and above.