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.
diff --git a/include/proto/applet.h b/include/proto/applet.h
index 00ad63b..d664fd9 100644
--- a/include/proto/applet.h
+++ b/include/proto/applet.h
@@ -34,9 +34,8 @@
 
 struct task *task_run_applet(struct task *t, void *context, unsigned short state);
 
+int appctx_buf_available(void *arg);
 
-static int inline appctx_res_wakeup(struct appctx *appctx);
-
 
 /* Initializes all required fields for a new appctx. Note that it does the
  * minimum acceptable initialization for an appctx. This means only the
@@ -75,7 +74,7 @@
 		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;
@@ -118,30 +117,9 @@
 /* wakes up an applet when conditions have changed */
 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 */
 
 /*
diff --git a/src/applet.c b/src/applet.c
index 6911ab4..69e13c5 100644
--- a/src/applet.c
+++ b/src/applet.c
@@ -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;