[PATCH] Fix a busy loop on BSD

Uli Schlachter psychon at znc.in
Thu Mar 1 11:26:39 PST 2012


On FreeBSD MSG_WAITALL on a non-blocking socket fails immediately if less bytes
than were asked for are available. This is different than the behavior on linux
where as many bytes as are available are returned in this case.

_xcb_in_read() is used to fill xcb's read buffer, thus this function will call
recv() with a big length argument (xcb's read buffer is by default 16 KiB
large).

This means that _xcb_in_read() always failed on FreeBSD. Since the socket was
still signaled as readable by poll(), this bug even resulted in a busy loop.

The same issue is present in read_block(), but here it is slightly different.
read_block() is called when we read the first few bytes of an event or a reply,
so that we already know its length. This means that we should be able to use
MSG_WAITALL here, because we know how many bytes there have to be.

However, that function could busy loop, too, when only the first few bytes of
the packet were sent while the rest is stuck somewhere on the way to us. Thus,
MSG_WAITALL should be removed here, too.

(Alternatively the socket could be temporarily be made blocking, but that could
interfere with other threads trying to write at the same time. Since
read_block() should normally only ever need a single recv() call to get its
whole packet, the risk isn't worth it and the syscall overhead for making the fd
blocking and non-blocking might be too high, too)

Thanks to Christoph Egger from Debian for noticing the problem, doing all the
necessary debugging and figuring out what the problem was!

This bug was introduced in commit 2dcf8b025be88a25d4333abdc28d425b88238d96.

Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=45776

Signed-off-by: Uli Schlachter <psychon at znc.in>
---
 src/xcb_in.c |    9 ++-------
 1 files changed, 2 insertions(+), 7 deletions(-)

diff --git a/src/xcb_in.c b/src/xcb_in.c
index 969cfc0..4998cdd 100644
--- a/src/xcb_in.c
+++ b/src/xcb_in.c
@@ -51,11 +51,6 @@
 #define XCB_REPLY 1
 #define XCB_XGE_EVENT 35
 
-/* required for compiling for Win32 using MinGW */
-#ifndef MSG_WAITALL
-#define MSG_WAITALL 0
-#endif
-
 struct event_list {
     xcb_generic_event_t *event;
     struct event_list *next;
@@ -269,7 +264,7 @@ static int read_block(const int fd, void *buf, const ssize_t len)
     int done = 0;
     while(done < len)
     {
-        int ret = recv(fd, ((char *) buf) + done, len - done,MSG_WAITALL);
+        int ret = recv(fd, ((char *) buf) + done, len - done, 0);
         if(ret > 0)
             done += ret;
 #ifndef _WIN32
@@ -661,7 +656,7 @@ void _xcb_in_replies_done(xcb_connection_t *c)
 
 int _xcb_in_read(xcb_connection_t *c)
 {
-    int n = recv(c->fd, c->in.queue + c->in.queue_len, sizeof(c->in.queue) - c->in.queue_len,MSG_WAITALL);
+    int n = recv(c->fd, c->in.queue + c->in.queue_len, sizeof(c->in.queue) - c->in.queue_len, 0);
     if(n > 0)
         c->in.queue_len += n;
     while(read_packet(c))
-- 
1.7.9


--------------030703030705060402090807--


More information about the Xcb mailing list