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 <info@metux.net>
This commit is contained in:
Enrico Weigelt, metux IT consult
2025-07-30 16:53:55 +02:00
committed by Enrico Weigelt
parent a3a068d6d3
commit 1c13cfa6ca

48
os/io.c
View File

@@ -60,6 +60,7 @@ SOFTWARE.
#ifdef WIN32
#include <X11/Xwinsock.h>
#endif
#include <stdbool.h>
#include <stdio.h>
#include "os/Xtrans.h"
#include <X11/Xmd.h>
@@ -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);