From 91f9e3a3dd6619683b29e6df2d80a30e57b1e391 Mon Sep 17 00:00:00 2001 From: Olivier Houchard Date: Thu, 16 Apr 2026 18:01:52 +0200 Subject: [PATCH] MINOR: tasks: Introduce __task_set_state_and_tid Introduce a new function, __task_set_state_and_tid, that atomically can set a task's state and its tid. This will be used later, as the tid will be used to indicate task ownership even for shared tasks. --- include/haproxy/task.h | 57 ++++++++++++++++++++++++++++++++++++++++++ src/task.c | 4 +++ 2 files changed, 61 insertions(+) diff --git a/include/haproxy/task.h b/include/haproxy/task.h index 419d7749f..c5c0c1eb6 100644 --- a/include/haproxy/task.h +++ b/include/haproxy/task.h @@ -205,6 +205,63 @@ static inline uint64_t task_mono_time(void) return th_ctx->sched_call_date; } +#if !defined(HA_CAS_IS_8B) && !defined(HA_HAVE_CAS_DW) +__decl_thread(extern HA_SPINLOCK_T task_state_tid); +#endif + +static inline int __task_set_state_and_tid(struct task *t, int expected_tid, int new_tid, unsigned int current, unsigned int wanted) +{ +#if defined(HA_CAS_IS_8B) || defined(HA_HAVE_CAS_DW) + uint64_t expected_value; + uint64_t new_value; + +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + expected_value = ((uint64_t)(current) << 32) | (uint32_t)expected_tid; +#else + expected_value = current | ((uint64_t)expected_tid << 32); +#endif + do { + int tid_seen; + +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + tid_seen = (expected_value & 0xffffffff); + if (tid_seen != expected_tid) + return 0; + if ((expected_value >> 32) != current) + return 0; + new_value = ((uint64_t)wanted << 32) | (uint32_t)new_tid; + +#else + tid_seen = (expected_value >> 32); + if (tid_seen != expected_tid) + return 0; + if ((expected_value & 0xffffffff) != current) + return 0; + new_value = wanted | ((uint64_t)new_tid << 32); +#endif +#if defined(HA_CAS_IS_8B) + } while (!HA_ATOMIC_CAS((uint64_t *)&t->state, &expected_value, new_value) && __ha_cpu_relax()); +#elif defined(HA_HAVE_CAS_DW) + } while (!HA_ATOMIC_DWCAS((uint64_t *)&t->state, &expected_value, &new_value) && __ha_cpu_relax()); +#endif + return 1; +#else /* !HA_CAS_IS_8B && !HA_HAVE_CAS_DW */ + int old_state; + int ret = 0; + + HA_SPIN_LOCK(OTHER_LOCK, &task_state_tid); + if (_HA_ATOMIC_LOAD(&t->tid) == expected_tid) { + old_state = _HA_ATOMIC_LOAD(&t->state); + if (old_state == current && HA_ATOMIC_CAS(&t->state, &old_state, wanted)) { + _HA_ATOMIC_STORE(&t->tid, new_tid); + ret = 1; + } + } + HA_SPIN_UNLOCK(OTHER_LOCK, &task_state_tid); + return ret; +#endif +} + /* puts the task in run queue with reason flags , and returns */ /* This will put the task in the local runqueue if the task is only runnable * by the current thread, in the global runqueue otherwies. With DEBUG_TASK, diff --git a/src/task.c b/src/task.c index 8d1bfdff1..0d866ddd9 100644 --- a/src/task.c +++ b/src/task.c @@ -47,6 +47,10 @@ static struct { int sched_stuck THREAD_ALIGNED(); } sched_ctx[MAX_THREADS]; +#if !defined(HA_CAS_IS_8B) && !defined(HA_HAVE_CAS_DW) +__decl_thread(HA_SPINLOCK_T task_state_tid); +#endif + /* Flags the task for immediate destruction and puts it into its first * thread's shared tasklet list if not yet queued/running. This will bypass * the priority scheduling and make the task show up as fast as possible in