[Stackless] strange bug with weakref on main tasklet
Andrew Dalke
dalke at dalkescientific.com
Tue Dec 11 03:33:09 CET 2007
This appears to be a memory bug which requires everything to be in
just the right place to trigger the error. My code starts
import weakref
import stackless
_names = weakref.WeakKeyDictionary()
def set_name(obj, name):
# obj should be a tasklet or a channel
_names[obj] = name
def get_name(obj):
try:
return _names[obj]
except KeyError:
return repr(obj)
set_name(stackless.getmain(), "main")
class _TaskletLifetimeTracker(object):
def __init__(self):
print "qwe"
print "Ref is", ref # <----- There is an error here
def remove(k, selfref=ref(self)):
return
self = selfref()
if self is not None:
self.weak_tasklets.remove(k)
self._remove = remove
def add(self, tasklet):
ref = weakref.ref(tasklet, self.remove_tasklet)
#ref = weakref.ref(tasklet, self._remove)
print "Added", repr(ref)
self.weak_tasklets.add(ref)
def remove_tasklet(self, ref):
print "I have", repr(ref), repr(ref())
print self.weak_tasklets
print "died", get_name(ref())
self.tasklets.remove(ref)
_tasklet_tracker = _TaskletLifetimeTracker()
... more code here ...
Here's the output
qwe
Bus error
I expected there to be a NameError because "ref" does not exist
inside the __init__ method.
Note that this is not the full reproducible as I need all of the code
in the module to produce the error, even though the rest of the code
is never executed. If I remove the "get_name" function -- which is
never called! -- I get the expected
Traceback (most recent call last):
File "bug.py", line 39, in <module>
_tasklet_tracker = _TaskletLifetimeTracker()
File "bug.py", line 19, in __init__
print "Ref is", ref
NameError: global name 'ref' is not defined
The gdb traceback is
#0 0x000a6e65 in PyObject_GC_Del (op=0x6200f0) at Modules/gcmodule.c:
161
#1 0x00043975 in subtype_dealloc (self=0x6200f0) at Objects/
typeobject.c:711
#2 0x0007269e in schedule_task_destruct (prev=0x6200f0,
next=0x6200f0) at Stackless/module/scheduling.c:1023
#3 0x000728b2 in slp_run_tasklet () at Stackless/module/scheduling.c:
1099
#4 0x00070858 in slp_eval_frame (f=0x110d8c0) at Stackless/core/
stacklesseval.c:299
#5 0x000708a4 in climb_stack_and_eval_frame (f=0x6200f0) at
Stackless/core/stacklesseval.c:266
#6 0x000707f1 in slp_eval_frame (f=0x110d8c0) at Stackless/core/
stacklesseval.c:294
#7 0x000689a3 in PyEval_EvalCode (co=0x629218, globals=0x498660,
locals=0x498660) at Python/ceval.c:497
#8 0x0009cbe6 in PyRun_FileExFlags (fp=0xa000bda0,
filename=0xbffff3e5 "bug.py", start=257, globals=0x498660,
locals=0x498660, closeit=1, flags=0xbffff27c) at Python/pythonrun.c:1324
#9 0x0009d045 in PyRun_SimpleFileExFlags (fp=0xa000bda0,
filename=0xbffff3e5 "bug.py", closeit=1, flags=0xbffff27c) at Python/
pythonrun.c:906
#10 0x00005798 in Py_Main (argc=1, argv=0xbffff2fc) at Modules/main.c:
527
#11 0x0000215e in _start ()
#12 0x00002085 in start ()
I interpret this to mean that there is a tasklet destruction going on
here which causes a subtle interaction with weak references.
Specifically:
- I get the main tasklet
- I make a weakref to it, when I store it in the name WeakKeyDict
- There are no hard references to it, so at the next garbage
collection
the main tasklet object is removed
- Somewhere things get messed up
Note that if I replace
def set_name(obj, name):
# obj should be a tasklet or a channel
_names[obj] = name
with
def set_name(obj, name):
# obj should be a tasklet or a channel
set_name.x = obj
_names[obj] = name
then there is no bus error. There is a bus error if I use
set_name.x = name
so I take that to mean that the extra hard reference to the tasklet
prevents the code from breaking, and not the extra code causing the
gc time to change.
Should I be calling getmain() before starting any other tasklets? Is
there something I'm doing wrong? But any way to cause Python to bus
error like this likely indicates a bug.
Andrew
dalke at dalkescientific.com
More information about the Stackless
mailing list