[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