summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Harris <pharris@opentext.com>2021-02-01 19:45:28 -0500
committerPeter Harris <pharris@opentext.com>2021-06-04 14:31:13 +0000
commit21414e7c447f18224c577ed5e32bd5d6e45c44f9 (patch)
tree0002140c8f51e0cf23d186f6f6a3ec362586addd
parent4b0d9d3868aad8d5f4266821e9eda586e6c2bfa7 (diff)
downloadxcb-libxcb-21414e7c447f18224c577ed5e32bd5d6e45c44f9.tar.gz
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.c57
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;
}