[Python-3000] socket GC worries (original) (raw)
Bill Janssen janssen at parc.com
Tue Oct 30 20:49:21 CET 2007
- Previous message: [Python-3000] socket GC worries
- Next message: [Python-3000] socket GC worries
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
But if we remove SocketCloser, there's no need for the cyclic GC to be involved. If the count (of the number of outstanding SocketIO instances pointing to this socket.socket) is just moved into the socket.socket object itself, there's no cyclic reference, and normal refcounting should work just fine. I don't even think a del method on socket.socket is necessary.
Here's a patch, for whenever you get back to this. You can ignore/remove the first hunk, which is about SSL. I've tried all the tests, and they work. I've looked for leaks in test_socket and test_ssl, no leaks.
Bill
Index: Lib/socket.py
--- Lib/socket.py (revision 58714) +++ Lib/socket.py (working copy) @@ -21,7 +21,6 @@ htons(), htonl() -- convert 16, 32 bit int from host to network byte order inet_aton() -- convert IP addr string (123.45.67.89) to 32-bit packed format inet_ntoa() -- convert 32-bit packed format IP to string (123.45.67.89) -ssl() -- secure socket layer support (only available if configured) socket.getdefaulttimeout() -- get the default timeout value socket.setdefaulttimeout() -- set the default timeout value create_connection() -- connects to an address, with an optional timeout @@ -46,36 +45,6 @@ import _socket from _socket import * -try: - import _ssl - import ssl as _realssl -except ImportError: - # no SSL support - pass -else: - def ssl(sock, keyfile=None, certfile=None): - # we do an internal import here because the ssl - # module imports the socket module - warnings.warn("socket.ssl() is deprecated. Use ssl.wrap_socket() instead.", - DeprecationWarning, stacklevel=2) - return _realssl.sslwrap_simple(sock, keyfile, certfile)
we need to import the same constants we used to...
from _ssl import SSLError as sslerror
from _ssl import \
RAND_add, \
RAND_egd, \
RAND_status, \
SSL_ERROR_ZERO_RETURN, \
SSL_ERROR_WANT_READ, \
SSL_ERROR_WANT_WRITE, \
SSL_ERROR_WANT_X509_LOOKUP, \
SSL_ERROR_SYSCALL, \
SSL_ERROR_SSL, \
SSL_ERROR_WANT_CONNECT, \
SSL_ERROR_EOF, \
SSL_ERROR_INVALID_ERROR_CODE
import os, sys, io
try:
@@ -119,49 +88,11 @@ nfd = os.dup(fd) return socket(family, type, proto, fileno=nfd) -class SocketCloser:
"""Helper to manage socket close() logic for makefile().
The OS socket should not be closed until the socket and all
of its makefile-children are closed. If the refcount is zero
when socket.close() is called, this is easy: Just close the
socket. If the refcount is non-zero when socket.close() is
called, then the real close should not occur until the last
makefile-child is closed.
"""
def init(self, sock):
self._sock = sock
self._makefile_refs = 0
# Test whether the socket is open.
try:
sock.fileno()
self._socket_open = True
except error:
self._socket_open = False
def socket_close(self):
self._socket_open = False
self.close()
def makefile_open(self):
self._makefile_refs += 1
def makefile_close(self):
self._makefile_refs -= 1
self.close()
def close(self):
if not (self._socket_open or self._makefile_refs):
self._sock._real_close()
class socket(_socket.socket):
"""A subclass of _socket.socket adding the makefile() method."""
slots = ["weakref", "_closer"]
- slots = ["weakref", "_io_refs", "_closed"] if not _can_dup_socket: slots.append("_base")
@@ -170,16 +101,17 @@ _socket.socket.init(self, family, type, proto) else: _socket.socket.init(self, family, type, proto, fileno) - # Defer creating a SocketCloser until makefile() is actually called. - self._closer = None + self._io_refs = 0 + self._closed = False def repr(self): """Wrap repr() to reveal the real class name.""" s = _socket.socket.repr(self) if s.startswith("<socket object"): - s = "<%s.%s%s" % (self.__class__.__module__, - self.__class__.__name__, - s[7:]) + s = "<%s.%s%s%s" % (self.__class__.__module__, + self.__class__.__name__, + (self._closed and " [closed] ") or "", + s[7:]) return s def accept(self): @@ -196,6 +128,12 @@ conn.close() return wrapper, addr + def decref_socketios(self): + if self._io_refs > 0: + self._io_refs -= 1 + if self._closed: + self.close() + def makefile(self, mode="r", buffering=None, *, encoding=None, newline=None): """Return an I/O stream connected to the socket. @@ -216,9 +154,8 @@ rawmode += "r" if writing: rawmode += "w" - if self._closer is None: - self._closer = SocketCloser(self) - raw = SocketIO(self, rawmode, self._closer) + raw = SocketIO(self, rawmode) + self._io_refs += 1 if buffering is None: buffering = -1 if buffering < 0: @@ -246,10 +183,9 @@ return text def close(self): - if self._closer is None: + self._closed = True + if self._io_refs < 1: self._real_close() - else: - self._closer.socket_close() # _real_close calls close on the _socket.socket base class. @@ -275,16 +211,14 @@ # XXX More docs - def init(self, sock, mode, closer): + def init(self, sock, mode): if mode not in ("r", "w", "rw"): raise ValueError("invalid mode: %r" % mode) io.RawIOBase.init(self) self._sock = sock self._mode = mode - self._closer = closer self._reading = "r" in mode self._writing = "w" in mode - closer.makefile_open() def readinto(self, b): self._checkClosed() @@ -308,10 +242,12 @@ def close(self): if self.closed: return - self._closer.makefile_close() io.RawIOBase.close(self) + def del(self): + self._sock.decref_socketios() + def getfqdn(name=''): """Get fully qualified domain name from name.
- Previous message: [Python-3000] socket GC worries
- Next message: [Python-3000] socket GC worries
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]