-- Bugfix #3186: Fix memory leaks caused by multiple calls to
PyDict_SetItemString() with objects that were not properly decrefed afterwards. Due to the number of places this was used, I added a wrapper EXPP_dict_set_item_str() to gen_utils.c to handle it. This started as a scriptlink bug, due to how many times scripts were being executed I think it just magnified how bad the memory leak in BPy was. Animating the blend attached with this bug report would cause memory to grow by about 3MB for every 280 frames. After the patch, memory did not appear to grow at all (or at least not noticably using Unix's ps and top utils). Since many of the PyDict_SetItemString() calls were in initialization modules I think my tests executed most of the changed code, but would appreciate script users really giving feedback.
This commit is contained in:
@@ -223,7 +223,7 @@ void init_syspath( int first_time )
|
||||
|
||||
if( mod ) {
|
||||
d = PyModule_GetDict( mod );
|
||||
PyDict_SetItemString( d, "progname", path );
|
||||
EXPP_dict_set_item_str( d, "progname", path );
|
||||
Py_DECREF( mod );
|
||||
} else
|
||||
printf( "Warning: could not set Blender.sys.progname\n" );
|
||||
@@ -302,7 +302,7 @@ void init_syspath( int first_time )
|
||||
|
||||
if( mod ) {
|
||||
d = PyModule_GetDict( mod ); /* borrowed ref */
|
||||
PyDict_SetItemString( d, "executable",
|
||||
EXPP_dict_set_item_str( d, "executable",
|
||||
Py_BuildValue( "s", bprogname ) );
|
||||
Py_DECREF( mod );
|
||||
}
|
||||
@@ -545,7 +545,7 @@ int BPY_txt_do_python_Text( struct Text *text )
|
||||
PyString_FromString( script->id.name + 2 ) );
|
||||
Py_INCREF( Py_None );
|
||||
PyConstant_Insert( info, "arg", Py_None );
|
||||
PyDict_SetItemString( py_dict, "__script__",
|
||||
EXPP_dict_set_item_str( py_dict, "__script__",
|
||||
( PyObject * ) info );
|
||||
}
|
||||
|
||||
@@ -787,7 +787,7 @@ int BPY_menu_do_python( short menutype, int event )
|
||||
PyConstant_Insert( info, "name",
|
||||
PyString_FromString( script->id.name + 2 ) );
|
||||
PyConstant_Insert( info, "arg", pyarg );
|
||||
PyDict_SetItemString( py_dict, "__script__",
|
||||
EXPP_dict_set_item_str( py_dict, "__script__",
|
||||
( PyObject * ) info );
|
||||
}
|
||||
|
||||
@@ -1081,10 +1081,10 @@ void BPY_do_pyscript( ID * id, short event )
|
||||
disable_where_scriptlink( (short)during_slink );
|
||||
|
||||
/* set globals in Blender module to identify scriptlink */
|
||||
PyDict_SetItemString( g_blenderdict, "bylink", EXPP_incr_ret_True() );
|
||||
PyDict_SetItemString( g_blenderdict, "link",
|
||||
EXPP_dict_set_item_str( g_blenderdict, "bylink", EXPP_incr_ret_True() );
|
||||
EXPP_dict_set_item_str( g_blenderdict, "link",
|
||||
ID_asPyObject( id ) );
|
||||
PyDict_SetItemString( g_blenderdict, "event",
|
||||
EXPP_dict_set_item_str( g_blenderdict, "event",
|
||||
PyString_FromString( event_to_name
|
||||
( event ) ) );
|
||||
|
||||
@@ -1123,10 +1123,9 @@ void BPY_do_pyscript( ID * id, short event )
|
||||
/* cleanup bylink flag and clear link so PyObject
|
||||
* can be released
|
||||
*/
|
||||
PyDict_SetItemString( g_blenderdict, "bylink", EXPP_incr_ret_False() );
|
||||
Py_INCREF( Py_None );
|
||||
EXPP_dict_set_item_str( g_blenderdict, "bylink", EXPP_incr_ret_False() );
|
||||
PyDict_SetItemString( g_blenderdict, "link", Py_None );
|
||||
PyDict_SetItemString( g_blenderdict, "event",
|
||||
EXPP_dict_set_item_str( g_blenderdict, "event",
|
||||
PyString_FromString( "" ) );
|
||||
}
|
||||
}
|
||||
@@ -1302,11 +1301,11 @@ int BPY_do_spacehandlers( ScrArea *sa, unsigned short event,
|
||||
}
|
||||
|
||||
/* set globals in Blender module to identify space handler scriptlink */
|
||||
PyDict_SetItemString(g_blenderdict, "bylink", EXPP_incr_ret_True());
|
||||
EXPP_dict_set_item_str(g_blenderdict, "bylink", EXPP_incr_ret_True());
|
||||
/* unlike normal scriptlinks, here Blender.link is int (space event type) */
|
||||
PyDict_SetItemString(g_blenderdict, "link", PyInt_FromLong(space_event));
|
||||
EXPP_dict_set_item_str(g_blenderdict, "link", PyInt_FromLong(space_event));
|
||||
/* note: DRAW space_events set event to 0 */
|
||||
PyDict_SetItemString(g_blenderdict, "event", PyInt_FromLong(event));
|
||||
EXPP_dict_set_item_str(g_blenderdict, "event", PyInt_FromLong(event));
|
||||
|
||||
/* now run all assigned space handlers for this space and space_event */
|
||||
for( index = 0; index < scriptlink->totscript; index++ ) {
|
||||
@@ -1362,9 +1361,9 @@ int BPY_do_spacehandlers( ScrArea *sa, unsigned short event,
|
||||
|
||||
}
|
||||
|
||||
PyDict_SetItemString(g_blenderdict, "bylink", EXPP_incr_ret_False());
|
||||
PyDict_SetItemString(g_blenderdict, "link", EXPP_incr_ret(Py_None));
|
||||
PyDict_SetItemString(g_blenderdict, "event", PyString_FromString(""));
|
||||
EXPP_dict_set_item_str(g_blenderdict, "bylink", EXPP_incr_ret_False());
|
||||
PyDict_SetItemString(g_blenderdict, "link", Py_None );
|
||||
EXPP_dict_set_item_str(g_blenderdict, "event", PyString_FromString(""));
|
||||
}
|
||||
|
||||
/* retval:
|
||||
@@ -1548,7 +1547,7 @@ PyObject *CreateGlobalDictionary( void )
|
||||
PyObject *dict = PyDict_New( );
|
||||
|
||||
PyDict_SetItemString( dict, "__builtins__", PyEval_GetBuiltins( ) );
|
||||
PyDict_SetItemString( dict, "__name__",
|
||||
EXPP_dict_set_item_str( dict, "__name__",
|
||||
PyString_FromString( "__main__" ) );
|
||||
|
||||
return dict;
|
||||
@@ -1672,7 +1671,7 @@ void init_ourImport( void )
|
||||
|
||||
m = PyImport_AddModule( "__builtin__" );
|
||||
d = PyModule_GetDict( m );
|
||||
PyDict_SetItemString( d, "__import__", import );
|
||||
EXPP_dict_set_item_str( d, "__import__", import );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1772,5 +1771,5 @@ void init_ourReload( void )
|
||||
|
||||
m = PyImport_AddModule( "__builtin__" );
|
||||
d = PyModule_GetDict( m );
|
||||
PyDict_SetItemString( d, "reload", reload );
|
||||
EXPP_dict_set_item_str( d, "reload", reload );
|
||||
}
|
||||
|
||||
@@ -1096,11 +1096,11 @@ PyObject *BGL_Init(void)
|
||||
|
||||
buffer_Type.ob_type = &PyType_Type;
|
||||
|
||||
#define EXPP_ADDCONST(x) PyDict_SetItemString(dict, #x, PyInt_FromLong(x))
|
||||
#define EXPP_ADDCONST(x) EXPP_dict_set_item_str(dict, #x, PyInt_FromLong(x))
|
||||
|
||||
/* So, for example:
|
||||
* EXPP_ADDCONST(GL_CURRENT_BIT) becomes
|
||||
* PyDict_SetItemString(dict, "GL_CURRENT_BIT", PyInt_FromLong(GL_CURRENT_BIT)) */
|
||||
* EXPP_dict_set_item_str(dict, "GL_CURRENT_BIT", PyInt_FromLong(GL_CURRENT_BIT)) */
|
||||
|
||||
EXPP_ADDCONST(GL_CURRENT_BIT);
|
||||
EXPP_ADDCONST(GL_POINT_BIT);
|
||||
|
||||
@@ -416,13 +416,13 @@ static PyObject *Blender_Get( PyObject * self, PyObject * args )
|
||||
else if( StringEqual( str, "vrmloptions" ) ) {
|
||||
ret = PyDict_New( );
|
||||
|
||||
PyDict_SetItemString( dict, "twoside",
|
||||
EXPP_dict_set_item_str( ret, "twoside",
|
||||
PyInt_FromLong( U.vrmlflag & USER_VRML_TWOSIDED ) );
|
||||
|
||||
PyDict_SetItemString( dict, "layers",
|
||||
EXPP_dict_set_item_str( ret, "layers",
|
||||
PyInt_FromLong( U.vrmlflag & USER_VRML_LAYERS ) );
|
||||
|
||||
PyDict_SetItemString( dict, "autoscale",
|
||||
EXPP_dict_set_item_str( ret, "autoscale",
|
||||
PyInt_FromLong( U.vrmlflag & USER_VRML_AUTOSCALE ) );
|
||||
|
||||
} /* End 'quick hack' part. */
|
||||
@@ -678,8 +678,8 @@ static PyObject *Blender_ShowHelp(PyObject *self, PyObject *args)
|
||||
return EXPP_ReturnPyObjError(PyExc_MemoryError,
|
||||
"can't create py dictionary!");
|
||||
|
||||
PyDict_SetItemString(rkeyd, "script", script);
|
||||
PyDict_SetItemString(bpy_registryDict, "__help_browser", rkeyd);
|
||||
EXPP_dict_set_item_str(rkeyd, "script", script);
|
||||
EXPP_dict_set_item_str(bpy_registryDict, "__help_browser", rkeyd);
|
||||
|
||||
arglist = Py_BuildValue("(s)", hspath);
|
||||
Blender_Run(self, arglist);
|
||||
@@ -798,10 +798,10 @@ void M_Blender_Init(void)
|
||||
PyModule_AddIntConstant(module, "TRUE", 1);
|
||||
PyModule_AddIntConstant( module, "FALSE", 0 );
|
||||
|
||||
PyDict_SetItemString(dict, "bylink", EXPP_incr_ret_False());
|
||||
PyDict_SetItemString(dict, "link", EXPP_incr_ret (Py_None));
|
||||
PyDict_SetItemString(dict, "event", PyString_FromString(""));
|
||||
PyDict_SetItemString(dict, "mode", smode);
|
||||
EXPP_dict_set_item_str(dict, "bylink", EXPP_incr_ret_False());
|
||||
PyDict_SetItemString(dict, "link", Py_None);
|
||||
EXPP_dict_set_item_str(dict, "event", PyString_FromString(""));
|
||||
EXPP_dict_set_item_str(dict, "mode", smode);
|
||||
|
||||
PyDict_SetItemString(dict, "Armature", Armature_Init());
|
||||
PyDict_SetItemString(dict, "BezTriple", BezTriple_Init());
|
||||
|
||||
@@ -534,12 +534,14 @@ void BPY_spacescript_do_pywin_event( SpaceScript * sc, unsigned short event,
|
||||
int pass_ascii = 0;
|
||||
if (ascii > 31 && ascii != 127) {
|
||||
pass_ascii = 1;
|
||||
PyDict_SetItemString(g_blenderdict, "event", PyInt_FromLong((long)ascii));
|
||||
EXPP_dict_set_item_str(g_blenderdict, "event",
|
||||
PyInt_FromLong((long)ascii));
|
||||
}
|
||||
exec_callback( sc, sc->script->py_event,
|
||||
Py_BuildValue( "(ii)", event, val ) );
|
||||
if (pass_ascii)
|
||||
PyDict_SetItemString(g_blenderdict, "event", PyString_FromString(""));
|
||||
EXPP_dict_set_item_str(g_blenderdict, "event",
|
||||
PyString_FromString(""));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1392,11 +1394,11 @@ PyObject *Draw_Init( void )
|
||||
dict = PyModule_GetDict( submodule );
|
||||
|
||||
#define EXPP_ADDCONST(x) \
|
||||
PyDict_SetItemString(dict, #x, PyInt_FromLong(x))
|
||||
EXPP_dict_set_item_str(dict, #x, PyInt_FromLong(x))
|
||||
|
||||
/* So, for example:
|
||||
* EXPP_ADDCONST(LEFTMOUSE) becomes
|
||||
* PyDict_SetItemString(dict, "LEFTMOUSE", PyInt_FromLong(LEFTMOUSE))
|
||||
* EXPP_dict_set_item_str(dict, "LEFTMOUSE", PyInt_FromLong(LEFTMOUSE))
|
||||
*/
|
||||
|
||||
EXPP_ADDCONST( LEFTMOUSE );
|
||||
|
||||
@@ -734,7 +734,7 @@ PyObject *Effect_Init( void )
|
||||
|
||||
dict = PyModule_GetDict( submodule );
|
||||
|
||||
PyDict_SetItemString( dict, "Particle", particle );
|
||||
EXPP_dict_set_item_str( dict, "Particle", particle );
|
||||
return ( submodule );
|
||||
}
|
||||
|
||||
|
||||
@@ -369,12 +369,12 @@ PyObject *Lattice_Init( void )
|
||||
Lattice_Type.ob_type = &PyType_Type;
|
||||
|
||||
//Module dictionary
|
||||
#define EXPP_ADDCONST(x) PyDict_SetItemString(dict, #x, PyInt_FromLong(LT_##x))
|
||||
#define EXPP_ADDCONST(x) EXPP_dict_set_item_str(dict, #x, PyInt_FromLong(LT_##x))
|
||||
EXPP_ADDCONST( GRID );
|
||||
EXPP_ADDCONST( OUTSIDE );
|
||||
|
||||
#undef EXPP_ADDCONST
|
||||
#define EXPP_ADDCONST(x) PyDict_SetItemString(dict, #x, PyInt_FromLong(KEY_##x))
|
||||
#define EXPP_ADDCONST(x) EXPP_dict_set_item_str(dict, #x, PyInt_FromLong(KEY_##x))
|
||||
EXPP_ADDCONST( LINEAR );
|
||||
EXPP_ADDCONST( CARDINAL );
|
||||
EXPP_ADDCONST( BSPLINE );
|
||||
|
||||
@@ -158,8 +158,8 @@ PyObject *sys_Init( void )
|
||||
|
||||
if( sep ) {
|
||||
Py_INCREF( sep );
|
||||
PyDict_SetItemString( dict, "dirsep", sep );
|
||||
PyDict_SetItemString( dict, "sep", sep );
|
||||
EXPP_dict_set_item_str( dict, "dirsep", sep );
|
||||
EXPP_dict_set_item_str( dict, "sep", sep );
|
||||
}
|
||||
|
||||
return submodule;
|
||||
|
||||
@@ -74,7 +74,7 @@ static PyObject *new_const(void)
|
||||
"couldn't create constant object's dictionary"));
|
||||
}
|
||||
|
||||
return EXPP_incr_ret((PyObject *)constant);
|
||||
return (PyObject *)constant;
|
||||
}
|
||||
//------------------------tp_doc
|
||||
//The __doc__ string for this object
|
||||
@@ -230,7 +230,7 @@ PyObject *PyConstant_New(void)
|
||||
//Inserts a key:value pair into the constant and then returns 0/1
|
||||
int PyConstant_Insert(BPy_constant *self, char *name, PyObject *value)
|
||||
{
|
||||
return PyDict_SetItemString(self->dict, name, value);
|
||||
return EXPP_dict_set_item_str(self->dict, name, value);
|
||||
}
|
||||
//This is a helper function for generating constants......
|
||||
PyObject *PyConstant_NewInt(char *name, int value)
|
||||
|
||||
@@ -904,3 +904,16 @@ PyObject *EXPP_setterWrapperTuple ( PyObject * self, PyObject * args,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper to keep dictionaries from causing memory leaks. When some object
|
||||
* is just created to be added to the dictionary, its reference count needs
|
||||
* to be decremented so it can be reclaimed.
|
||||
*/
|
||||
|
||||
int EXPP_dict_set_item_str( PyObject *dict, char *key, PyObject *value)
|
||||
{
|
||||
/* add value to dictionary */
|
||||
int ret = PyDict_SetItemString(dict, key, value);
|
||||
Py_DECREF( value ); /* delete original */
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -142,5 +142,8 @@ PyObject *EXPP_clearScriptLinks(ScriptLink *slink, PyObject *args);
|
||||
/* this queues redraws if we're not in background mode: */
|
||||
void EXPP_allqueue(unsigned short event, short val);
|
||||
|
||||
/* helper to keep dictionaries from causing memory leaks */
|
||||
int EXPP_dict_set_item_str( PyObject *dict, char *key, PyObject *value);
|
||||
|
||||
#endif /* EXPP_gen_utils_h */
|
||||
|
||||
|
||||
@@ -234,8 +234,7 @@ static void insertname(PyObject *m,PyObject *p, char *name)
|
||||
{
|
||||
PyObject *d = PyModule_GetDict(m);
|
||||
|
||||
PyDict_SetItemString(d, name, p);
|
||||
Py_DECREF(p);
|
||||
EXPP_dict_set_item_str(d, name, p);
|
||||
}
|
||||
|
||||
/* initialisation */
|
||||
@@ -243,8 +242,6 @@ static void initprot()
|
||||
{
|
||||
PyObject *m, *d;
|
||||
PyObject *capi1;
|
||||
PyObject *ErrorObject;
|
||||
|
||||
init_ftable();
|
||||
|
||||
g_main = PyImport_AddModule("__main__");
|
||||
@@ -254,8 +251,7 @@ static void initprot()
|
||||
(PyObject*)NULL,PYTHON_API_VERSION);
|
||||
g_module_self = m;
|
||||
d = PyModule_GetDict(m);
|
||||
ErrorObject = PyString_FromString("prot.error");
|
||||
PyDict_SetItemString(d, "error", ErrorObject);
|
||||
EXPP_dict_set_item_str(d, "error", PyString_FromString("prot.error");
|
||||
|
||||
/* add global object */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user