From: Christopher Faulet Date: Fri, 10 Jul 2026 09:53:33 +0000 (+0200) Subject: BUG/MEDIUM: applet: Reenable reads in applet context if requesting a connection X-Git-Url: http://git.kaiwu.me/web/doc/static/gitweb.js?a=commitdiff_plain;h=16d259c5db02997e2da47d77bb4f71fa09197c89;p=haproxy.git BUG/MEDIUM: applet: Reenable reads in applet context if requesting a connection When a applet is waiting for a connection, like Socket applet in lua, reads are blocked. When the connection is finally available , a read activity is reported and reads are reenabled (flag SE_FL_APPLET_NEED_CONN is removed). However, most of time (always ?), this operation is performed in the context of the connection and the applet is woken up. It means the task expiration date is not updated where the read activity is reported. It is an issue for small timeouts because the task may be queued in past, triggering a BUG_ON in sc_notify(). In fact, a read activity must never be reported in another context of the endpoint itself or the upper stream, specifically to properly set the task expiration date. To fix the issue, in sc_chk_rcv(), the applet is only woken up in that case and the reads are re-enabled when the applet is executed. For applets using the new API, it is performed in sc_appet_sync_recv(). For legacy applets, it is directly performed in task_run_applet(). This patch is related to #3442. It must be backported as far as 2.8. On the 2.8, only task_run_applet must be fixed because sc_applet_sync_recv() does not exist. --- diff --git a/src/applet.c b/src/applet.c index 63f2654c0..59969fc1c 100644 --- a/src/applet.c +++ b/src/applet.c @@ -826,6 +826,12 @@ struct task *task_run_applet(struct task *t, void *context, unsigned int state) applet_need_more_data(app); applet_have_no_more_data(app); + if (sc_ep_test(sc, SE_FL_APPLET_NEED_CONN) && + sc_state_in(sco->state, SC_SB_RDY|SC_SB_EST|SC_SB_DIS|SC_SB_CLO)) { + sc_ep_clr(sc, SE_FL_APPLET_NEED_CONN); + sc_ep_report_read_activity(sc); + } + /* Now we'll try to allocate the input buffer. We wake up the applet in * all cases. So this is the applet's responsibility to check if this * buffer was allocated or not. This leaves a chance for applets to do diff --git a/src/stconn.c b/src/stconn.c index 6e30b3851..8dcdd4970 100644 --- a/src/stconn.c +++ b/src/stconn.c @@ -717,8 +717,11 @@ void sc_chk_rcv(struct stconn *sc) if (sc_ep_test(sc, SE_FL_APPLET_NEED_CONN) && sc_state_in(sc_opposite(sc)->state, SC_SB_RDY|SC_SB_EST|SC_SB_DIS|SC_SB_CLO)) { - sc_ep_clr(sc, SE_FL_APPLET_NEED_CONN); - sc_ep_report_read_activity(sc); + /* connection available (or closed), so wake applet up to handle + * the event. here we are not in the applet context (but most + * probably in the connection context). + */ + appctx_wakeup(__sc_appctx(sc)); } if (!sc_is_recv_allowed(sc)) @@ -2024,6 +2027,12 @@ int sc_applet_sync_recv(struct stconn *sc) if (!appctx_app_test(__sc_appctx(sc), APPLET_FL_NEW_API)) return 0; + if (sc_ep_test(sc, SE_FL_APPLET_NEED_CONN) && + sc_state_in(sc_opposite(sc)->state, SC_SB_RDY|SC_SB_EST|SC_SB_DIS|SC_SB_CLO)) { + sc_ep_clr(sc, SE_FL_APPLET_NEED_CONN); + sc_ep_report_read_activity(sc); + } + if (!sc_state_in(sc->state, SC_SB_RDY|SC_SB_EST)) return 0;