MAJOR: session: only allocate buffers when needed

A session doesn't need buffers all the time, especially when they're
empty. With this patch, we don't allocate buffers anymore when the
session is initialized, we only allocate them in two cases :

  - during process_session()
  - during I/O operations

During process_session(), we try hard to allocate both buffers at once
so that we know for sure that a started operation can complete. Indeed,
a previous version of this patch used to allocate one buffer at a time,
but it can result in a deadlock when all buffers are allocated for
requests for example, and there's no buffer left to emit error responses.
Here, if any of the buffers cannot be allocated, the whole operation is
cancelled and the session is added at the tail of the buffer wait queue.

At the end of process_session(), a call to session_release_buffers() is
done so that we can offer unused buffers to other sessions waiting for
them.

For I/O operations, we only need to allocate a buffer on the Rx path.
For this, we only allocate a single buffer but ensure that at least two
are available to avoid the deadlock situation. In case buffers are not
available, SI_FL_WAIT_ROOM is set on the stream interface and the session
is queued. Unused buffers resulting either from a successful send() or
from an unused read buffer are offered to pending sessions during the
->wake() callback.
diff --git a/src/session.c b/src/session.c
index d236859..fc5b886 100644
--- a/src/session.c
+++ b/src/session.c
@@ -516,12 +516,6 @@
 	s->rep->wex = TICK_ETERNITY;
 	s->rep->analyse_exp = TICK_ETERNITY;
 
-	if (unlikely(b_alloc(&s->req->buf) == NULL))
-		goto out_free_rep; /* no memory */
-
-	if (unlikely(b_alloc(&s->rep->buf) == NULL))
-		goto out_free_req_buf; /* no memory */
-
 	txn = &s->txn;
 	/* Those variables will be checked and freed if non-NULL in
 	 * session.c:session_free(). It is important that they are
@@ -550,7 +544,7 @@
 		 * finished (=0, eg: monitoring), in both situations,
 		 * we can release everything and close.
 		 */
-		goto out_free_rep_buf;
+		goto out_free_rep;
 	}
 
 	/* if logs require transport layer information, note it on the connection */
@@ -568,10 +562,6 @@
 	return 1;
 
 	/* Error unrolling */
- out_free_rep_buf:
-	b_free(&s->rep->buf);
- out_free_req_buf:
-	b_free(&s->req->buf);
  out_free_rep:
 	pool_free2(pool2_channel, s->rep);
  out_free_req:
@@ -1804,6 +1794,16 @@
 			goto update_exp_and_leave;
 	}
 
+	/* below we may emit error messages so we have to ensure that we have
+	 * our buffers properly allocated.
+	 */
+	if (!session_alloc_buffers(s)) {
+		/* No buffer available, we've been subscribed to the list of
+		 * buffer waiters, let's wait for our turn.
+		 */
+		goto update_exp_and_leave;
+	}
+
 	/* 1b: check for low-level errors reported at the stream interface.
 	 * First we check if it's a retryable error (in which case we don't
 	 * want to tell the buffer). Otherwise we report the error one level
@@ -2602,6 +2602,7 @@
 		if ((si_applet_call(s->req->cons) | si_applet_call(s->rep->cons)) != 0) {
 			if (task_in_rq(t)) {
 				t->expire = TICK_ETERNITY;
+				session_release_buffers(s);
 				return t;
 			}
 		}
@@ -2631,6 +2632,7 @@
 		if (!tick_isset(t->expire))
 			ABORT_NOW();
 #endif
+		session_release_buffers(s);
 		return t; /* nothing more to do */
 	}