Cyclic garbage collection and segfaults...

Thomas Mailund mailund at birc.dk
Wed Jan 14 15:21:41 EST 2004


Hi group.

I have a problem with some C extensions I am working with and
hope that some of you can help.  Basically, I am wrapping a a tree
structure from C where I have python methods for extracting either the
entire tree or subtrees; since I don't want the full tree to be
deallocated while python has references to the subtrees, I INCREF the
full tree whenever I hand out a subtree reference.  I don't want any of
the subtrees to be deallocated while the full tree lives, either, so I
want the fulltree to have a reference to each of the subtrees.  Naturally,
this gives me a cyclik reference structure, and I want to be able to
garbage collect it.  But this is where my problems begin...

For some reason, when I turn on the cyclic garbage collection for my
types, a deallocation automatically gives me a segfault.  That is, if my
type new type contains any data whatsoever, I cannot garbage collect
without dumping core.  I have boiled the problem down to the code shown
below.  It contains no cyclic structure, it is as simple as it gets, but
it still seqfaults for me.  If I comment out the magic void-pointer (which
isn't used for anything) I don't get the segfault.


#include <Python.h>

struct SimpleObject;

static PyObject *	Simple_new     (PyTypeObject	 	    *type,
					PyObject  		    *args,
					PyObject            	    *kwds);
static int		Simple_init    (struct SimpleObject 	    *self,
					PyObject	            *args,
					PyObject 		    *kwds);
static int              Simple_traverse(struct SimpleObject         *self,
					visitproc                    visit,
					void                        *arg);
static void		Simple_dealloc (struct SimpleObject         *self);




typedef struct SimpleObject {
    /* object stuff */
    PyObject_HEAD

    /* dark magic */
    void *magic;

} SimpleObject;


static PyTypeObject simple_SimpleType = {
    PyObject_HEAD_INIT(NULL)
    0,					/*ob_size*/
    "simple.Simple",			/*tp_name*/
    sizeof(SimpleObject),  		/*tp_basicsize*/
    0,					/*tp_itemsize*/
    (destructor)Simple_dealloc, 	/*tp_dealloc*/
    0,					/*tp_print*/
    0,					/*tp_getattr*/
    0,					/*tp_setattr*/
    0,					/*tp_compare*/
    0,					/*tp_repr*/
    0,					/*tp_as_number*/
    0,					/*tp_as_sequence*/
    0,					/*tp_as_mapping*/
    0,					/*tp_hash */
    0,					/*tp_call*/
    0,					/*tp_str*/
    0,				        /*tp_getattro*/
    0,				        /*tp_setattro*/
    0,					/*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
    "Simple objects",		        /*tp_doc*/
    (traverseproc)Simple_traverse, 	/*tp_traverse*/
    0,					/*tp_clear*/
    0,					/*tp_richcompare*/
    0,					/*tp_weaklistoffset*/
    0,					/*tp_iter*/
    0,					/*tp_iternext*/
    0,					/*tp_methods*/
    0,					/*tp_members*/
    0,					/*tp_getset*/
    0,					/*tp_base*/
    0,				        /*tp_dict*/
    0,                                  /*tp_descr_get*/
    0,                                  /*tp_descr_set*/
    0,				        /*tp_dictoffset*/
    (initproc)Simple_init,		/*tp_init*/
    0,                                  /*tp_alloc*/
    Simple_new,		        	/*tp_new*/

    0 /*...the rest...*/
};


static PyObject *
Simple_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    SimpleObject *self = (SimpleObject *)type->tp_alloc(type, 0);
    return (PyObject*)self;
}

static int
Simple_init(SimpleObject *self, PyObject *args, PyObject *kwds)
{
    return 0;
}

/* cyclic gc */
static int
Simple_traverse (SimpleObject *self, visitproc visit, void *arg)
{
    fprintf(stderr,"Simple_traverse...\n");
    return 0;
}

static int
Simple_clear(SimpleObject *self)
{
    fprintf(stderr,"Simple_clear...\n");
    return 0;
}

static void
Simple_dealloc(SimpleObject *self)
{
    fprintf(stderr,"Simple_dealloc %p\n", self);
    self->ob_type->tp_free((PyObject*)self); /* <= segfault here */
    return;
}



static PyMethodDef simple_methods[] = {
    {0}				/* sentinel */
};



void
initsimple(void)
{
    if (PyType_Ready(&simple_SimpleType) < 0) return;
    Py_INCREF(&simple_SimpleType);

    PyObject* m = Py_InitModule3("simple", simple_methods, "Simple module.");
    PyModule_AddObject(m,"Simple",(PyObject*)&simple_SimpleType);
}


A script sufficient to provoke the fault is this:

import simple
simple.Simple()


Can anyone explain what I'm doing wrong?  Or perhaps suggest a better
solution to my "real" problem, if I'm approaching the problem completely
wrong :-)

Yours,
	/mailund




More information about the Python-list mailing list