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>
This commit is contained in:
Peter Harris 2021-02-01 19:45:28 -05:00
parent 4b0d9d3868
commit 21414e7c44

View File

@ -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;
}