[pypy-commit] pypy default: issue #2064: trying to call socket.close() on all sockets at exit, on Windows

arigo noreply at buildbot.pypy.org
Sun Jun 21 11:38:55 CEST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r78222:ddbc9f97d312
Date: 2015-06-21 11:39 +0200
http://bitbucket.org/pypy/pypy/changeset/ddbc9f97d312/

Log:	issue #2064: trying to call socket.close() on all sockets at exit,
	on Windows

diff --git a/pypy/module/_socket/__init__.py b/pypy/module/_socket/__init__.py
--- a/pypy/module/_socket/__init__.py
+++ b/pypy/module/_socket/__init__.py
@@ -18,6 +18,10 @@
         from rpython.rlib.rsocket import rsocket_startup
         rsocket_startup()
 
+    def shutdown(self, space):
+        from pypy.module._socket.interp_socket import close_all_sockets
+        close_all_sockets(space)
+
     def buildloaders(cls):
         from rpython.rlib import rsocket
         for name in """
diff --git a/pypy/module/_socket/interp_func.py b/pypy/module/_socket/interp_func.py
--- a/pypy/module/_socket/interp_func.py
+++ b/pypy/module/_socket/interp_func.py
@@ -142,7 +142,7 @@
         sock = rsocket.fromfd(fd, family, type, proto)
     except SocketError, e:
         raise converted_error(space, e)
-    return space.wrap(W_Socket(sock))
+    return space.wrap(W_Socket(space, sock))
 
 @unwrap_spec(family=int, type=int, proto=int)
 def socketpair(space, family=rsocket.socketpair_default_family,
@@ -160,8 +160,8 @@
     except SocketError, e:
         raise converted_error(space, e)
     return space.newtuple([
-        space.wrap(W_Socket(sock1)),
-        space.wrap(W_Socket(sock2))
+        space.wrap(W_Socket(space, sock1)),
+        space.wrap(W_Socket(space, sock2))
     ])
 
 # The following 4 functions refuse all negative numbers, like CPython 2.6.
diff --git a/pypy/module/_socket/interp_socket.py b/pypy/module/_socket/interp_socket.py
--- a/pypy/module/_socket/interp_socket.py
+++ b/pypy/module/_socket/interp_socket.py
@@ -1,4 +1,5 @@
-from rpython.rlib import rsocket
+import sys
+from rpython.rlib import rsocket, rweaklist
 from rpython.rlib.rarithmetic import intmask
 from rpython.rlib.rsocket import (
     RSocket, AF_INET, SOCK_STREAM, SocketError, SocketErrorWithErrno,
@@ -153,8 +154,9 @@
 
 
 class W_Socket(W_Root):
-    def __init__(self, sock):
+    def __init__(self, space, sock):
         self.sock = sock
+        register_socket(space, sock)
 
     def get_type_w(self, space):
         return space.wrap(self.sock.type)
@@ -183,7 +185,7 @@
             fd, addr = self.sock.accept()
             sock = rsocket.make_socket(
                 fd, self.sock.family, self.sock.type, self.sock.proto)
-            return space.newtuple([space.wrap(W_Socket(sock)),
+            return space.newtuple([space.wrap(W_Socket(space, sock)),
                                    addr_as_object(addr, sock.fd, space)])
         except SocketError as e:
             raise converted_error(space, e)
@@ -248,7 +250,7 @@
     def dup_w(self, space):
         try:
             sock = self.sock.dup()
-            return W_Socket(sock)
+            return W_Socket(space, sock)
         except SocketError as e:
             raise converted_error(space, e)
 
@@ -592,10 +594,50 @@
         sock = RSocket(family, type, proto)
     except SocketError as e:
         raise converted_error(space, e)
-    W_Socket.__init__(self, sock)
+    W_Socket.__init__(self, space, sock)
     return space.wrap(self)
 descr_socket_new = interp2app(newsocket)
 
+
+# ____________________________________________________________
+# Automatic shutdown()/close()
+
+# On some systems, the C library does not guarantee that when the program
+# finishes, all data sent so far is really sent even if the socket is not
+# explicitly closed.  This behavior has been observed on Windows but not
+# on Linux, so far.
+NEED_EXPLICIT_CLOSE = (sys.platform == 'win32')
+
+class OpenRSockets(rweaklist.RWeakListMixin):
+    pass
+class OpenRSocketsState:
+    def __init__(self, space):
+        self.openrsockets = OpenRSockets()
+        self.openrsockets.initialize()
+
+def getopenrsockets(space):
+    if NEED_EXPLICIT_CLOSE and space.config.translation.rweakref:
+        return space.fromcache(OpenRSocketsState).openrsockets
+    else:
+        return None
+
+def register_socket(space, socket):
+    openrsockets = getopenrsockets(space)
+    if openrsockets is not None:
+        openrsockets.add_handle(socket)
+
+def close_all_sockets(space):
+    openrsockets = getopenrsockets(space)
+    if openrsockets is not None:
+        for sock_wref in openrsockets.get_all_handles():
+            sock = sock_wref()
+            if sock is not None:
+                try:
+                    sock.close()
+                except SocketError:
+                    pass
+
+
 # ____________________________________________________________
 # Error handling
 
diff --git a/pypy/module/_socket/test/test_sock_app.py b/pypy/module/_socket/test/test_sock_app.py
--- a/pypy/module/_socket/test/test_sock_app.py
+++ b/pypy/module/_socket/test/test_sock_app.py
@@ -309,10 +309,15 @@
 
 
 class AppTestSocket:
+    spaceconfig = dict(usemodules=['_socket', '_weakref', 'struct'])
+
     def setup_class(cls):
         cls.space = space
         cls.w_udir = space.wrap(str(udir))
 
+    def teardown_class(cls):
+        cls.space.sys.getmodule('_socket').shutdown(cls.space)
+
     def test_module(self):
         import _socket
         assert _socket.socket.__name__ == 'socket'
@@ -614,6 +619,12 @@
         finally:
             os.chdir(oldcwd)
 
+    def test_automatic_shutdown(self):
+        # doesn't really test anything, but at least should not explode
+        # in close_all_sockets()
+        import _socket
+        self.foo = _socket.socket()
+
 
 class AppTestPacket:
     def setup_class(cls):


More information about the pypy-commit mailing list