[Xcb] [PATCH libxcb 2/3] Prevent event waiters from blocking reply waiters.

Rami Ylimäki rami.ylimaki at vincit.fi
Thu Oct 7 08:43:23 PDT 2010


Concurrent calls to xcb_wait_for_event and xcb_wait_for_reply may make
xcb_wait_for_reply block needlessly until an event arrives.

Signed-off-by: Rami Ylimäki <rami.ylimaki at vincit.fi>
---
 src/xcb_conn.c |   12 ++++++++++--
 src/xcb_in.c   |   56 ++++++++++++++++++++++++++++++++++++++++++++++++++------
 src/xcbint.h   |    1 +
 3 files changed, 61 insertions(+), 8 deletions(-)

diff --git a/src/xcb_conn.c b/src/xcb_conn.c
index 1d37614..e99234c 100644
--- a/src/xcb_conn.c
+++ b/src/xcb_conn.c
@@ -269,6 +269,15 @@ void _xcb_conn_shutdown(xcb_connection_t *c)
     c->has_error = 1;
 }
 
+/**
+ * Check if someone is already reading from a connection or is just
+ * about to start reading.
+ */
+static int permit_reading(xcb_connection_t *c)
+{
+    return !c->in.reading && !c->in.prioritized_readers;
+}
+
 int _xcb_conn_wait(xcb_connection_t *c, pthread_cond_t *cond, struct iovec **vector, int *count)
 {
     int ret;
@@ -279,7 +288,7 @@ int _xcb_conn_wait(xcb_connection_t *c, pthread_cond_t *cond, struct iovec **vec
 #endif
 
     /* If the thing I should be doing is already being done, wait for it. */
-    if(count ? c->out.writing : c->in.reading)
+    if(count ? c->out.writing : !permit_reading(c))
     {
         pthread_cond_wait(cond, &c->iolock);
         return 1;
@@ -294,7 +303,6 @@ int _xcb_conn_wait(xcb_connection_t *c, pthread_cond_t *cond, struct iovec **vec
     FD_SET(c->fd, &rfds);
 #endif
     ++c->in.reading;
-
 #if USE_POLL
     if(count)
     {
diff --git a/src/xcb_in.c b/src/xcb_in.c
index 8cfc5c9..7b83da6 100644
--- a/src/xcb_in.c
+++ b/src/xcb_in.c
@@ -358,6 +358,21 @@ static int poll_for_reply(xcb_connection_t *c, unsigned int request, void **repl
 
 /* Public interface */
 
+/**
+ * If an event reader gave priority to reply reader, the reply reader
+ * can use this function to give the event reader a permission to
+ * continue.
+ */
+static void permit_event_reader(xcb_connection_t *c)
+{
+    if (c->in.prioritized_readers)
+    {
+        int pthreadret = pthread_cond_signal(&c->in.event_cond);
+        c->in.prioritized_readers = 0;
+        assert(pthreadret == 0);
+    }
+}
+
 void *xcb_wait_for_reply(xcb_connection_t *c, unsigned int request, xcb_generic_error_t **e)
 {
     uint64_t widened_request;
@@ -379,6 +394,7 @@ void *xcb_wait_for_reply(xcb_connection_t *c, unsigned int request, xcb_generic_
         pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
         reader_list reader;
         reader_list **prev_reader;
+        int status = 1;
 
         for(prev_reader = &c->in.readers; 
 	    *prev_reader && 
@@ -392,9 +408,13 @@ void *xcb_wait_for_reply(xcb_connection_t *c, unsigned int request, xcb_generic_
         reader.next = *prev_reader;
         *prev_reader = &reader;
 
-        while(!poll_for_reply(c, request, &ret, e))
-            if(!_xcb_conn_wait(c, &cond, 0, 0))
-                break;
+        /* Wait for response from the connection. If an event reader
+         * has given priority to us, we must let it continue. */
+        while(status && !poll_for_reply(c, request, &ret, e))
+        {
+            status = _xcb_conn_wait(c, &cond, 0, 0);
+            permit_event_reader(c);
+        }
 
         for(prev_reader = &c->in.readers;
 	    *prev_reader && 
@@ -542,16 +562,39 @@ int xcb_poll_for_reply(xcb_connection_t *c, unsigned int request, void **reply,
     return ret;
 }
 
+/**
+ * If an event reader received a reply instead of an event, it can
+ * wake up next reply reader to check that reply with this
+ * function. Otherwise an event reader may cause the reply reader to
+ * block forever if events don't arrive.
+ */
+static void permit_reply_reader(xcb_connection_t *c)
+{
+    if (c->in.readers)
+    {
+        int pthreadret = pthread_cond_signal(c->in.readers->data);
+        c->in.prioritized_readers = 1;
+        assert(pthreadret == 0);
+    }
+}
+
 xcb_generic_event_t *xcb_wait_for_event(xcb_connection_t *c)
 {
     xcb_generic_event_t *ret;
     if(c->has_error)
         return 0;
     pthread_mutex_lock(&c->iolock);
+
     /* get_event returns 0 on empty list. */
-    while(!(ret = get_event(c)))
-        if(!_xcb_conn_wait(c, &c->in.event_cond, 0, 0))
-            break;
+    ret = get_event(c);
+    /* Wait for response from the connection. If the response wasn't
+     * an event, let a reply reader get it. Otherwise the reply reader
+     * could block until an event arrives. */
+    while(!ret && _xcb_conn_wait(c, &c->in.event_cond, 0, 0))
+    {
+        if (!(ret = get_event(c)))
+            permit_reply_reader(c);
+    }
 
     wake_up_next_reader(c);
     pthread_mutex_unlock(&c->iolock);
@@ -613,6 +656,7 @@ int _xcb_in_init(_xcb_in *in)
     if(pthread_cond_init(&in->event_cond, 0))
         return 0;
     in->reading = 0;
+    in->prioritized_readers = 0;
 
     in->queue_len = 0;
 
diff --git a/src/xcbint.h b/src/xcbint.h
index 154cca0..122c8c9 100644
--- a/src/xcbint.h
+++ b/src/xcbint.h
@@ -115,6 +115,7 @@ int _xcb_out_flush_to(xcb_connection_t *c, uint64_t request);
 typedef struct _xcb_in {
     pthread_cond_t event_cond;
     int reading;
+    int prioritized_readers;
 
     char queue[4096];
     int queue_len;
-- 
1.6.3.3



More information about the Xcb mailing list