[Python-Dev] Re: Evil Trashcan and GC interaction
Neil Schemenauer
nas@python.ca
Thu, 28 Mar 2002 09:53:38 -0800
Guido:
> Well, it *also* abuses the ob_refcnt field.
My patch fixes that too (by abusing the gc_prev pointer to chain trash
together).
> How about this wild idea (which Tim & I had simultaneously
> yesterday): change the trashcan code to simply leave the
> object in the GC generational list, and let the GC code
> special-case objects with zero refcnt so that they are
> eventually properly disposed?
That could probably work. What happens when the GC is disabled?
There is insidious bug here. Andrew helped me walk through it and I
think we figured it out. First here's the code to trigger it:
import gc
class Ouch:
def __del__(self):
for x in range(20):
list([])
def f():
gc.set_threshold(5)
while 1:
t = () # <-- here
for i in range(300):
t = [t, Ouch()]
f()
The line marked with "here" is where things go wrong. t used to refer
to a long chain of [t, Ouch()]. The SETLOCAL macro in ceval calls
Py_XDECREF(GETLOCAL(i)). That starts the deallocation of the list
structure. Ouch.__del__ gets called can creates some more objects,
triggering a collection. The f frame's traverse gets called and tries
to follow the pointer for the t local. It points to memory that was
freed by _PyTrash_destroy_chain.
Hmm, now that I think about it the GC is not needed to trigger the bug:
import gc
gc.disable()
import sys
class Ouch:
def __del__(self):
print f_frame.f_locals['t']
def f():
global f_frame
f_frame = sys._getframe()
while 1:
t = ()
for i in range(300):
t = [t, Ouch()]
f()
I haven't figured out the correct solution yet. I'm just giving a
status update. :-)
Neil