[project @ Arch-1:robey@lag.net--2005-master-shake%paramiko--dev--1--patch-45]

yeah! figured out the last things that were causing GC cycles and got rid of them: Channels, Transports, SFTPClients, and SFTPFiles can all have __del__ methods now, which auto-close themselves :)
This commit is contained in:
Robey Pointer 2005-07-17 20:06:29 +00:00
parent f27e625926
commit e9ccd7ea20
4 changed files with 23 additions and 8 deletions

View File

@ -88,6 +88,9 @@ class Channel (object):
self.combine_stderr = False self.combine_stderr = False
self.exit_status = -1 self.exit_status = -1
def __del__(self):
self.close()
def __repr__(self): def __repr__(self):
""" """
Return a string representation of this object, for debugging. Return a string representation of this object, for debugging.
@ -455,11 +458,7 @@ class Channel (object):
Close the channel. All future read/write operations on the channel Close the channel. All future read/write operations on the channel
will fail. The remote end will receive no more data (after queued data will fail. The remote end will receive no more data (after queued data
is flushed). Channels are automatically closed when their L{Transport} is flushed). Channels are automatically closed when their L{Transport}
is closed. is closed or when they are garbage collected.
Note that because of peculiarities with the way python's garbage
collection works on cycles, channels will B{not} be automatically
closed by the garbage collector.
""" """
self.lock.acquire() self.lock.acquire()
try: try:

View File

@ -64,6 +64,9 @@ class SFTPClient (BaseSFTP):
self.ultra_debug = transport.get_hexdump() self.ultra_debug = transport.get_hexdump()
self._send_version() self._send_version()
def __del__(self):
self.close()
def from_transport(selfclass, t): def from_transport(selfclass, t):
""" """
Create an SFTP client channel from an open L{Transport}. Create an SFTP client channel from an open L{Transport}.

View File

@ -41,6 +41,9 @@ class SFTPFile (BufferedFile):
self.handle = handle self.handle = handle
BufferedFile._set_mode(self, mode, bufsize) BufferedFile._set_mode(self, mode, bufsize)
def __del__(self):
self.close()
def close(self): def close(self):
BufferedFile.close(self) BufferedFile.close(self)
try: try:

View File

@ -21,6 +21,7 @@ L{BaseTransport} handles the core SSH2 protocol.
""" """
import sys, os, string, threading, socket, struct, time import sys, os, string, threading, socket, struct, time
import weakref
from common import * from common import *
from ssh_exception import SSHException from ssh_exception import SSHException
@ -205,7 +206,7 @@ class BaseTransport (threading.Thread):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((hostname, port)) sock.connect((hostname, port))
# okay, normal socket-ish flow here... # okay, normal socket-ish flow here...
threading.Thread.__init__(self, target=self._run) threading.Thread.__init__(self)
self.randpool = randpool self.randpool = randpool
self.sock = sock self.sock = sock
# Python < 2.3 doesn't have the settimeout method - RogerB # Python < 2.3 doesn't have the settimeout method - RogerB
@ -229,7 +230,7 @@ class BaseTransport (threading.Thread):
self.initial_kex_done = False self.initial_kex_done = False
self.in_kex = False self.in_kex = False
self.lock = threading.Lock() # synchronization (always higher level than write_lock) self.lock = threading.Lock() # synchronization (always higher level than write_lock)
self.channels = { } # (id -> Channel) self.channels = weakref.WeakValueDictionary() # (id -> Channel)
self.channel_events = { } # (id -> Event) self.channel_events = { } # (id -> Event)
self.channel_counter = 1 self.channel_counter = 1
self.window_size = 65536 self.window_size = 65536
@ -249,6 +250,9 @@ class BaseTransport (threading.Thread):
self.server_accept_cv = threading.Condition(self.lock) self.server_accept_cv = threading.Condition(self.lock)
self.subsystem_table = { } self.subsystem_table = { }
def __del__(self):
self.close()
def __repr__(self): def __repr__(self):
""" """
Returns a string representation of this object, for debugging. Returns a string representation of this object, for debugging.
@ -969,7 +973,12 @@ class BaseTransport (threading.Thread):
raise SSHException('Unknown client cipher ' + name) raise SSHException('Unknown client cipher ' + name)
return self._cipher_info[name]['class'].new(key, self._cipher_info[name]['mode'], iv) return self._cipher_info[name]['class'].new(key, self._cipher_info[name]['mode'], iv)
def _run(self): def run(self):
# (use the exposed "run" method, because if we specify a thread target
# of a private method, threading.Thread will keep a reference to it
# indefinitely, creating a GC cycle and not letting Transport ever be
# GC'd. it's a bug in Thread.)
# active=True occurs before the thread is launched, to avoid a race # active=True occurs before the thread is launched, to avoid a race
_active_threads.append(self) _active_threads.append(self)
if self.server_mode: if self.server_mode:
@ -1281,6 +1290,7 @@ class BaseTransport (threading.Thread):
# can also free a bunch of stuff here # can also free a bunch of stuff here
self.local_kex_init = self.remote_kex_init = None self.local_kex_init = self.remote_kex_init = None
self.K = None self.K = None
self.kex_engine = None
if not self.initial_kex_done: if not self.initial_kex_done:
# this was the first key exchange # this was the first key exchange
self.initial_kex_done = True self.initial_kex_done = True