Fix a multi-thread deadlock
[free-sw/xcb/libxcb] / src / xcb_conn.c
index 7979491..6a7a806 100644 (file)
@@ -36,6 +36,7 @@
 #include <stdlib.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <limits.h>
 
 #include "xcb.h"
 #include "xcbint.h"
@@ -209,7 +210,11 @@ static int write_vec(xcb_connection_t *c, struct iovec **vector, int *count)
          i++;
     }
 #else
-    n = writev(c->fd, *vector, *count);
+    n = *count;
+    if (n > IOV_MAX)
+       n = IOV_MAX;
+
+    n = writev(c->fd, *vector, n);
     if(n < 0 && errno == EAGAIN)
         return 1;
 #endif /* _WIN32 */    
@@ -427,10 +432,20 @@ int _xcb_conn_wait(xcb_connection_t *c, pthread_cond_t *cond, struct iovec **vec
 
     if(ret)
     {
+        /* The code allows two threads to call select()/poll() at the same time.
+         * First thread just wants to read, a second thread wants to write, too.
+         * We have to make sure that we don't steal the reading thread's reply
+         * and let it get stuck in select()/poll().
+         * So a thread may read if either:
+         * - There is no other thread that wants to read (the above situation
+         *   did not occur).
+         * - It is the reading thread (above situation occurred).
+         */
+        int may_read = c->in.reading == 1 || !count;
 #if USE_POLL
-        if((fd.revents & POLLIN) == POLLIN)
+        if(may_read && (fd.revents & POLLIN) == POLLIN)
 #else
-        if(FD_ISSET(c->fd, &rfds))
+        if(may_read && FD_ISSET(c->fd, &rfds))
 #endif
             ret = ret && _xcb_in_read(c);