]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: channel: implement a zero-copy buffer transfer
authorWilly Tarreau <w@1wt.eu>
Mon, 8 Dec 2014 17:14:53 +0000 (18:14 +0100)
committerWilly Tarreau <w@1wt.eu>
Wed, 24 Dec 2014 22:47:33 +0000 (23:47 +0100)
bi_swpbuf() swaps the buffer passed in argument with the one attached to
the channel, but only if this last one is empty. The idea is to avoid a
copy when buffers can simply be swapped.

include/proto/channel.h
src/channel.c

index 1cee05a1d58a6782348cf5b141bcb9b2e05794c1..e38e3754a1dcc2832d7c61540277d37706e8322b 100644 (file)
@@ -43,6 +43,7 @@ unsigned long long __channel_forward(struct channel *chn, unsigned long long byt
 
 /* SI-to-channel functions working with buffers */
 int bi_putblk(struct channel *chn, const char *str, int len);
+struct buffer *bi_swpbuf(struct channel *chn, struct buffer *buf);
 int bi_putchr(struct channel *chn, char c);
 int bo_inject(struct channel *chn, const char *msg, int len);
 int bo_getline(struct channel *chn, char *str, int len);
index 2f983960db867a018dd8e7256d2ae93d4f32ab2a..04301fba2b0c364933c9cdb9419a42d255fb214c 100644 (file)
@@ -196,6 +196,51 @@ int bi_putblk(struct channel *chn, const char *blk, int len)
        return len;
 }
 
+/* Tries to copy the whole buffer <buf> into the channel's buffer after length
+ * controls. It will only succeed if the target buffer is empty, in which case
+ * it will simply swap the buffers. The buffer not attached to the channel is
+ * returned so that the caller can store it locally. The chn->buf->o and
+ * to_forward pointers are updated. If the output buffer is a dummy buffer or
+ * if it still contains data <buf> is returned, indicating that nothing could
+ * be done. Channel flag READ_PARTIAL is updated if some data can be transferred.
+ * The chunk's length is updated with the number of bytes sent. On errors, NULL
+ * is returned. Note that only buf->i is considered.
+ */
+struct buffer *bi_swpbuf(struct channel *chn, struct buffer *buf)
+{
+       struct buffer *old;
+
+       if (unlikely(channel_input_closed(chn)))
+               return NULL;
+
+       if (!chn->buf->size || !buffer_empty(chn->buf)) {
+               chn->flags |= CF_WAKE_WRITE;
+               return buf;
+       }
+
+       old = chn->buf;
+       chn->buf = buf;
+
+       if (!buf->i)
+               return old;
+
+       chn->total += buf->i;
+
+       if (chn->to_forward) {
+               unsigned long fwd = buf->i;
+               if (chn->to_forward != CHN_INFINITE_FORWARD) {
+                       if (fwd > chn->to_forward)
+                               fwd = chn->to_forward;
+                       chn->to_forward -= fwd;
+               }
+               b_adv(chn->buf, fwd);
+       }
+
+       /* notify that some data was read from the SI into the buffer */
+       chn->flags |= CF_READ_PARTIAL;
+       return old;
+}
+
 /* Gets one text line out of a channel's buffer from a stream interface.
  * Return values :
  *   >0 : number of bytes read. Includes the \n if present before len or end.