[Checkins] SVN: zc.ngi/trunk/ Refactored handle_write to:

Jim Fulton jim at zope.com
Thu May 26 16:19:07 EDT 2011


Log message for revision 121823:
  Refactored handle_write to:
  
  - Gather input for send calls
  
  - Handle errors more cleanly.
  

Changed:
  U   zc.ngi/trunk/README.txt
  U   zc.ngi/trunk/src/zc/ngi/async.py

-=-
Modified: zc.ngi/trunk/README.txt
===================================================================
--- zc.ngi/trunk/README.txt	2011-05-26 16:12:31 UTC (rev 121822)
+++ zc.ngi/trunk/README.txt	2011-05-26 20:19:07 UTC (rev 121823)
@@ -20,6 +20,15 @@
 *******
 
 ====================
+2.0.0a6 (2011-05-26)
+====================
+
+Bugs Fixed
+
+- If application code made many small writes, each write was sent
+  individually, which could trigger Nagle's algorithm.
+
+====================
 2.0.0a5 (2010-08-19)
 ====================
 

Modified: zc.ngi/trunk/src/zc/ngi/async.py
===================================================================
--- zc.ngi/trunk/src/zc/ngi/async.py	2011-05-26 16:12:31 UTC (rev 121822)
+++ zc.ngi/trunk/src/zc/ngi/async.py	2011-05-26 20:19:07 UTC (rev 121823)
@@ -337,54 +337,68 @@
         if __debug__:
             self.logger.debug('handle_write_event')
 
-        while self.__output:
-            output = self.__output
-            v = output[0]
-            if v is zc.ngi.END_OF_DATA:
-                self.close()
-                return
-
-            if not isinstance(v, str):
-                # Must be an iterator
-                try:
-                    v = v.next()
-                    if not isinstance(v, str):
-                        raise TypeError(
-                            "writelines iterator must return strings",
-                            v)
-                except StopIteration:
-                    # all done
+        tosend = []
+        nsend = 0
+        send_size = 60000
+        output = self.__output
+        try:
+            while output:
+                v = output[0]
+                if v is zc.ngi.END_OF_DATA:
+                    if not nsend:
+                        self.close()
+                        return
+                    send_size = 0
+                elif isinstance(v, str):
+                    tosend.append(v)
+                    nsend += len(v)
                     output.pop(0)
-                    continue
-                except Exception, v:
-                    self.logger.exception("writelines iterator failed")
-                    if self.__handler is None:
-                        self.__iterator_exception = v
+                else:
+                    # Must be an iterator
+                    try:
+                        v = v.next()
+                        if not isinstance(v, str):
+                            raise TypeError(
+                                "writelines iterator must return strings", v)
+                    except StopIteration:
+                        # all done
+                        output.pop(0)
+                    except Exception, v:
+                        self.logger.exception("writelines iterator failed")
+                        if self.__handler is None:
+                            self.__iterator_exception = v
+                        else:
+                            self.__handler.handle_exception(self._connection, v)
                         raise
                     else:
-                        self.__handler.handle_exception(self._connection, v)
+                        tosend.append(v)
+                        nsend += len(v)
 
-                output.insert(0, v)
+                if output and nsend < send_size:
+                    continue
 
-            if not v:
-                output.pop(0)
-                continue
+                v = ''.join(tosend)
+                try:
+                    n = self.send(v)
+                except socket.error, err:
+                    if err[0] in expected_socket_write_errors:
+                        return # we couldn't write anything
+                    raise
+                except Exception, v:
+                    self.logger.exception("send failed")
+                    raise
 
-            try:
-                n = self.send(v)
-            except socket.error, err:
-                if err[0] in expected_socket_write_errors:
-                    return # we couldn't write anything
-                raise
-            except Exception, v:
-                self.logger.exception("send failed")
-                raise
+                if n == nsend:
+                    nsend = 0
+                    del tosend[:]
+                else:
+                    nsend -= n
+                    tosend[:] = v[n:],
+                    return # can't send any more
+        finally:
+            if nsend:
+                self.output[0:0] = tosend
 
-            if n == len(v):
-                output.pop(0)
-            else:
-                output[0] = v[n:]
-                return # can't send any more
 
     def handle_close(self, reason='end of input'):
         if __debug__:



More information about the checkins mailing list