Added Dan's fd/socket fix.
[fedora-mingw.git] / gtk-vnc / gtk-vnc-dan-fd-fix.patch
diff --git a/gtk-vnc/gtk-vnc-dan-fd-fix.patch b/gtk-vnc/gtk-vnc-dan-fd-fix.patch
new file mode 100644 (file)
index 0000000..86b2b1a
--- /dev/null
@@ -0,0 +1,136 @@
+I managed to reproduce the problem you saw with gtk-vnc and windows
+hanging. With the glib IO debug turned up high I can see it is treating
+the FD as a file handle, rather than a socket - which is to be expected
+because gnulib is wrapping winsock to give back real FDs.
+
+Unfortunately glib is on crack, and for file handles it implements
+IO waits by having a background thread for each file handle that sits
+in a read/write call forever, and sets flags. To quote the docs
+
+  "If you have created a GIOChannel for a file descriptor and started 
+   watching (polling) it, you shouldn't call read() on the file descriptor.
+   This is because adding polling for a file descriptor is implemented in 
+   GLib on Windows by starting a thread that sits blocked in a read() from
+   the file descriptor most of the time. All reads from the file descriptor 
+   should be done by this internal GLib thread. Your code should call only 
+   g_io_channel_read(). "
+
+This is just epic fail when combined with what we want todo. Making gtk-vnc
+use g_io_channel_read() instead didn't work either. I think the glib thread
+concept simply doesn't work for FDs which are aliases for sockets.
+
+So I've tried a atch which creates an IO channel using 
+
+  g_io_channel_win32_new_socket(_get_osfhandle(fd))
+
+to explicitly make glib treat it as a socket, and avoid the thread insanity.
+Slightly nasty because we're reversing gnulibs magic by translating the
+FD back into a socket, but empirically it seems to work better for me. I've
+not yet got it  to hang with this patch.
+
+I also needed to make our read/write functions use the 'fd' in the gvnc
+struct, instead of fetching it from the IOChannel object.
+
+Let me know if this patch works for you too....
+
+Daniel
+
+diff --git a/src/gvnc.c b/src/gvnc.c
+--- a/src/gvnc.c
++++ b/src/gvnc.c
+@@ -347,7 +347,6 @@ static int gvnc_zread(struct gvnc *gvnc,
+ static int gvnc_read(struct gvnc *gvnc, void *data, size_t len)
+ {
+-      int fd = g_io_channel_unix_get_fd(gvnc->channel);
+       char *ptr = data;
+       size_t offset = 0;
+@@ -380,7 +379,7 @@ static int gvnc_read(struct gvnc *gvnc, 
+                                       ret = -1;
+                               }
+                       } else
+-                              ret = recv (fd, gvnc->read_buffer, 4096, 0);
++                              ret = recv (gvnc->fd, gvnc->read_buffer, 4096, 0);
+                       if (ret == -1) {
+                               switch (errno) {
+@@ -422,7 +421,6 @@ static int gvnc_read(struct gvnc *gvnc, 
+ static void gvnc_flush(struct gvnc *gvnc)
+ {
+-      int fd = g_io_channel_unix_get_fd(gvnc->channel);
+       size_t offset = 0;
+       while (offset < gvnc->write_offset) {
+               int ret;
+@@ -439,7 +437,7 @@ static void gvnc_flush(struct gvnc *gvnc
+                               ret = -1;
+                       }
+               } else
+-                      ret = send (fd,
++                      ret = send (gvnc->fd,
+                                   gvnc->write_buffer+offset,
+                                   gvnc->write_offset-offset, 0);
+               if (ret == -1) {
+@@ -490,11 +488,10 @@ static ssize_t gvnc_tls_push(gnutls_tran
+                             const void *data,
+                             size_t len) {
+       struct gvnc *gvnc = (struct gvnc *)transport;
+-      int fd = g_io_channel_unix_get_fd(gvnc->channel);
+       int ret;
+  retry:
+-      ret = write(fd, data, len);
++      ret = write(gvnc->fd, data, len);
+       if (ret < 0) {
+               if (errno == EINTR)
+                       goto retry;
+@@ -508,11 +505,10 @@ static ssize_t gvnc_tls_pull(gnutls_tran
+                            void *data,
+                            size_t len) {
+       struct gvnc *gvnc = (struct gvnc *)transport;
+-      int fd = g_io_channel_unix_get_fd(gvnc->channel);
+       int ret;
+  retry:
+-      ret = read(fd, data, len);
++      ret = read(gvnc->fd, data, len);
+       if (ret < 0) {
+               if (errno == EINTR)
+                       goto retry;
+@@ -2872,7 +2868,13 @@ gboolean gvnc_open_fd(struct gvnc *gvnc,
+       if (!gvnc_set_nonblock(fd))
+               return FALSE;
+-      if (!(gvnc->channel = g_io_channel_unix_new(fd))) {
++      if (!(gvnc->channel =
++#ifdef WIN32
++            g_io_channel_win32_new_socket(_get_osfhandle(fd))
++#else
++            g_io_channel_unix_new(fd)
++#endif
++            )) {
+               GVNC_DEBUG ("Failed to g_io_channel_unix_new()\n");
+               return FALSE;
+       }
+@@ -2917,7 +2919,13 @@ gboolean gvnc_open_host(struct gvnc *gvn
+               if (!gvnc_set_nonblock(fd))
+                       break;
+-                if (!(chan = g_io_channel_unix_new(fd))) {
++                if (!(chan =
++#ifdef WIN32
++                    g_io_channel_win32_new_socket(_get_osfhandle(fd))
++#else
++                    g_io_channel_unix_new(fd)
++#endif
++                    )) {
+                         close(fd);
+                         GVNC_DEBUG ("Failed to g_io_channel_unix_new()\n");
+                         break;
+
+-- 
+|: Red Hat, Engineering, London   -o-   http://people.redhat.com/berrange/ :|
+|: http://libvirt.org  -o-  http://virt-manager.org  -o-  http://ovirt.org :|
+|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
+|: GnuPG: 7D3B9505  -o-  F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|