[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):
         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)):
             self = selfref()
             if self is not None:
         self._remove = remove

     def add(self, tasklet):
         ref = weakref.ref(tasklet, self.remove_tasklet)
         #ref = weakref.ref(tasklet, self._remove)
         print "Added", repr(ref)
     def remove_tasklet(self, ref):
         print "I have", repr(ref), repr(ref())
         print self.weak_tasklets
         print "died", get_name(ref())

_tasklet_tracker = _TaskletLifetimeTracker()

   ... more code here ...

Here's the output

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: 
#1  0x00043975 in subtype_dealloc (self=0x6200f0) at Objects/ 
#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: 
#4  0x00070858 in slp_eval_frame (f=0x110d8c0) at Stackless/core/ 
#5  0x000708a4 in climb_stack_and_eval_frame (f=0x6200f0) at  
#6  0x000707f1 in slp_eval_frame (f=0x110d8c0) at Stackless/core/ 
#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/ 
#10 0x00005798 in Py_Main (argc=1, argv=0xbffff2fc) at Modules/main.c: 
#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.   

   - 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  
           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


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.

				dalke at dalkescientific.com

More information about the Stackless mailing list