From d9c1d33fa10769fef2aa60b5562f705ed27115e3 Mon Sep 17 00:00:00 2001 From: Christopher Faulet Date: Mon, 16 May 2022 17:15:31 +0200 Subject: [PATCH] MEDIUM: applet: Add support for async appctx startup on a thread subset It is now possible to start an appctx on a thread subset. Some controls were added here and there. It is forbidden to start a backend appctx on another thread than the local one. If a frontend appctx is started on another thread or a thread subset, the applet .init callback function must be defined. This callback function is responsible to finalize the appctx startup. It can be performed synchornously. In this case, the appctx is started on the local thread. It is not really useful but it is valid. Or it can be performed asynchronously. In this case, .init callback function is called when the appctx is woken up for the first time. When this happens, the appctx affinity is set to the current thread to be able to start the session and the stream. --- include/haproxy/applet.h | 7 +++++++ src/applet.c | 26 ++++++++++++++++++++++---- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/include/haproxy/applet.h b/include/haproxy/applet.h index 0bba054df7..937fdbc757 100644 --- a/include/haproxy/applet.h +++ b/include/haproxy/applet.h @@ -64,6 +64,13 @@ static inline struct appctx *appctx_new_anywhere(struct applet *applet, struct c */ static inline int appctx_init(struct appctx *appctx) { + /* Set appctx affinity to the current thread. Because, after this call, + * the appctx will be fully initialized. The session and the stream will + * eventually be created. The affinity must be set now ! + */ + BUG_ON((appctx->t->thread_mask & tid_bit) == 0); + task_set_affinity(appctx->t, tid_bit); + if (appctx->applet->init) return appctx->applet->init(appctx); return 0; diff --git a/src/applet.c b/src/applet.c index 3c5ffb2569..42d46f83f9 100644 --- a/src/applet.c +++ b/src/applet.c @@ -35,8 +35,8 @@ struct appctx *appctx_new(struct applet *applet, struct cs_endpoint *endp, unsig { struct appctx *appctx; - /* Disable the feature for now ! */ - BUG_ON(thread_mask != tid_bit); + /* Backend appctx cannot be started on another thread than the local one */ + BUG_ON(thread_mask != tid_bit && endp); appctx = pool_zalloc(pool_head_appctx); if (unlikely(!appctx)) @@ -91,7 +91,10 @@ int appctx_finalize_startup(struct appctx *appctx, struct proxy *px, struct buff { struct session *sess; - BUG_ON(appctx->sess || !(appctx->endp->flags & CS_EP_ORPHAN)); + /* async startup is only possible for frontend appctx. Thus for orphan + * appctx. Because no backend appctx can be orphan. + */ + BUG_ON(!(appctx->endp->flags & CS_EP_ORPHAN)); sess = session_new(px, NULL, &appctx->obj_type); if (!sess) @@ -188,7 +191,7 @@ int appctx_buf_available(void *arg) struct task *task_run_applet(struct task *t, void *context, unsigned int state) { struct appctx *app = context; - struct conn_stream *cs = appctx_cs(app); + struct conn_stream *cs; unsigned int rate; size_t count; @@ -197,6 +200,21 @@ struct task *task_run_applet(struct task *t, void *context, unsigned int state) return NULL; } + if (app->endp->flags & CS_EP_ORPHAN) { + /* Finalize init of orphan appctx. .init callback function must + * be defined and it must finalize appctx startup. + */ + BUG_ON(!app->applet->init); + + if (appctx_init(app) == -1) { + appctx_free_on_early_error(app); + return NULL; + } + BUG_ON(!app->sess || !appctx_cs(app) || !appctx_strm(app)); + } + + cs = appctx_cs(app); + /* We always pretend the applet can't get and doesn't want to * put, it's up to it to change this if needed. This ensures * that one applet which ignores any event will not spin. -- 2.39.5