Dowemo
0 0 0 0


Question:

I have some ctypes bindings, and for each body.New I should call body.Free. The library I'm binding doesn't have allocation routines insulated out from the rest of the code (they can be called about anywhere there), and to use couple of useful features I need to make cyclic references.

I think It'd solve if I'd find a reliable way to hook destructor to an object. (weakrefs would help if they'd give me the callback just before the data is dropped.

So obviously this code megafails when I put in velocity_func:

class Body(object):
    def __init__(self, mass, inertia):
        self._body = body.New(mass, inertia)
    def __del__(self):
        print '__del__ %r' % self
        if body:
            body.Free(self._body)
    ...        
    def set_velocity_func(self, func):
        self._body.contents.velocity_func = ctypes_wrapping(func)

I also tried to solve it through weakrefs, with those the things seem getting just worse, just only largely more unpredictable.

Even if I don't put in the velocity_func, there will appear cycles at least then when I do this:

class Toy(object):
    def __init__(self, body):
        self.body.owner = self
...
def collision(a, b, contacts):
    whatever(a.body.owner)

So how to make sure Structures will get garbage collected, even if they are allocated/freed by the shared library?

There's repository if you are interested about more details: http://bitbucket.org/cheery/ctypes-chipmunk/


Best Answer:


In CPython, __del__is a reliable destructor of an object, because it will always be called when the reference count reaches zero (note: there may be cases - like circular references of items with __del__ method defined - where the reference count will never reaches zero, but that is another issue).

UpdateFrom the comments, I understand the problem is related to the order of destruction of objects: body is a global object, and it is being destroyed before all other objects, thus it is no longer available to them.
Actually, using global objects is not good; not only because of issues like this one, but also because of maintenance.

I would then change your class with something like this

class Body(object):
    def __init__(self, mass, inertia):
        self._bodyref = body
        self._body = body.New(mass, inertia)
    def __del__(self):
        print '__del__ %r' % self
        if body:
            body.Free(self._body)
...        
def set_velocity_func(self, func):
    self._body.contents.velocity_func = ctypes_wrapping(func)

A couple of notes:

  • The change is only adding a reference to the global body object, that thus will live at least as much as all the objects derived from that class.
  • Still, using a global object is not good because of unit testing and maintenance; better would be to have a factory for the object, that will set the correct "body" to the class, and in case of unit test will easily put a mock object. But that's really up to you and how much effort you think makes sense in this project.



  • Copyright © 2011 Dowemo All rights reserved.    Creative Commons   AboutUs