From 1c13cfa6ca09c20c10b4233a56a02a7a7caeda07 Mon Sep 17 00:00:00 2001 From: "Enrico Weigelt, metux IT consult" Date: Wed, 30 Jul 2025 16:53:55 +0200 Subject: [PATCH] os: io: factor out making room in output buffer First step for simplifying the output path - this is really complicated now: FlushClient() is called in two cases: a) we really need to send out critical data (eg. critical events now), here we have no extra data b) going to write new data in the output buffer, but it's already full here we do have extra data In case b) (only called from WriteToClient()) we're first trying to write out as much as we can, and if there's still not enough room, the buffer is resized. The write-out path is a complex look trying to write buffered data first, then the new data. That's even more complex since using writev() with 3 pieces (old buffer, new data, padding), and considering each of those could be written just partially. By the way, there's really no need that the new data is strictly written along with the already buffered one - practically that's not even any actual performance optimization - so it's just making things unncessarily complicated. Therefore reduce it to what's really needed: ensure enough room in the output buffer (and potentially flush out or resize the buffer). In a later, remove the whole extra data part from FlushClient(), as it's not needed anymore. Signed-off-by: Enrico Weigelt, metux IT consult --- os/io.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/os/io.c b/os/io.c index f61f6bb9c..eda8fa791 100644 --- a/os/io.c +++ b/os/io.c @@ -60,6 +60,7 @@ SOFTWARE. #ifdef WIN32 #include #endif +#include #include #include "os/Xtrans.h" #include @@ -669,6 +670,47 @@ AbortClient(ClientPtr client) } } +/* + * try to make room in the output buffer: + * if not enough room, try to flush first. + * if that's not giving enough room, increase the buffer size + */ +static bool OutputBufferMakeRoom(ClientPtr who, OsCommPtr oc, size_t sz) +{ + const size_t padsize = padding_for_int32(sz); + const size_t needed = sz + padsize; + + ConnectionOutputPtr oco = oc->output; + + /* check whether it already fits into buffer */ + if (oco->count + needed <= oco->size) + return true; + + /* try flushing the buffer */ + int ret = FlushClient(who, oc, NULL, 0); + if (ret == -1) /* client was aborted */ + return false; + + /* does it fit this time ? */ + if (oco->count + needed <= oco->size) + return true; + + /* try to resize the buffer */ + const int newsize = oco->count + (((needed / BUFSIZE)+1)*BUFSIZE); + + void *newbuf = realloc(oco->buf, newsize); + if (!newbuf) { + AbortClient(who); + dixMarkClientException(who); + oco->count = 0; + return FALSE; + } + + oco->buf = newbuf; + oco->size = newsize; + return true; +} + /***************** * WriteToClient * Copies buf into ClientPtr.buf if it fits (with padding), else @@ -799,10 +841,12 @@ WriteToClient(ClientPtr who, int count, const void *__buf) CriticalOutputPending = FALSE; NewOutputPending = FALSE; } - - return FlushClient(who, oc, buf, count); } + /* if we fail to make room, the client will be aborted */ + if (!OutputBufferMakeRoom(who, oc, count)) + return -1; + NewOutputPending = TRUE; output_pending_mark(who); memmove((char *) oco->buf + oco->count, buf, count);