Fix T88033: Python reference memory leaks for non main data-blocks
ID data-blocks that could be accessed from Python and weren't freed using BKE_id_free_ex did not release the Python reference count. Add BKE_libblock_free_data_py function to clear the Python reference in this case. Add asserts to ensure no Python reference is held in situations when ID's are copied for internal use (not exposed through the RNA API), to ensure these kinds of leaks don't go by unnoticed again.
This commit is contained in:
parent
18fbcaf7b9
commit
cbc671947a
Notes:
blender-bot
2023-02-14 03:29:37 +01:00
Referenced by issue #88449, Blender LTS: Maintenance Task 2.93 Referenced by issue #88033, Memory leak after importing a model/ toggling shading workspace
|
@ -201,6 +201,8 @@ enum {
|
|||
void BKE_libblock_free_datablock(struct ID *id, const int flag) ATTR_NONNULL();
|
||||
void BKE_libblock_free_data(struct ID *id, const bool do_id_user) ATTR_NONNULL();
|
||||
|
||||
void BKE_libblock_free_data_py(struct ID *id);
|
||||
|
||||
void BKE_id_free_ex(struct Main *bmain, void *idv, int flag, const bool use_flag_from_idtag);
|
||||
void BKE_id_free(struct Main *bmain, void *idv);
|
||||
|
||||
|
|
|
@ -521,6 +521,7 @@ void BKE_gpencil_eval_delete(bGPdata *gpd_eval)
|
|||
{
|
||||
BKE_gpencil_free(gpd_eval, true);
|
||||
BKE_libblock_free_data(&gpd_eval->id, false);
|
||||
BLI_assert(!gpd_eval->id.py_instance); /* Or call #BKE_libblock_free_data_py. */
|
||||
MEM_freeN(gpd_eval);
|
||||
}
|
||||
|
||||
|
|
|
@ -142,14 +142,7 @@ void BKE_id_free_ex(Main *bmain, void *idv, int flag, const bool use_flag_from_i
|
|||
DEG_id_type_tag(bmain, type);
|
||||
}
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
# ifdef WITH_PYTHON_SAFETY
|
||||
BPY_id_release(id);
|
||||
# endif
|
||||
if (id->py_instance) {
|
||||
BPY_DECREF_RNA_INVALIDATE(id->py_instance);
|
||||
}
|
||||
#endif
|
||||
BKE_libblock_free_data_py(id);
|
||||
|
||||
Key *key = ((flag & LIB_ID_FREE_NO_MAIN) == 0) ? BKE_key_from_id(id) : NULL;
|
||||
|
||||
|
@ -406,3 +399,29 @@ size_t BKE_id_multi_tagged_delete(Main *bmain)
|
|||
{
|
||||
return id_delete(bmain, true);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Python Data Handling
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* In most cases #BKE_id_free_ex handles this, when lower level functions are called directly
|
||||
* this function will need to be called too, if Python has access to the data.
|
||||
*
|
||||
* ID data-blocks such as #Material.nodetree are not stored in #Main.
|
||||
*/
|
||||
void BKE_libblock_free_data_py(ID *id)
|
||||
{
|
||||
#ifdef WITH_PYTHON
|
||||
# ifdef WITH_PYTHON_SAFETY
|
||||
BPY_id_release(id);
|
||||
# endif
|
||||
if (id->py_instance) {
|
||||
BPY_DECREF_RNA_INVALIDATE(id->py_instance);
|
||||
}
|
||||
#else
|
||||
UNUSED_VARS(id);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -1802,6 +1802,7 @@ void BKE_material_copybuf_free(void)
|
|||
{
|
||||
if (matcopybuf.nodetree) {
|
||||
ntreeFreeLocalTree(matcopybuf.nodetree);
|
||||
BLI_assert(!matcopybuf.nodetree->id.py_instance); /* Or call #BKE_libblock_free_data_py. */
|
||||
MEM_freeN(matcopybuf.nodetree);
|
||||
matcopybuf.nodetree = NULL;
|
||||
}
|
||||
|
|
|
@ -3194,6 +3194,7 @@ void ntreeFreeEmbeddedTree(bNodeTree *ntree)
|
|||
{
|
||||
ntreeFreeTree(ntree);
|
||||
BKE_libblock_free_data(&ntree->id, true);
|
||||
BKE_libblock_free_data_py(&ntree->id);
|
||||
}
|
||||
|
||||
void ntreeFreeLocalTree(bNodeTree *ntree)
|
||||
|
|
|
@ -4761,6 +4761,7 @@ static void particle_settings_free_local(ParticleSettings *particle_settings)
|
|||
{
|
||||
BKE_libblock_free_datablock(&particle_settings->id, 0);
|
||||
BKE_libblock_free_data(&particle_settings->id, false);
|
||||
BLI_assert(!particle_settings->id.py_instance); /* Or call #BKE_libblock_free_data_py. */
|
||||
MEM_freeN(particle_settings);
|
||||
}
|
||||
|
||||
|
|
|
@ -446,6 +446,7 @@ static void scene_free_data(ID *id)
|
|||
* collections in the scene need to do it too? */
|
||||
if (scene->master_collection) {
|
||||
BKE_collection_free_data(scene->master_collection);
|
||||
BKE_libblock_free_data_py(&scene->master_collection->id);
|
||||
MEM_freeN(scene->master_collection);
|
||||
scene->master_collection = NULL;
|
||||
}
|
||||
|
|
|
@ -1037,6 +1037,7 @@ static void prefetch_freejob(void *pjv)
|
|||
if (clip_local != NULL) {
|
||||
BKE_libblock_free_datablock(&clip_local->id, 0);
|
||||
BKE_libblock_free_data(&clip_local->id, false);
|
||||
BLI_assert(!clip_local->id.py_instance); /* Or call #BKE_libblock_free_data_py. */
|
||||
MEM_freeN(clip_local);
|
||||
}
|
||||
|
||||
|
|
|
@ -758,6 +758,7 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene,
|
|||
/* Only free after GPU_pass_shader_get where GPUUniformBuf
|
||||
* read data from the local tree. */
|
||||
ntreeFreeLocalTree(localtree);
|
||||
BLI_assert(!localtree->id.py_instance); /* Or call #BKE_libblock_free_data_py. */
|
||||
MEM_freeN(localtree);
|
||||
|
||||
/* note that even if building the shader fails in some way, we still keep
|
||||
|
|
|
@ -531,6 +531,7 @@ static void ntree_shader_groups_flatten(bNodeTree *localtree)
|
|||
bNodeTree *ngroup = (bNodeTree *)node->id;
|
||||
ntreeFreeLocalNode(localtree, node);
|
||||
ntreeFreeTree(ngroup);
|
||||
BLI_assert(!ngroup->id.py_instance); /* Or call #BKE_libblock_free_data_py. */
|
||||
MEM_freeN(ngroup);
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -628,6 +628,7 @@ void wm_close_and_free_all(bContext *C, ListBase *wmlist)
|
|||
wm_close_and_free(C, wm);
|
||||
BLI_remlink(wmlist, wm);
|
||||
BKE_libblock_free_data(&wm->id, true);
|
||||
BKE_libblock_free_data_py(&wm->id);
|
||||
MEM_freeN(wm);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue