Fix massive amount of memleaks on exit in BPY.

Essentially, our current code would not properly remove (dereference)
its python objects matching various RNA data created during execution.

Some cases are fairly trivial to understand (like the lack of handling
of unregstering for our 'startup' operators and UI), other were more
subtle (like unregistered PropertyGroups who would remove/free their RNA
struct definition, without releasing first the potential matching python
object).

Co-authored-by: Campbell Barton <campbell@blender.org>

Pull Request: https://projects.blender.org/blender/blender/pulls/128899
This commit is contained in:
Bastien Montagne
2024-10-11 18:33:29 +02:00
committed by Gitea
parent 649dfb8abc
commit 1e55d034a1
12 changed files with 101 additions and 58 deletions

View File

@@ -765,10 +765,6 @@ void BPy_init_modules(bContext *C)
PointerRNA ctx_ptr = RNA_pointer_create(nullptr, &RNA_Context, C);
bpy_context_module = (BPy_StructRNA *)pyrna_struct_CreatePyObject(&ctx_ptr);
/* odd that this is needed, 1 ref on creation and another for the module
* but without we get a crash on exit */
Py_INCREF(bpy_context_module);
PyModule_AddObject(mod, "context", (PyObject *)bpy_context_module);
/* Register methods and property get/set for RNA types. */

View File

@@ -577,7 +577,7 @@ void BPY_python_end(const bool do_python_exit)
BPY_rna_props_clear_all();
/* Free other Python data. */
pyrna_free_types();
RNA_bpy_exit();
BPY_rna_exit();

View File

@@ -9096,26 +9096,14 @@ void pyrna_alloc_types()
#endif /* !NDEBUG */
}
void pyrna_free_types()
void BPY_free_srna_pytype(StructRNA *srna)
{
PropertyRNA *prop;
void *py_ptr = RNA_struct_py_type_get(srna);
/* Avoid doing this lookup for every getattr. */
PointerRNA ptr = RNA_blender_rna_pointer_create();
prop = RNA_struct_find_property(&ptr, "structs");
RNA_PROP_BEGIN (&ptr, itemptr, prop) {
StructRNA *srna = srna_from_ptr(&itemptr);
void *py_ptr = RNA_struct_py_type_get(srna);
if (py_ptr) {
#if 0 /* XXX: should be able to do this, but makes Python crash on exit. */
bpy_class_free(py_ptr);
#endif
RNA_struct_py_type_set(srna, nullptr);
}
if (py_ptr) {
bpy_class_free(py_ptr);
RNA_struct_py_type_set(srna, nullptr);
}
RNA_PROP_END;
}
/**
@@ -9255,11 +9243,13 @@ static PyObject *pyrna_register_class(PyObject * /*self*/, PyObject *py_class)
pyrna_subtype_set_rna(py_class, srna_new);
/* Old srna still references us, keep the check in case registering somehow can free it. */
if (RNA_struct_py_type_get(srna)) {
if (PyObject *old_py_class = static_cast<PyObject *>(RNA_struct_py_type_get(srna))) {
RNA_struct_py_type_set(srna, nullptr);
#if 0
/* Should be able to do this XXX since the old RNA adds a new ref. */
Py_DECREF(py_class);
Py_DECREF(old_py_class);
#else
UNUSED_VARS(old_py_class);
#endif
}

View File

@@ -232,10 +232,7 @@ int pyrna_struct_as_ptr_or_null_parse(PyObject *o, void *p);
void pyrna_struct_type_extend_capi(StructRNA *srna, PyMethodDef *method, PyGetSetDef *getset);
/* Called before stopping Python. */
void pyrna_alloc_types(void);
void pyrna_free_types(void);
/* Primitive type conversion. */