BUG/MINOR: task: do not set TASK_F_USR1 for no reason
authorWilly Tarreau <w@1wt.eu>
Thu, 21 Oct 2021 14:17:29 +0000 (16:17 +0200)
committerWilly Tarreau <w@1wt.eu>
Fri, 25 Feb 2022 14:29:08 +0000 (15:29 +0100)
This applicationn specific flag was added in 2.4-dev by commit 6fa8bcdc7
("MINOR: task: add an application specific flag to the state: TASK_F_USR1")
to help preserve a the idle connections status across wakeup calls. While
the code to do this was OK for tasklets, it was wrong for tasks, as in an
effort not to lose it when setting the RUNNING flag (that tasklets don't
have), it ended up being inconditionally set. It just happens that for now
no regular tasks use it, only tasklets.

This fix makes sure we always atomically perform (state & flags | running)
there, using a CAS. It also does it for tasklets because it was possible
to lose some such flags if set by another thread, even though this should
not happen with current code. In order to make the code more readable (and
avoid the previous mistake of repeated flags in the bit field), a new
TASK_PERSISTENT aggregate was declared in task.h for this.

In practice the CAS is cheap here because task states are stable or
convergent so the loop will almost never be taken.

This should be backported to 2.4.

(cherry picked from commit 3193eb9907dd97d0521d796967958e4716e2ce35)
[wt: minor ctx adjustment]
Signed-off-by: Willy Tarreau <w@1wt.eu>
(cherry picked from commit d9dd842d210b47c67569d66b8774af27f5ebf3c7)
[wt: needed for next fixes and because the aforementioned patch was
 backported; significant context adjustments]
Signed-off-by: Willy Tarreau <w@1wt.eu>

include/haproxy/task-t.h
src/task.c

index 958ab13..56b0aa6 100644 (file)
                            TASK_WOKEN_RES)
 
 #define TASK_F_USR1       0x00008000  /* nature of this task: 0=task 1=tasklet */
+/* unused: 0x10000..0x80000000 */
+
+/* These flags are persistent across scheduler calls */
+#define TASK_PERSISTENT   (TASK_SHARED_WQ | TASK_SELF_WAKING | TASK_KILLED | \
+                           TASK_HEAVY | TASK_F_USR1)
 
 enum {
        TL_URGENT = 0,   /* urgent tasklets (I/O callbacks) */
index 830155d..15fc219 100644 (file)
@@ -453,10 +453,9 @@ unsigned int run_tasks_from_lists(unsigned int budgets[])
                }
 
                budgets[queue]--;
-               t = (struct task *)LIST_ELEM(tl_queues[queue].n, struct tasklet *, list);
-               state = t->state & (TASK_SHARED_WQ|TASK_SELF_WAKING|TASK_HEAVY|TASK_KILLED|TASK_F_USR1);
 
-               if (state & TASK_HEAVY) {
+               t = (struct task *)LIST_ELEM(tl_queues[queue].n, struct tasklet *, list);
+               if (t->state & TASK_HEAVY) {
                        /* This is a heavy task. We'll call no more than one
                         * per function call. If we called one already, we'll
                         * return and announce the max possible weight so that
@@ -471,6 +470,8 @@ unsigned int run_tasks_from_lists(unsigned int budgets[])
 
                ti->flags &= ~TI_FL_STUCK; // this thread is still running
                activity[tid].ctxsw++;
+
+               t = (struct task *)LIST_ELEM(tl_queues[queue].n, struct tasklet *, list);
                ctx = t->context;
                process = t->process;
                t->calls++;
@@ -481,7 +482,8 @@ unsigned int run_tasks_from_lists(unsigned int budgets[])
                if (TASK_IS_TASKLET(t)) {
                        LIST_DEL_INIT(&((struct tasklet *)t)->list);
                        __ha_barrier_store();
-                       state = _HA_ATOMIC_XCHG(&t->state, state);
+
+                       state = _HA_ATOMIC_FETCH_AND(&t->state, TASK_PERSISTENT);
                        __ha_barrier_atomic_store();
                        process(t, ctx, state);
                        done++;
@@ -492,7 +494,11 @@ unsigned int run_tasks_from_lists(unsigned int budgets[])
 
                LIST_DEL_INIT(&((struct tasklet *)t)->list);
                __ha_barrier_store();
-               state = _HA_ATOMIC_XCHG(&t->state, state|TASK_RUNNING|TASK_F_USR1);
+
+               state = t->state;
+               while (!_HA_ATOMIC_CAS(&t->state, &state, (state & TASK_PERSISTENT) | TASK_RUNNING))
+                       ;
+
                __ha_barrier_atomic_store();
 
                /* OK then this is a regular task */