[Stackless-checkins] r51707 - stackless/sandbox/examples/stacklesssocket.py

andrew.dalke python-checkins at python.org
Tue Sep 5 00:06:42 CEST 2006


Author: andrew.dalke
Date: Tue Sep  5 00:06:42 2006
New Revision: 51707

Modified:
   stackless/sandbox/examples/stacklesssocket.py
Log:
Split the dispatcher into two parts.  New one is 'stacklesssocket'.

Why?  GC problems.  The old scheme required explict .close() calls
because asyncore's global socket_map kept a bound method from each
dispatcher.  This meant the dispatcher was never gc'ed if it was
no longer in use elsewhere.

The new scheme has stacklesssocket as a facade to dispatcher.
When the stacklesssocket gets a __del__ it forces the dispatcher
to close if not already closed.

Removed all explicit close statements from the test code.




Modified: stackless/sandbox/examples/stacklesssocket.py
==============================================================================
--- stackless/sandbox/examples/stacklesssocket.py	(original)
+++ stackless/sandbox/examples/stacklesssocket.py	Tue Sep  5 00:06:42 2006
@@ -46,19 +46,6 @@
 getaddrinfo = stdsocket.getaddrinfo
 
 
-# socket._fileobject does not forward close() to the underlying
-# socket.  That behavior is needed so gc'ed _fileobjects get
-# correctly removed from the asyncore dispatcher.
-class _stackless_fileobject(stdsocket._fileobject):
-    def close(self):
-        try:
-            if self._sock:
-                self.flush()
-                self._sock.close()
-        finally:
-            self._sock = None
-
-
 managerRunning = False
 
 def ManageSockets():
@@ -76,20 +63,51 @@
     global managerRunning
 
     currentSocket = stdsocket.socket(family, type, proto)
-    ret = dispatcher(currentSocket)
+    ret = stacklesssocket(currentSocket)
     # Ensure that the sockets actually work.
     if not managerRunning:
         managerRunning = True
         stackless.tasklet(ManageSockets)()
     return ret
 
+# This is a facade to the dispatcher object.
+# It exists because asyncore's socket map keeps a bound reference to
+# the dispatcher and hence the dispatcher will never get gc'ed.
+#
+# The rest of the world sees a 'stacklesssocket' which has no cycles
+# and will be gc'ed correctly
+
+class stacklesssocket(object):
+    def __init__(self, sock):
+        self.sock = sock
+        self.dispatcher = dispatcher(sock)
+
+    def __getattr__(self, name):
+        # Forward nearly everything to the dispatcher
+        if not name.startswith("__"):
+            # I don't like forwarding __repr__
+            return getattr(self.dispatcher, name)
+
+    def __del__(self):
+        # Close dispatcher if it isn't already closed
+        if self.dispatcher._fileno is not None:
+            try:
+                self.dispatcher.close()
+            finally:
+                self.dispatcher = None
+
+    # Catch this one here to make gc work correctly.
+    # (Consider if stacklesssocket gets gc'ed before the _fileobject)
+    def makefile(self, mode='r', bufsize=-1):
+        return stdsocket._fileobject(self, mode, bufsize)
+
+
 class dispatcher(asyncore.dispatcher):
     def __init__(self, sock):
         # This is worth doing.  I was passing in an invalid socket which was
         # an instance of dispatcher and it was causing tasklet death.
         if not isinstance(sock, stdsocket.socket):
             raise StandardError("Invalid socket passed to dispatcher")
-
         asyncore.dispatcher.__init__(self, sock)
 
         self.acceptChannel = stackless.channel()
@@ -178,13 +196,12 @@
     # In order for incoming connections to be stackless compatible,
     # they need to be wrapped by an asyncore based dispatcher subclass.
     def wrap_accept_socket(self, currentSocket):
-        return dispatcher(currentSocket)
+        return stacklesssocket(currentSocket)
 
+    # asyncore doesn't support this.  Why not?
     def fileno(self):
         return self.socket.fileno()
 
-    def makefile(self, mode='r', bufsize=-1):
-        return _stackless_fileobject(self, mode, bufsize)
 
 if __name__ == '__main__':
     import struct
@@ -202,9 +219,6 @@
         listenSocket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
         listenSocket.bind(address)
         listenSocket.listen(5)
-        # We will have to manually close the listening socket, at
-        # which point it should be the last one left and we want the
-        # socket manager tasklet to exit then.
 
         NUM_TESTS = 2
 
@@ -224,15 +238,12 @@
                 print "server test", i, "recv"
                 if currentSocket.recv(4) != "":
                     print "server recv(1)", i, "FAIL"
-                    currentSocket.close()
                     break
                 # multiple empty recvs are fine
                 if currentSocket.recv(4) != "":
                     print "server recv(2)", i, "FAIL"
-                    currentSocket.close()
                     break
 
-                currentSocket.close()
             else:
                 currentSocket.close()
 
@@ -244,7 +255,6 @@
         else:
             print "server: OK", i
 
-        listenSocket.close()
         print "Done server"
 
     def TestClientConnections(address):
@@ -258,7 +268,6 @@
             print "client test", 1, "FAIL"
         else:
             print "client test", 1, "OK"
-        clientSocket.close()
 
         # Attempt 2:
         clientSocket = socket()
@@ -270,7 +279,6 @@
             print "client test", 2, "OK"
         else:
             print "client test", 2, "FAIL"
-        clientSocket.close()
 
     def TestMonkeyPatch(uri):
         # replace the system socket with this module
@@ -278,7 +286,7 @@
         sys.modules["socket"] = __import__(__name__)
         import urllib  # must occur after monkey-patching!
         f = urllib.urlopen(uri)
-        if not isinstance(f.fp._sock, dispatcher):
+        if not isinstance(f.fp._sock, stacklesssocket):
             raise AssertionError("failed to apply monkeypatch")
         s = f.read()
         if len(s) != 0:

_______________________________________________
Stackless-checkins mailing list
Stackless-checkins at stackless.com
http://www.stackless.com/mailman/listinfo/stackless-checkins



More information about the Stackless-checkins mailing list