From ee1bc1d28a1bda0526db90139edc1304d2ef3d7c Mon Sep 17 00:00:00 2001 From: Jamey Sharp Date: Sat, 9 Oct 2010 04:08:18 -0700 Subject: xcb_send_request: Send all requests using a common internal send_request. This simplifies the critical section of xcb_send_request and fixes a couple of subtle bugs: - It's possible for xcb_send_request to need to issue two sync requests before it can issue the real request. Previously, we counted sequence numbers as if both were issued, but only one went out on the wire. - The test for whether to sync at 32-bit sequence number wrap has been incorrect since we switched to 64-bit sequence numbers internally. This change means that if the output queue was already full and the current request is bigger than the output queue, XCB will do one more write syscall than it did before. But syncs are rare and small requests are the norm, so this shouldn't be a measurable difference. Signed-off-by: Jamey Sharp --- src/xcb_out.c | 99 ++++++++++++++++++++++++++++++----------------------------- 1 file changed, 50 insertions(+), 49 deletions(-) (limited to 'src') diff --git a/src/xcb_out.c b/src/xcb_out.c index fbce7a0..fe71193 100644 --- a/src/xcb_out.c +++ b/src/xcb_out.c @@ -35,8 +35,17 @@ #include "xcbint.h" #include "bigreq.h" -static int write_block(xcb_connection_t *c, struct iovec *vector, int count) +static inline void send_request(xcb_connection_t *c, int isvoid, enum workarounds workaround, int flags, struct iovec *vector, int count) { + if(c->has_error) + return; + + ++c->out.request; + if(!isvoid) + c->in.request_expected = c->out.request; + if(workaround != WORKAROUND_NONE || flags != 0) + _xcb_in_expect_reply(c, c->out.request, workaround, flags); + while(count && c->out.queue_len + vector[0].iov_len <= sizeof(c->out.queue)) { memcpy(c->out.queue + c->out.queue_len, vector[0].iov_base, vector[0].iov_len); @@ -46,13 +55,29 @@ static int write_block(xcb_connection_t *c, struct iovec *vector, int count) ++vector, --count; } if(!count) - return 1; + return; --vector, ++count; vector[0].iov_base = c->out.queue; vector[0].iov_len = c->out.queue_len; c->out.queue_len = 0; - return _xcb_out_send(c, vector, count); + _xcb_out_send(c, vector, count); +} + +static void send_sync(xcb_connection_t *c) +{ + static const union { + struct { + uint8_t major; + uint8_t pad; + uint16_t len; + } fields; + uint32_t packet; + } sync_req = { { /* GetInputFocus */ 43, 0, 1 } }; + struct iovec vector[2]; + vector[1].iov_base = (char *) &sync_req; + vector[1].iov_len = sizeof(sync_req); + send_request(c, 0, WORKAROUND_NONE, XCB_REQUEST_DISCARD_REPLY, vector + 1, 1); } static void get_socket_back(xcb_connection_t *c) @@ -123,16 +148,8 @@ uint32_t xcb_get_maximum_request_length(xcb_connection_t *c) unsigned int xcb_send_request(xcb_connection_t *c, int flags, struct iovec *vector, const xcb_protocol_request_t *req) { - static const union { - struct { - uint8_t major; - uint8_t pad; - uint16_t len; - } fields; - uint32_t packet; - } sync_req = { { /* GetInputFocus */ 43, 0, 1 } }; uint64_t request; - uint32_t prefix[3] = { 0 }; + uint32_t prefix[2]; int veclen = req->count; enum workarounds workaround = WORKAROUND_NONE; @@ -193,7 +210,15 @@ unsigned int xcb_send_request(xcb_connection_t *c, int flags, struct iovec *vect /* set the length field. */ ((uint16_t *) vector[0].iov_base)[1] = shortlen; if(!shortlen) - prefix[2] = ++longlen; + { + prefix[0] = ((uint32_t *) vector[0].iov_base)[0]; + prefix[1] = ++longlen; + vector[0].iov_base = (uint32_t *) vector[0].iov_base + 1; + vector[0].iov_len -= sizeof(uint32_t); + --vector, ++veclen; + vector[0].iov_base = prefix; + vector[0].iov_len = sizeof(prefix); + } } flags &= ~XCB_REQUEST_RAW; @@ -212,45 +237,21 @@ unsigned int xcb_send_request(xcb_connection_t *c, int flags, struct iovec *vect pthread_cond_wait(&c->out.cond, &c->iolock); get_socket_back(c); - request = ++c->out.request; /* send GetInputFocus (sync_req) when 64k-2 requests have been sent without - * a reply. - * Also send sync_req (could use NoOp) at 32-bit wrap to avoid having + * a reply. */ + if(req->isvoid && c->out.request == c->in.request_expected + (1 << 16) - 2) + send_sync(c); + /* Also send sync_req (could use NoOp) at 32-bit wrap to avoid having * applications see sequence 0 as that is used to indicate * an error in sending the request */ - while((req->isvoid && - c->out.request == c->in.request_expected + (1 << 16) - 1) || - request == 0) - { - prefix[0] = sync_req.packet; - _xcb_in_expect_reply(c, request, WORKAROUND_NONE, XCB_REQUEST_DISCARD_REPLY); - c->in.request_expected = c->out.request; - request = ++c->out.request; - } - - if(workaround != WORKAROUND_NONE || flags != 0) - _xcb_in_expect_reply(c, request, workaround, flags); - if(!req->isvoid) - c->in.request_expected = c->out.request; - - if(prefix[0] || prefix[2]) - { - --vector, ++veclen; - if(prefix[2]) - { - prefix[1] = ((uint32_t *) vector[1].iov_base)[0]; - vector[1].iov_base = (uint32_t *) vector[1].iov_base + 1; - vector[1].iov_len -= sizeof(uint32_t); - } - vector[0].iov_len = sizeof(uint32_t) * ((prefix[0] ? 1 : 0) + (prefix[2] ? 2 : 0)); - vector[0].iov_base = prefix + !prefix[0]; - } - - if(!write_block(c, vector, veclen)) - { - _xcb_conn_shutdown(c); - request = 0; - } + if((unsigned int) (c->out.request + 1) == 0) + send_sync(c); + + /* The above send_sync calls could drop the I/O lock, but this + * thread will still exclude any other thread that tries to write, + * so the sequence number postconditions still hold. */ + send_request(c, req->isvoid, workaround, flags, vector, veclen); + request = c->has_error ? 0 : c->out.request; pthread_mutex_unlock(&c->iolock); return request; } -- cgit v1.2.3