Jean-Paul Calderone, 11.10.2012 15:21:
> On Thursday, October 11, 2012 2:44:15 AM UTC-4, Stefan Behnel wrote:
>> You are accessing a Python object attribute in __dealloc__(). Python's
>> garbage collector is free to clear Python object attributes at any moment
>> between the time the last reachable reference to an object dies and the
>> time the object is cleaned up and __dealloc__() is called. This definitely
>> happens when a reference cycle is involved, because in order to clean it
>> up, the garbage collector must start by killing a reference somewhere in
>> order to break the cycle, so it takes an arbitrary object and sets its
>> object attributes to None. The code above will stop working and do nothing
>> in that case because self.objects is None, i.e. False.
> Indeed. I forgot about that while minimizing the example. This doesn't
> affect my actual code, I think, as the "list of objects" is actually held
> inside the c++ object pointed to by self.thisptr - and I think that
> self.thisptr is safe in __dealloc__?
> I dug a little more and came across something I don't understand in the
> handling of __weakref__ in the generated tp_traverse for this type.
> e = (*v)(o->__weakref__, a); if (e) return e;
> Based on inspection of some types defined by CPython itself (eg the ones in
> Modules/_io/fileio.c), this looks fishy. The CPython documentation for
> what to do with the __weakref__ list is rather absent unfortunately, so I'm
> not *sure* this is wrong. However, changing the Cython code generator to
> omit this particular traversal gets rid of the failing gc assertion
> (replacing it with another failing assertion about a negative reference
> count - which doesn't do anything for my confidence about the *correctness*
> of this change). I also talked to Yhg1s on #python and his understanding
> is that it is not the type's responsibility to traverse this list - it is
> owned by the weak reference system, not the instance. The resulting double
> traversal seems consistent with the failing assertion in my initial post.
> I've also filed this bug against CPython about the poor documentation:
> Perhaps it will result in some more clarity on the issue.
Antoine mentioned PyObject_ClearWeakRefs() as the way to do it. It's quite
possible that Cython misbehaves here, given the lack of explicit documentation.
> As I mentioned, the example still doesn't work. The next failure after
> changing the code generator is:
> Fatal Python error: bullet/bullet.cpp:724 object at 0x1aa9a58 has negative
> ref count -1
> I wonder if this is due to the casts in the example doing the wrong thing.
> Does `<object>integer` actually give me the PyObject* with the address
No, it would give you an integer object. And given that the right side of
the cast is already a Python object (not a C integer), it's actually a no-op.
What you might have been thinking of is "<object>some_voidptr". That casts
a pointer to an object to an owned object reference, thus increasing (and
further managing) the reference count.
> Or is it a no-op, since Python integers are indeed already
> objects (resulting in the Py_DECREF applying to the wrong object). If so,
> I wonder how to spell the correct cast.
Could you explain why you use these complicated indirections at all? Why
doesn't a normal Python reference work?