From b6b77b8426318a8f02f3f01c8cd9abe799843470 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 3 Mar 2011 09:16:06 +0000 Subject: Py/RNA api: fix for crash when iterating over a collection which allocates the collection and frees on when finished. The ability for BPy_StructRNA to hold a reference to other PyObject's was added to support this. --- source/blender/python/intern/bpy_rna.c | 62 +++++++++++++++++++++++++++++++--- source/blender/python/intern/bpy_rna.h | 8 +++++ 2 files changed, 66 insertions(+), 4 deletions(-) (limited to 'source/blender/python/intern') diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 121407bcb70..0009c5f7f47 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -900,6 +900,20 @@ static long pyrna_prop_hash(BPy_PropertyRNA *self) return x; } +#ifdef USE_PYRNA_STRUCT_REFERENCE +static int pyrna_struct_traverse(BPy_StructRNA *self, visitproc visit, void *arg) +{ + Py_VISIT(self->reference); + return 0; +} + +static int pyrna_struct_clear(BPy_StructRNA *self) +{ + Py_CLEAR(self->reference); + return 0; +} +#endif /* !USE_PYRNA_STRUCT_REFERENCE */ + /* use our own dealloc so we can free a property if we use one */ static void pyrna_struct_dealloc(BPy_StructRNA *self) { @@ -915,10 +929,34 @@ static void pyrna_struct_dealloc(BPy_StructRNA *self) } #endif +#ifdef USE_PYRNA_STRUCT_REFERENCE + if(self->reference) { + PyObject_GC_UnTrack(self); + pyrna_struct_clear(self); + } +#endif /* !USE_PYRNA_STRUCT_REFERENCE */ + /* Note, for subclassed PyObjects we cant just call PyObject_DEL() directly or it will crash */ Py_TYPE(self)->tp_free(self); } +#ifdef USE_PYRNA_STRUCT_REFERENCE +static void pyrna_struct_reference_set(BPy_StructRNA *self, PyObject *reference) +{ + if(self->reference) { +// PyObject_GC_UnTrack(self); /* INITIALIZED TRACKED? */ + pyrna_struct_clear(self); + } + /* reference is now NULL */ + + if(reference) { + self->reference= reference; + Py_INCREF(reference); +// PyObject_GC_Track(self); /* INITIALIZED TRACKED? */ + } +} +#endif /* !USE_PYRNA_STRUCT_REFERENCE */ + /* use our own dealloc so we can free a property if we use one */ static void pyrna_prop_dealloc(BPy_PropertyRNA *self) { @@ -4312,15 +4350,22 @@ PyTypeObject pyrna_struct_Type = { NULL, /* PyBufferProcs *tp_as_buffer; */ /*** Flags to define presence of optional/expanded features ***/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* long tp_flags; */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* long tp_flags; */ NULL, /* char *tp_doc; Documentation string */ /*** Assigned meaning in release 2.0 ***/ /* call function for all accessible objects */ - NULL, /* traverseproc tp_traverse; */ +#ifdef USE_PYRNA_STRUCT_REFERENCE + (traverseproc) pyrna_struct_traverse, /* traverseproc tp_traverse; */ /* delete references to contained objects */ + (inquiry )pyrna_struct_clear, /* inquiry tp_clear; */ +#else + NULL, /* traverseproc tp_traverse; */ + +/* delete references to contained objects */ NULL, /* inquiry tp_clear; */ +#endif /* !USE_PYRNA_STRUCT_REFERENCE */ /*** Assigned meaning in release 2.1 ***/ /*** rich comparisons ***/ @@ -4817,11 +4862,16 @@ static PyObject *pyrna_prop_collection_iter_next(BPy_PropertyCollectionIterRNA * else { BPy_StructRNA *pyrna= (BPy_StructRNA *)pyrna_struct_CreatePyObject(&self->iter.ptr); +#ifdef USE_PYRNA_STRUCT_REFERENCE if(pyrna) { /* unlikely but may fail */ if((PyObject *)pyrna != Py_None) { - // pyrna->reference; + /* hold a reference to the iterator since it may have + * allocated memory 'pyrna' needs. eg: introspecting dynamic enum's */ + /* TODO, we could have an api call to know if this is needed since most collections don't */ + pyrna_struct_reference_set(pyrna, (PyObject *)self); } } +#endif /* !USE_PYRNA_STRUCT_REFERENCE */ RNA_property_collection_next(&self->iter); @@ -5055,7 +5105,7 @@ PyObject *pyrna_struct_CreatePyObject(PointerRNA *ptr) } else { fprintf(stderr, "Could not make type\n"); - pyrna = (BPy_StructRNA *) PyObject_NEW(BPy_StructRNA, &pyrna_struct_Type); + pyrna = (BPy_StructRNA *) PyObject_GC_New(BPy_StructRNA, &pyrna_struct_Type); #ifdef USE_WEAKREFS pyrna->in_weakreflist= NULL; #endif @@ -5070,6 +5120,10 @@ PyObject *pyrna_struct_CreatePyObject(PointerRNA *ptr) pyrna->ptr= *ptr; pyrna->freeptr= FALSE; +#ifdef USE_PYRNA_STRUCT_REFERENCE + pyrna->reference= NULL; +#endif + // PyC_ObSpit("NewStructRNA: ", (PyObject *)pyrna); #ifdef USE_PYRNA_INVALIDATE_WEAKREF diff --git a/source/blender/python/intern/bpy_rna.h b/source/blender/python/intern/bpy_rna.h index 9c2b2b62ff8..b5da9bf91be 100644 --- a/source/blender/python/intern/bpy_rna.h +++ b/source/blender/python/intern/bpy_rna.h @@ -40,6 +40,9 @@ /* different method */ //#define USE_PYRNA_INVALIDATE_WEAKREF +/* support for inter references, currently only needed for corner case */ +// #define USE_PYRNA_STRUCT_REFERENCE + /* use real collection iterators rather then faking with a list */ #define USE_PYRNA_ITER @@ -90,6 +93,11 @@ typedef struct { PyObject *in_weakreflist; #endif PointerRNA ptr; +#ifdef USE_PYRNA_STRUCT_REFERENCE + /* generic PyObject we hold a reference to, example use: + * hold onto the collection iterator to prevent it from freeing allocated data we may use */ + PyObject *reference; +#endif /* !USE_PYRNA_STRUCT_REFERENCE */ int freeptr; /* needed in some cases if ptr.data is created on the fly, free when deallocing */ } BPy_StructRNA; -- cgit v1.2.3