]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: applet: Add support for async appctx startup on a thread subset
authorChristopher Faulet <cfaulet@haproxy.com>
Mon, 16 May 2022 15:15:31 +0000 (17:15 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Tue, 17 May 2022 14:13:22 +0000 (16:13 +0200)
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
src/applet.c

index 0bba054df737659b1f4dca7ea9d30efd06c6612e..937fdbc757eb90c24b2c1f0a92dda77646f1db16 100644 (file)
@@ -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;
index 3c5ffb256943d8863c34b951eb2b54a4d3b0654e..42d46f83f96303815e80d69d42ebe0ce2f8c87a8 100644 (file)
@@ -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.