diff options
author | Peter Harris <pharris@opentext.com> | 2021-02-01 19:45:28 -0500 |
---|---|---|
committer | Peter Harris <pharris@opentext.com> | 2021-06-04 14:31:13 +0000 |
commit | 21414e7c447f18224c577ed5e32bd5d6e45c44f9 (patch) | |
tree | 0002140c8f51e0cf23d186f6f6a3ec362586addd | |
parent | 4b0d9d3868aad8d5f4266821e9eda586e6c2bfa7 (diff) |
Fix writev emulation on Windows
There are at least two bugs in the previous implementation:
- If an early iovec is partially written, there can be a gap of missing
data (as a later iovec will be started before the early iovec is
completed).
- If a late iovec returns WSAEWOULDBLOCK, *vector and *count are not
updated, leading to a re-send of the entire request.
Move the *vector update into the send() loop to update piecemeal as
individual iovecs are sent.
Example program that demonstrates the issue (this program should run
forever after these bugs have been fixed):
#include <stdio.h>
#include <stdlib.h>
#include "xcb.h"
// Non-cryptographic random number generator from http://burtleburtle.net/bob/rand/smallprng.html
// because Microsoft's random number generators either have a too small RAND_MAX or are too slow
typedef struct ranctx { uint32_t a; uint32_t b; uint32_t c; uint32_t d; } ranctx;
static uint32_t ranval(ranctx *x);
static void raninit(ranctx *x, uint32_t seed);
#define MAX_PROP_LEN (128 * 1024)
int main(int argc, char *argv[]) {
uint32_t seed = 0x12345678;
if (argc > 1) {
seed = strtoul(argv[1], NULL, 0);
}
ranctx ran;
raninit(&ran, seed);
xcb_connection_t *c = xcb_connect(NULL, NULL);
if (!c || xcb_connection_has_error(c)) {
printf("Cannot connect to $DISPLAY\n");
return 1;
}
const xcb_setup_t *setup = xcb_get_setup(c);
char *buf = malloc(MAX_PROP_LEN + 8); // plus a bit of slack so we can run random values off the end
if (!buf) {
printf("oom\n");
return 1;
}
for (uint32_t i=0; i < (MAX_PROP_LEN + 3) / 4; i++) {
((uint32_t *)buf)[i] = ranval(&ran);
}
xcb_window_t win = xcb_generate_id(c);
xcb_create_window(c, 0, win, xcb_setup_roots_iterator(setup).data[0].root, 0, 0, 1, 1, 0,
XCB_WINDOW_CLASS_INPUT_ONLY, 0, 0, NULL);
printf("Created window 0x%X\n", win);
for (;;) {
xcb_flush(c);
xcb_generic_event_t *ev = xcb_poll_for_event(c);
if (ev) {
if (ev->response_type == 0) {
xcb_generic_error_t *err = (xcb_generic_error_t *)ev;
printf("Unexpected X Error %d\n", err->error_code);
printf(" Sequence %d\n", err->sequence);
printf(" Resource ID 0x%X\n", err->resource_id);
printf(" Opcode: %d.%d\n", err->major_code, err->minor_code);
return 1;
}
printf("Unexpected X Event %d\n", ev->response_type);
return 1;
}
uint32_t siz = ranval(&ran) % MAX_PROP_LEN + 1;
xcb_change_property(c, XCB_PROP_MODE_REPLACE, win, XCB_ATOM_STRING, XCB_ATOM_STRING, 8, siz, buf);
}
return 0;
}
#define rot(x,k) (((x)<<(k))|((x)>>(32-(k))))
static uint32_t ranval(ranctx *x) {
uint32_t e = x->a - rot(x->b, 27);
x->a = x->b ^ rot(x->c, 17);
x->b = x->c + x->d;
x->c = x->d + e;
x->d = e + x->a;
return x->d;
}
static void raninit(ranctx *x, uint32_t seed) {
uint32_t i;
x->a = 0xf1ea5eed, x->b = x->c = x->d = seed;
for (i = 0; i<20; ++i) {
(void)ranval(x);
}
}
Signed-off-by: Peter Harris <pharris@opentext.com>
-rw-r--r-- | src/xcb_conn.c | 57 |
1 files changed, 36 insertions, 21 deletions
diff --git a/src/xcb_conn.c b/src/xcb_conn.c index 158f676..1d2e62f 100644 --- a/src/xcb_conn.c +++ b/src/xcb_conn.c @@ -212,33 +212,47 @@ static int read_setup(xcb_connection_t *c) /* precondition: there must be something for us to write. */ static int write_vec(xcb_connection_t *c, struct iovec **vector, int *count) { +#ifndef _WIN32 int n; +#endif + assert(!c->out.queue_len); #ifdef _WIN32 - int i = 0; - int ret = 0,err = 0; - struct iovec *vec; - n = 0; - /* Could use the WSASend win32 function for scatter/gather i/o but setting up the WSABUF struct from an iovec would require more work and I'm not sure of the benefit....works for now */ - vec = *vector; - while(i < *count) + while (*count) { - ret = send(c->fd,vec->iov_base,vec->iov_len,0); - if(ret == SOCKET_ERROR) - { - err = WSAGetLastError(); - if(err == WSAEWOULDBLOCK) - { - return 1; - } - } - n += ret; - *vec++; - i++; + struct iovec *vec = *vector; + if (vec->iov_len) + { + int ret = send(c->fd, vec->iov_base, vec->iov_len, 0); + if (ret == SOCKET_ERROR) + { + int err = WSAGetLastError(); + if (err == WSAEWOULDBLOCK) + { + return 1; + } + } + if (ret <= 0) + { + _xcb_conn_shutdown(c, XCB_CONN_ERROR); + return 0; + } + c->out.total_written += ret; + vec->iov_len -= ret; + vec->iov_base = (char *)vec->iov_base + ret; + } + if (vec->iov_len == 0) { + (*vector)++; + (*count)--; + } } + + if (!*count) + *vector = 0; + #else n = *count; if (n > IOV_MAX) @@ -280,8 +294,6 @@ static int write_vec(xcb_connection_t *c, struct iovec **vector, int *count) return 1; } -#endif /* _WIN32 */ - if(n <= 0) { _xcb_conn_shutdown(c, XCB_CONN_ERROR); @@ -303,6 +315,9 @@ static int write_vec(xcb_connection_t *c, struct iovec **vector, int *count) if(!*count) *vector = 0; assert(n == 0); + +#endif /* _WIN32 */ + return 1; } |