]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: session: implement a basic atomic buffer allocator
authorWilly Tarreau <w@1wt.eu>
Tue, 25 Nov 2014 18:46:36 +0000 (19:46 +0100)
committerWilly Tarreau <w@1wt.eu>
Wed, 24 Dec 2014 22:47:32 +0000 (23:47 +0100)
This patch introduces session_alloc_recv_buffer(), session_alloc_buffers()
and session_release_buffers() whose purpose will be to allocate missing
buffers and release unneeded ones around the process_session() and during
I/O operations.

I/O callbacks only need a single buffer for recv operations and none
for send. However we still want to ensure that we don't pick the last
buffer. That's what session_alloc_recv_buffer() is for.

This allocator is atomic in that it always ensures we can get 2 buffers
or fails. Here, if any of the buffers is not ready and cannot be
allocated, the operation is cancelled. The purpose is to guarantee that
we don't enter into the deadlock where all buffers are allocated by the
same size of all sessions.

A queue will have to be implemented for failed allocations. For now
they're just reported as failures.

include/proto/session.h
src/session.c

index 2389a79934ac35ef9a08dbd4c37b766247d4f2bf..e9b72bff096e3c596062067e889034c6243f5562 100644 (file)
@@ -53,6 +53,9 @@ int parse_track_counters(char **args, int *arg,
 
 /* Update the session's backend and server time stats */
 void session_update_time_stats(struct session *s);
+int session_alloc_buffers(struct session *s);
+void session_release_buffers(struct session *s);
+int session_alloc_recv_buffer(struct session *s, struct buffer **buf);
 
 /* returns the session from a void *owner */
 static inline struct session *session_from_task(struct task *t)
index fbeb9cf4afb17f24c120e73ac78d15812c60fbbf..fdd99ba956493f0863f2920e4c1c08e49e2f4906 100644 (file)
@@ -675,6 +675,62 @@ static void session_free(struct session *s)
        }
 }
 
+/* Allocates a single buffer for session <s>, but only if it's guaranteed that
+ * it's not the last available buffer. To be called at the beginning of recv()
+ * callbacks to ensure that the required buffers are properly allocated.
+ * Returns 0 in case of failure, non-zero otherwise.
+ */
+int session_alloc_recv_buffer(struct session *s, struct buffer **buf)
+{
+       struct buffer *b;
+
+       b = b_alloc_margin(buf, 2);
+       if (b)
+               return 1;
+
+       /* FIXME: normally we're supposed to subscribe to a list of waiters
+        * for buffers. We release what we failed to allocate.
+        */
+       return 0;
+}
+
+/* Allocates up to two buffers for session <s>. Only succeeds if both buffers
+ * are properly allocated. It is meant to be called inside process_session() so
+ * that both request and response buffers are allocated. Returns 0 incase of
+ * failure, non-zero otherwise.
+ */
+int session_alloc_buffers(struct session *s)
+{
+       if (!s->req->buf->size && !b_alloc(&s->req->buf))
+               return 0;
+
+       if (s->rep->buf->size || b_alloc(&s->rep->buf))
+               return 1;
+
+       if (buffer_empty(s->req->buf)) {
+               __b_drop(&s->req->buf);
+               s->req->buf = &buf_wanted;
+       }
+
+       /* FIXME: normally we're supposed to subscribe to a list of waiters
+        * for buffers. We release what we failed to allocate.
+        */
+       return 0;
+}
+
+/* releases unused buffers after processing. Typically used at the end of the
+ * update() functions.
+ */
+void session_release_buffers(struct session *s)
+{
+       if (s->req->buf->size && buffer_empty(s->req->buf))
+               b_free(&s->req->buf);
+
+       if (s->rep->buf->size && buffer_empty(s->rep->buf))
+               b_free(&s->rep->buf);
+
+       /* FIXME: normally we want to wake up pending tasks */
+}
 
 /* perform minimal intializations, report 0 in case of error, 1 if OK. */
 int init_session()