Make socket writes non-blockable. This allows the system to more quickly
return back to the Xorg main loop, meaning that things will be more responsive
in the presence of slow VNC clients.


git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@4735 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/unix/xserver/hw/vnc/XserverDesktop.cc b/unix/xserver/hw/vnc/XserverDesktop.cc
index 9afbb49..9c5b20b 100644
--- a/unix/xserver/hw/vnc/XserverDesktop.cc
+++ b/unix/xserver/hw/vnc/XserverDesktop.cc
@@ -581,6 +581,7 @@
         if (FD_ISSET(listener->getFd(), fds)) {
           FD_CLR(listener->getFd(), fds);
           Socket* sock = listener->accept();
+          sock->outStream().setBlocking(false);
           server->addSocket(sock);
           vlog.debug("new client, sock %d",sock->getFd());
         }
@@ -590,6 +591,7 @@
         if (FD_ISSET(httpListener->getFd(), fds)) {
           FD_CLR(httpListener->getFd(), fds);
           Socket* sock = httpListener->accept();
+          sock->outStream().setBlocking(false);
           httpServer->addSocket(sock);
           vlog.debug("new http client, sock %d",sock->getFd());
         }
@@ -632,6 +634,78 @@
   }
 }
 
+void XserverDesktop::writeBlockHandler(fd_set* fds)
+{
+  try {
+    std::list<Socket*> sockets;
+    std::list<Socket*>::iterator i;
+
+    server->getSockets(&sockets);
+    for (i = sockets.begin(); i != sockets.end(); i++) {
+      int fd = (*i)->getFd();
+      if ((*i)->isShutdown()) {
+        vlog.debug("client gone, sock %d",fd);
+        server->removeSocket(*i);
+        vncClientGone(fd);
+        delete (*i);
+      } else {
+        if ((*i)->outStream().bufferUsage() > 0)
+          FD_SET(fd, fds);
+      }
+    }
+
+    if (httpServer) {
+      httpServer->getSockets(&sockets);
+      for (i = sockets.begin(); i != sockets.end(); i++) {
+        int fd = (*i)->getFd();
+        if ((*i)->isShutdown()) {
+          vlog.debug("http client gone, sock %d",fd);
+          httpServer->removeSocket(*i);
+          delete (*i);
+        } else {
+          if ((*i)->outStream().bufferUsage() > 0)
+            FD_SET(fd, fds);
+        }
+      }
+    }
+  } catch (rdr::Exception& e) {
+    vlog.error("XserverDesktop::writeBlockHandler: %s",e.str());
+  }
+}
+
+void XserverDesktop::writeWakeupHandler(fd_set* fds, int nfds)
+{
+  if (nfds < 1)
+    return;
+
+  try {
+    std::list<Socket*> sockets;
+    std::list<Socket*>::iterator i;
+
+    server->getSockets(&sockets);
+    for (i = sockets.begin(); i != sockets.end(); i++) {
+      int fd = (*i)->getFd();
+      if (FD_ISSET(fd, fds)) {
+        FD_CLR(fd, fds);
+        (*i)->outStream().flush();
+      }
+    }
+
+    if (httpServer) {
+      httpServer->getSockets(&sockets);
+      for (i = sockets.begin(); i != sockets.end(); i++) {
+        int fd = (*i)->getFd();
+        if (FD_ISSET(fd, fds)) {
+          FD_CLR(fd, fds);
+          (*i)->outStream().flush();
+        }
+      }
+    }
+  } catch (rdr::Exception& e) {
+    vlog.error("XserverDesktop::writeWakeupHandler: %s",e.str());
+  }
+}
+
 void XserverDesktop::addClient(Socket* sock, bool reverse)
 {
   vlog.debug("new client, sock %d reverse %d",sock->getFd(),reverse);
diff --git a/unix/xserver/hw/vnc/XserverDesktop.h b/unix/xserver/hw/vnc/XserverDesktop.h
index 1c03705..af36511 100644
--- a/unix/xserver/hw/vnc/XserverDesktop.h
+++ b/unix/xserver/hw/vnc/XserverDesktop.h
@@ -72,6 +72,8 @@
   void ignoreHooks(bool b) { ignoreHooks_ = b; }
   void blockHandler(fd_set* fds);
   void wakeupHandler(fd_set* fds, int nfds);
+  void writeBlockHandler(fd_set* fds);
+  void writeWakeupHandler(fd_set* fds, int nfds);
   void addClient(network::Socket* sock, bool reverse);
   void disconnectClients();
 
diff --git a/unix/xserver/hw/vnc/vncExtInit.cc b/unix/xserver/hw/vnc/vncExtInit.cc
index d3cfbe2..baa8fa4 100644
--- a/unix/xserver/hw/vnc/vncExtInit.cc
+++ b/unix/xserver/hw/vnc/vncExtInit.cc
@@ -21,6 +21,7 @@
 #endif
 
 #include <stdio.h>
+#include <errno.h>
 
 extern "C" {
 #define class c_class
@@ -28,6 +29,7 @@
 #define NEED_EVENTS
 #include <X11/X.h>
 #include <X11/Xproto.h>
+#include <X11/Xpoll.h>
 #include "misc.h"
 #include "os.h"
 #include "dixstruct.h"
@@ -63,6 +65,8 @@
   static void vncResetProc(ExtensionEntry* extEntry);
   static void vncBlockHandler(pointer data, OSTimePtr t, pointer readmask);
   static void vncWakeupHandler(pointer data, int nfds, pointer readmask);
+  void vncWriteBlockHandler(fd_set *fds);
+  void vncWriteWakeupHandler(int nfds, fd_set *fds);
   static void vncClientStateChange(CallbackListPtr*, pointer, pointer);
   static void SendSelectionChangeEvent(Atom selection);
   static int ProcVncExtDispatch(ClientPtr client);
@@ -287,6 +291,9 @@
   SendSelectionChangeEvent(selection->selection);
 }
 
+static void vncWriteBlockHandlerFallback(OSTimePtr timeout);
+static void vncWriteWakeupHandlerFallback();
+
 //
 // vncBlockHandler - called just before the X server goes into select().  Call
 // on to the block handler for each desktop.  Then check whether any of the
@@ -297,6 +304,8 @@
 {
   fd_set* fds = (fd_set*)readmask;
 
+  vncWriteBlockHandlerFallback(timeout);
+
   for (int scr = 0; scr < screenInfo.numScreens; scr++)
     if (desktop[scr])
       desktop[scr]->blockHandler(fds);
@@ -311,6 +320,85 @@
       desktop[scr]->wakeupHandler(fds, nfds);
     }
   }
+
+  vncWriteWakeupHandlerFallback();
+}
+
+//
+// vncWriteBlockHandler - extra hack to be able to get the main select loop
+// to monitor writeable fds and not just readable. This requirers a modified
+// Xorg and might therefore not be called. When it is called though, it will
+// do so before vncBlockHandler (and vncWriteWakeupHandler called after
+// vncWakeupHandler).
+//
+
+static bool needFallback = true;
+static fd_set fallbackFds;
+static struct timeval tw;
+
+void vncWriteBlockHandler(fd_set *fds)
+{
+  needFallback = false;
+
+  for (int scr = 0; scr < screenInfo.numScreens; scr++)
+    if (desktop[scr])
+      desktop[scr]->writeBlockHandler(fds);
+}
+
+void vncWriteWakeupHandler(int nfds, fd_set *fds)
+{
+  for (int scr = 0; scr < screenInfo.numScreens; scr++) {
+    if (desktop[scr]) {
+      desktop[scr]->writeWakeupHandler(fds, nfds);
+    }
+  }
+}
+
+static void vncWriteBlockHandlerFallback(OSTimePtr timeout)
+{
+  if (!needFallback)
+    return;
+
+  FD_ZERO(&fallbackFds);
+  vncWriteBlockHandler(&fallbackFds);
+  needFallback = true;
+
+  if (!XFD_ANYSET(&fallbackFds))
+    return;
+
+  if ((*timeout == NULL) ||
+      ((*timeout)->tv_sec > 0) || ((*timeout)->tv_usec > 10000)) {
+    tw.tv_sec = 0;
+    tw.tv_usec = 10000;
+    *timeout = &tw;
+  }
+}
+
+static void vncWriteWakeupHandlerFallback()
+{
+  int ret;
+  struct timeval timeout;
+
+  if (!needFallback)
+    return;
+
+  if (!XFD_ANYSET(&fallbackFds))
+    return;
+
+  timeout.tv_sec = 0;
+  timeout.tv_usec = 0;
+
+  ret = select(XFD_SETSIZE, NULL, &fallbackFds, NULL, &timeout);
+  if (ret < 0) {
+    ErrorF("vncWriteWakeupHandlerFallback(): select: %s\n",
+           strerror(errno));
+    return;
+  }
+
+  if (ret == 0)
+    return;
+
+  vncWriteWakeupHandler(ret, &fallbackFds);
 }
 
 static void vncClientStateChange(CallbackListPtr*, pointer, pointer p)
diff --git a/unix/xserver15.patch b/unix/xserver15.patch
index f930777..7d8c94b 100644
--- a/unix/xserver15.patch
+++ b/unix/xserver15.patch
@@ -98,3 +98,61 @@
  #ifdef XIDLE
      if (!noXIdleExtension) XIdleExtensionInit();
  #endif
+--- xserver/os/WaitFor.c.orig	2011-10-07 12:57:57.000000000 +0200
++++ xserver/os/WaitFor.c	2011-10-07 13:21:11.000000000 +0200
+@@ -125,6 +125,9 @@
+ static void CheckAllTimers(void);
+ static OsTimerPtr timers = NULL;
+ 
++extern void vncWriteBlockHandler(fd_set *fds);
++extern void vncWriteWakeupHandler(int nfds, fd_set *fds);
++
+ /*****************
+  * WaitForSomething:
+  *     Make the server suspend until there is
+@@ -150,6 +153,7 @@
+     INT32 timeout = 0;
+     fd_set clientsReadable;
+     fd_set clientsWritable;
++    fd_set socketsWritable;
+     int curclient;
+     int selecterr;
+     int nready;
+@@ -220,23 +224,29 @@
+ 	SmartScheduleStopTimer ();
+ 
+ #endif
++	FD_ZERO(&socketsWritable);
++	vncWriteBlockHandler(&socketsWritable);
+ 	BlockHandler((pointer)&wt, (pointer)&LastSelectMask);
+ 	if (NewOutputPending)
+ 	    FlushAllOutput();
+ 	/* keep this check close to select() call to minimize race */
+ 	if (dispatchException)
+ 	    i = -1;
+-	else if (AnyClientsWriteBlocked)
+-	{
+-	    XFD_COPYSET(&ClientsWriteBlocked, &clientsWritable);
+-	    i = Select (MaxClients, &LastSelectMask, &clientsWritable, NULL, wt);
+-	}
+-	else 
+-	{
+-	    i = Select (MaxClients, &LastSelectMask, NULL, NULL, wt);
++	else {
++	    if (AnyClientsWriteBlocked)
++		XFD_ORSET(&socketsWritable, &ClientsWriteBlocked, &socketsWritable);
++
++	    if (XFD_ANYSET(&socketsWritable)) {
++		i = Select (MaxClients, &LastSelectMask, &socketsWritable, NULL, wt);
++		if (AnyClientsWriteBlocked)
++		    XFD_ANDSET(&clientsWritable, &socketsWritable, &ClientsWriteBlocked);
++	    } else {
++		i = Select (MaxClients, &LastSelectMask, NULL, NULL, wt);
++	    }
+ 	}
+ 	selecterr = GetErrno();
+ 	WakeupHandler(i, (pointer)&LastSelectMask);
++	vncWriteWakeupHandler(i, &socketsWritable);
+ #ifdef SMART_SCHEDULE
+ 	SmartScheduleStartTimer ();
+ #endif