MEDIUM: appctx: check for allocation attempts in buffer allocation callbacks

The buffer allocation callback appctx_res_wakeup() used to rely on old
tricks to detect if a buffer was already granted to an appctx, namely
by checking the task's state. Not only this test is not valid anymore,
but it's inaccurate.

Let's solely on SI_FL_WAIT_ROOM that is now set on allocation failure by
the functions trying to allocate a buffer. The buffer is now allocated on
the fly and the flag removed so that the consistency between the two
remains granted. The patch also fixes minor issues such as the function
being improperly declared inline(!) and the fact that using appctx_wakeup()
sets the wakeup reason to TASK_WOKEN_OTHER while we try to use TASK_WOKEN_RES
when waking up consecutive to a ressource allocation such as a buffer.
This commit is contained in:
Willy Tarreau 2018-11-06 17:32:37 +01:00
parent b882dd88cc
commit 21028b5e7f
2 changed files with 28 additions and 24 deletions

View File

@ -34,8 +34,7 @@ extern unsigned int nb_applets;
struct task *task_run_applet(struct task *t, void *context, unsigned short state);
static int inline appctx_res_wakeup(struct appctx *appctx);
int appctx_buf_available(void *arg);
/* Initializes all required fields for a new appctx. Note that it does the
@ -75,7 +74,7 @@ static inline struct appctx *appctx_new(struct applet *applet, unsigned long thr
appctx->t->context = appctx;
LIST_INIT(&appctx->buffer_wait.list);
appctx->buffer_wait.target = appctx;
appctx->buffer_wait.wakeup_cb = (int (*)(void *))appctx_res_wakeup;
appctx->buffer_wait.wakeup_cb = appctx_buf_available;
HA_ATOMIC_ADD(&nb_applets, 1);
}
return appctx;
@ -121,27 +120,6 @@ static inline void appctx_wakeup(struct appctx *appctx)
task_wakeup(appctx->t, TASK_WOKEN_OTHER);
}
/* Callback used to wake up an applet when a buffer is available. The applet
* <appctx> is woken up is if it is not already in the list of "active"
* applets. This functions returns 1 is the stream is woken up, otherwise it
* returns 0. If task is running we request we check if woken was already
* requested */
static inline int appctx_res_wakeup(struct appctx *appctx)
{
int ret;
/* To detect if we have already been waken or not, we now that
* if the state contains TASK_RUNNING, but not just TASK_RUNNING.
* This is racy, but that's OK. At worst we will wake a little more
* tasks than necessary when a buffer is available.
*/
ret = ((appctx->state & TASK_RUNNING) != 0) &&
((appctx->state != TASK_RUNNING));
task_wakeup(appctx->t, TASK_WOKEN_OTHER);
return ret;
}
#endif /* _PROTO_APPLET_H */
/*

View File

@ -23,6 +23,32 @@
unsigned int nb_applets = 0;
/* Callback used to wake up an applet when a buffer is available. The applet
* <appctx> is woken up if an input buffer was requested for the associated
* stream interface. In this case the buffer is immediately allocated and the
* function returns 1. Otherwise it returns 0. Note that this automatically
* covers multiple wake-up attempts by ensuring that the same buffer will not
* be accounted for multiple times.
*/
int appctx_buf_available(void *arg)
{
struct appctx *appctx = arg;
struct stream_interface *si = appctx->owner;
/* allocation requested ? */
if (!(si->flags & SI_FL_WAIT_ROOM) || c_size(si_ic(si)) || si_ic(si)->pipe)
return 0;
/* allocation possible now ? */
if (!b_alloc_margin(&si_ic(si)->buf, global.tune.reserved_bufs))
return 0;
si->flags &= ~SI_FL_WAIT_ROOM;
task_wakeup(appctx->t, TASK_WOKEN_RES);
return 1;
}
/* Default applet handler */
struct task *task_run_applet(struct task *t, void *context, unsigned short state)
{
struct appctx *app = context;