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.
This commit is contained in:
Christopher Faulet 2022-05-16 17:15:31 +02:00
parent 6095d57701
commit d9c1d33fa1
2 changed files with 29 additions and 4 deletions

View 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;

View 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.