From 836aa11908097e148b2d269fc7c341ef07826bbe Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Tue, 25 Nov 2014 19:46:36 +0100 Subject: MEDIUM: session: implement a basic atomic buffer allocator This patch introduces session_alloc_one_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_one_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 | 3 +++ src/session.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/include/proto/session.h b/include/proto/session.h index 2389a79..907e32d 100644 --- a/include/proto/session.h +++ b/include/proto/session.h @@ -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_one_buffer(struct session *s, struct buffer **buf); /* returns the session from a void *owner */ static inline struct session *session_from_task(struct task *t) diff --git a/src/session.c b/src/session.c index fbeb9cf..f7644f5 100644 --- a/src/session.c +++ b/src/session.c @@ -675,6 +675,57 @@ static void session_free(struct session *s) } } +/* Allocates a single buffer for session , but only if it's guaranteed that + * it's not the last available buffer. To be called at the beginning of I/O + * callbacks to ensure that the required buffers are properly allocated. + * Returns 0 in case of failure, non-zero otherwise. + */ +int session_alloc_one_buffer(struct session *s, struct buffer **buf) +{ + struct buffer *b; + + b = b_alloc_single(buf); + 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 . 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) +{ + int ret; + + ret = b_alloc_pair(&s->req->buf, &s->rep->buf); + if (ret) + return ret; + + /* 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() -- 1.7.12.1