Yet another attempt to plug M2Crypto.SSL.Connection.Connection
Michael Dunstan <[email protected]>
| Newsgroups | gmane.comp.python.cryptography |
|---|---|
| Message-ID | <[email protected]> |
Hi all,
Reading "M2Crypto woes"
http://www.artima.com/weblogs/viewpost.jsp?thread=95863 reminded me
that I should go back and see what happened with that patch that in
some fluke just happened to work for me.
Seems that this still exists even on the 0.15 branch. Okay - time to
take a closer look then. Attached is a trivial example demonstrating
the memory leak. When you run this it prints something like:
[<__main__.Leaky instance at 0x6de50>]
Meaning that we have an instance of the Leaky class that is now marked
as garbage and not collected. But the other two instances of non leaky
classes are not present. They have been garbage collected. A class with
the combination of assignment of a bound method and use of __del__
creates a cycle that can not be garbage collected. Well, that's how I'm
interpreting the behaviour I'm seeing. (With python 2.3.4.)
So what to do about this in the case of
M2Crypto.SSL.Connection.Connection? (Without resorting to wrapping
read/write with an if statement.) One way would be to provide a
different class for the different behaviour rather than use
setblocking(). For example:
class Connection:
"""A blocking SSL connection."""
sendall = send = write = _write_bio
recv = read = _read_bio
class NonBlockingConnection(Connection):
"""A non blocking SSL connection."""
send = write = _write_nbio
recv = read = _read_nbio
(I don't pretend to understand why sendall is not affected by the
different modes. Should it even exist for NonBlockingConnection?)
Not sure how that fits into the existing framework though.
Michael.
leak.py
(application/octet-stream, 1.5 KB)
"""Example of leaking class instances when assinging bound methods and
providing a __dell__ method."""
class Leaky:
def __init__(self):
self.assinged_bound_method = self.bound_method
def bound_method(self):
pass
def __del__(self):
pass
class NonLeakyNoAssignmentOfBoundMethod:
def __init__(self):
pass
def bound_method(self):
pass
def __del__(self):
pass
class NonLeakyNo__del__Statement:
def __init__(self):
self.assinged_bound_method = self.bound_method
def bound_method(self):
pass
class NonLeakyClassTimeBinding:
def __init__(self):
pass
def bound_method(self):
pass
# XXX "Class time binding"? What's that? I'm not sure what the
# real term is. But I'm trying to describe just when the next
# assignement is made:
another_bound_method = bound_method
def __del__(self):
pass
class NonLeakyDerived(NonLeakyClassTimeBinding):
def __init__(self):
pass
def yet_another_bound_method(self):
pass
another_bound_method = yet_another_bound_method
def __del__(self):
pass
def test():
"""Create instances of classes in a new scope."""
leak = Leaky()
non_leak_no_assignment_of_bound_method = NonLeakyNoAssignmentOfBoundMethod()
non_leak_no__del__statement = NonLeakyNo__del__Statement()
non_leak_class_time_binding = NonLeakyClassTimeBinding()
non_leak_derived = NonLeakyDerived()
test()
import gc
gc.collect()
print gc.garbage