[Twisted-Python] deferToThread and thrown exceptions break gc - ticket 3853

exarkun at twistedmatrix.com exarkun at twistedmatrix.com
Wed Jun 26 15:07:25 MDT 2013


On 08:15 pm, rutt.4 at osu.edu wrote:
>I am not sure that that trac issue contains a patch that will fix it. 
>The
>most recent patch on that trac issue is for twisted 8.1, and it does 
>not
>apply cleanly to twisted 12.3.0.  When I tried to place the 2 lines 
>that
>form the patch in the most obvious place in the 12.3.0 codebase (before 
>the
>code 'self.waiters.append(ct)') I could not get it to fix the repro I 
>have.
>Here's my repro:
>
>from twisted.internet import reactor
>from twisted.internet.threads import deferToThread
>
>class SomeClass(object):
>    def __del__(self):
>        print 'SomeClass\'s destructor was called!'

You need to be a little careful here.  Adding an object with a `__del__` 
method is a good way to further confuse garbage collection issues.  For 
this test to be valid, you now also need to prove that this object never 
becomes part of a reference cycle.

It's generally safer to use a weakref with a callback to prove things 
about when objects get collected.

However, I don't think this is a problem in this case.

I did modify your example in another way though:

from twisted.internet import reactor
from twisted.internet.threads import deferToThread

class SomeClass(object):
    def __del__(self):
        print "SomeClass\'s destructor was called!"

def foo():
    sc = SomeClass()
    raise RuntimeError('bah')

def shutmedown(data):
    print 'reactor shutdown happening soon'
    reactor.callLater(1, stop)

def stop():
    print 'reactor shutdown happening now'
    reactor.stop()

def go():
    d = deferToThread(foo)
    d.addCallbacks(shutmedown, shutmedown)
reactor.callWhenRunning(go)
reactor.run()
print 'Reactor shutdown'

Notice that now it doesn't shutdown as soon as the thread completes, but 
lets execution continue for a little while longer.

Against trunk at HEAD on Debian Wheezy, I see this behavior:

reactor shutdown happening soon
reactor shutdown happening now
SomeClass's destructor was called!
Reactor shutdown

This still shows there is a problem, since the object is kept alive for 
an arbitrary amount of time after the function in the thread raises the 
exception.

Then I inserted just the `sys.exc_clear()` call above the "with 
self._workerState..." statement in threadpool.py in trunk at HEAD.  This 
changes the behavior to:

with
reactor shutdown happening soon
SomeClass's destructor was called!
reactor shutdown happening now
Reactor shutdown

Notice the destructor is called sooner.  From watching this run, I can 
also say it is called soon after the callback on the deferToThread 
Deferred fires (not, say, after the 1 second delay I inserted).

Whether or not it's possible to have the destructor called *even* sooner 
than this, I don't know.  This behavior does seem like it's good enough 
for most cases though.

Jean-Paul




More information about the Twisted-Python mailing list