generator: support listelement-ref
[free-sw/xcb/libxcb] / src / xcb_out.c
index fbce7a0..dc42954 100644 (file)
 
 /* Stuff that sends stuff to the server. */
 
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
 #include <assert.h>
 #include <stdlib.h>
 #include <unistd.h>
 #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 +59,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)
@@ -74,6 +103,33 @@ static void get_socket_back(xcb_connection_t *c)
     _xcb_in_replies_done(c);
 }
 
+static void prepare_socket_request(xcb_connection_t *c)
+{
+    /* We're about to append data to out.queue, so we need to
+     * atomically test for an external socket owner *and* some other
+     * thread currently writing.
+     *
+     * If we have an external socket owner, we have to get the socket back
+     * before we can use it again.
+     *
+     * If some other thread is writing to the socket, we assume it's
+     * writing from out.queue, and so we can't stick data there.
+     *
+     * We satisfy this condition by first calling get_socket_back
+     * (which may drop the lock, but will return when XCB owns the
+     * socket again) and then checking for another writing thread and
+     * escaping the loop if we're ready to go.
+     */
+    for (;;) {
+        if(c->has_error)
+            return;
+        get_socket_back(c);
+        if (!c->out.writing)
+            break;
+        pthread_cond_wait(&c->out.cond, &c->iolock);
+    }
+}
+
 /* Public interface */
 
 void xcb_prefetch_maximum_request_length(xcb_connection_t *c)
@@ -123,16 +179,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;
 
@@ -156,7 +204,7 @@ unsigned int xcb_send_request(xcb_connection_t *c, int flags, struct iovec *vect
             const xcb_query_extension_reply_t *extension = xcb_get_extension_data(c, req->ext);
             if(!(extension && extension->present))
             {
-                _xcb_conn_shutdown(c);
+                _xcb_conn_shutdown(c, XCB_CONN_CLOSED_EXT_NOTSUPPORTED);
                 return 0;
             }
             ((uint8_t *) vector[0].iov_base)[0] = extension->major_opcode;
@@ -186,14 +234,22 @@ unsigned int xcb_send_request(xcb_connection_t *c, int flags, struct iovec *vect
         }
         else if(longlen > xcb_get_maximum_request_length(c))
         {
-            _xcb_conn_shutdown(c);
+            _xcb_conn_shutdown(c, XCB_CONN_CLOSED_REQ_LEN_EXCEED);
             return 0; /* server can't take this; maybe need BIGREQUESTS? */
         }
 
         /* 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;
 
@@ -207,52 +263,45 @@ unsigned int xcb_send_request(xcb_connection_t *c, int flags, struct iovec *vect
 
     /* get a sequence number and arrange for delivery. */
     pthread_mutex_lock(&c->iolock);
-    /* wait for other writing threads to get out of my way. */
-    while(c->out.writing)
-        pthread_cond_wait(&c->out.cond, &c->iolock);
-    get_socket_back(c);
 
-    request = ++c->out.request;
+    prepare_socket_request(c);
+
     /* 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
      * 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)
+     * an error in sending the request
+     */
+     
+    while ((req->isvoid && c->out.request == c->in.request_expected + (1 << 16) - 2) ||
+           (unsigned int) (c->out.request + 1) == 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;
+        send_sync(c);
+        prepare_socket_request(c);
     }
 
-    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];
-    }
+    send_request(c, req->isvoid, workaround, flags, vector, veclen);
+    request = c->has_error ? 0 : c->out.request;
+    pthread_mutex_unlock(&c->iolock);
+    return request;
+}
 
-    if(!write_block(c, vector, veclen))
-    {
-        _xcb_conn_shutdown(c);
-        request = 0;
+void
+xcb_send_fd(xcb_connection_t *c, int fd)
+{
+#if HAVE_SENDMSG
+    if (c->has_error)
+        return;
+    pthread_mutex_lock(&c->iolock);
+    while (c->out.out_fd.nfd == XCB_MAX_PASS_FD) {
+        _xcb_out_flush_to(c, c->out.request);
+        if (c->has_error)
+            break;
     }
+    if (!c->has_error)
+        c->out.out_fd.fd[c->out.out_fd.nfd++] = fd;
     pthread_mutex_unlock(&c->iolock);
-    return request;
+#endif
 }
 
 int xcb_take_socket(xcb_connection_t *c, void (*return_socket)(void *closure), void *closure, int flags, uint64_t *sent)
@@ -262,7 +311,13 @@ int xcb_take_socket(xcb_connection_t *c, void (*return_socket)(void *closure), v
         return 0;
     pthread_mutex_lock(&c->iolock);
     get_socket_back(c);
-    ret = _xcb_out_flush_to(c, c->out.request);
+
+    /* _xcb_out_flush may drop the iolock allowing other threads to
+     * write requests, so keep flushing until we're done
+     */
+    do
+        ret = _xcb_out_flush_to(c, c->out.request);
+    while (ret && c->out.request != c->out.request_written);
     if(ret)
     {
         c->out.return_socket = return_socket;
@@ -342,6 +397,12 @@ int _xcb_out_send(xcb_connection_t *c, struct iovec *vector, int count)
     return ret;
 }
 
+void _xcb_out_send_sync(xcb_connection_t *c)
+{
+    prepare_socket_request(c);
+    send_sync(c);
+}
+
 int _xcb_out_flush_to(xcb_connection_t *c, uint64_t request)
 {
     assert(XCB_SEQUENCE_COMPARE(request, <=, c->out.request));