Missing stack frames?
Nikolaus Rath
Nikolaus at rath.org
Thu Jun 5 22:16:06 EDT 2014
dieter <dieter at handshake.de> writes:
[...]
> Someone else already mentioned that the "close" call
> can come from a destructor. Destructors can easily be called
> at not obvious places (e.g. "s = C(); ... x = [s for s in ...]";
> in this example the list comprehension calls the "C" destructor
> which is not obvious when one looks only locally).
> The destructor calls often have intervening C code (which
> one does not see). However, in your case, I do not see
> why the "cgi" module should cause a destructor call of one
> of your server components.
Paul, dieter, you are my heroes. It was indeed an issue with a
destructor. It turns out that the io.RawIOBase destructor calls
self.close(). If the instance of a derived class is part of a reference
cycle, it gets called on the next routine run of the garbage
collector -- with the stack trace originating at whatever statement was
last executed before the gc run.
The following minimal example reproduces the problem:
#!/usr/bin/env python3
import io
import traceback
import threading
class Container:
pass
class InnocentVictim(io.RawIOBase):
def close(self):
print('close called in %s by:'
% threading.current_thread().name)
traceback.print_stack()
def busywork():
numbers = []
for i in range(500):
o = Container()
o.l = numbers
numbers.append(o)
if i % 87 == 0:
numbers = []
l = [ InnocentVictim() ]
l[0].cycle = l
del l
t = threading.Thread(target=busywork)
t.start()
t.join()
If you run this, you could things like:
close called in Thread-1 by:
File "/usr/lib/python3.4/threading.py", line 888, in _bootstrap
self._bootstrap_inner()
File "/usr/lib/python3.4/threading.py", line 920, in _bootstrap_inner
self.run()
File "/usr/lib/python3.4/threading.py", line 868, in run
self._target(*self._args, **self._kwargs)
File "./test.py", line 18, in busywork
o = Container()
File "./test.py", line 13, in close
traceback.print_stack()
Ie, a method being called by a thread that doesn't have access to the
object, and without any reference to the call in the source.
I am left wondering:
- Is there really a point in the RawIOBase destructor calling close?
- Is there some way to make the call stack for destructors less confusing?
Best,
-Nikolaus
--
GPG encrypted emails preferred. Key id: 0xD113FCAC3C4E599F
Fingerprint: ED31 791B 2C5C 1613 AF38 8B8A D113 FCAC 3C4E 599F
»Time flies like an arrow, fruit flies like a Banana.«
More information about the Python-list
mailing list