Fix of important memory leak in Python module
Dear all, As described in the issue https://github.com/syoyo/tinyobjloader/issues/188#issue-385341218 there is a memory leak in the function pyLoadObj(). The reference count for all created Python objects must be decreased after they are fed into the lists and dictionaries. The only exception from this is the function PyTuple_SetItem() in pyTupleFromfloat3(), which decreases the reference counter of the Python object *tuple automatically by calling this function. In all other cases like PyList_Insert(), the references are only borrowed and not decreased. Therefore, each created Python object will remain in the heap, since there is one reference to it left in the counter. These facts are explained in https://docs.python.org/2/c-api/intro.html#reference-counts in detail. Best regards Martin. P.S. sorry, that i did not put that much effort into a more readable code and just inserted the Py_DECREF() function at every necessary position.
This commit is contained in:
148
python/main.cpp
148
python/main.cpp
@@ -18,11 +18,9 @@ typedef std::vector<int> vecti;
|
|||||||
PyObject* pyTupleFromfloat3(float array[3]) {
|
PyObject* pyTupleFromfloat3(float array[3]) {
|
||||||
int i;
|
int i;
|
||||||
PyObject* tuple = PyTuple_New(3);
|
PyObject* tuple = PyTuple_New(3);
|
||||||
|
|
||||||
for (i = 0; i <= 2; i++) {
|
for (i = 0; i <= 2; i++) {
|
||||||
PyTuple_SetItem(tuple, i, PyFloat_FromDouble(array[i]));
|
PyTuple_SetItem(tuple, i, PyFloat_FromDouble(array[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
return tuple;
|
return tuple;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,32 +28,27 @@ extern "C" {
|
|||||||
|
|
||||||
static PyObject* pyLoadObj(PyObject* self, PyObject* args) {
|
static PyObject* pyLoadObj(PyObject* self, PyObject* args) {
|
||||||
PyObject *rtndict, *pyshapes, *pymaterials, *pymaterial_indices, *attribobj, *current, *meshobj;
|
PyObject *rtndict, *pyshapes, *pymaterials, *pymaterial_indices, *attribobj, *current, *meshobj;
|
||||||
|
|
||||||
char const* current_name;
|
char const* current_name;
|
||||||
char const* filename;
|
char const* filename;
|
||||||
vectd vect;
|
vectd vect;
|
||||||
std::vector<tinyobj::index_t> indices;
|
std::vector<tinyobj::index_t> indices;
|
||||||
std::vector<unsigned char> face_verts;
|
std::vector<unsigned char> face_verts;
|
||||||
|
|
||||||
tinyobj::attrib_t attrib;
|
tinyobj::attrib_t attrib;
|
||||||
std::vector<tinyobj::shape_t> shapes;
|
std::vector<tinyobj::shape_t> shapes;
|
||||||
std::vector<tinyobj::material_t> materials;
|
std::vector<tinyobj::material_t> materials;
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "s", &filename)) return NULL;
|
if (!PyArg_ParseTuple(args, "s", &filename)) return NULL;
|
||||||
|
|
||||||
std::string err;
|
std::string err, warn;
|
||||||
tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename);
|
tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, filename);
|
||||||
|
|
||||||
pyshapes = PyDict_New();
|
pyshapes = PyDict_New();
|
||||||
pymaterials = PyDict_New();
|
pymaterials = PyDict_New();
|
||||||
pymaterial_indices = PyList_New(0);
|
pymaterial_indices = PyList_New(0);
|
||||||
rtndict = PyDict_New();
|
rtndict = PyDict_New();
|
||||||
|
|
||||||
attribobj = PyDict_New();
|
attribobj = PyDict_New();
|
||||||
|
|
||||||
for (int i = 0; i <= 2; i++) {
|
for (int i = 0; i <= 2; i++) {
|
||||||
current = PyList_New(0);
|
current = PyList_New(0);
|
||||||
|
|
||||||
switch (i) {
|
switch (i) {
|
||||||
case 0:
|
case 0:
|
||||||
current_name = "vertices";
|
current_name = "vertices";
|
||||||
@@ -72,124 +65,137 @@ static PyObject* pyLoadObj(PyObject* self, PyObject* args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (vectd::iterator it = vect.begin(); it != vect.end(); it++) {
|
for (vectd::iterator it = vect.begin(); it != vect.end(); it++) {
|
||||||
PyList_Insert(current, it - vect.begin(), PyFloat_FromDouble(*it));
|
PyObject* value = PyFloat_FromDouble(*it);
|
||||||
|
PyList_Insert(current, it - vect.begin(), value);
|
||||||
|
Py_DECREF(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
PyDict_SetItemString(attribobj, current_name, current);
|
PyDict_SetItemString(attribobj, current_name, current);
|
||||||
|
Py_DECREF(current);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (std::vector<tinyobj::shape_t>::iterator shape = shapes.begin();
|
for (std::vector<tinyobj::shape_t>::iterator shape = shapes.begin(); shape != shapes.end(); shape++) {
|
||||||
shape != shapes.end(); shape++) {
|
|
||||||
meshobj = PyDict_New();
|
meshobj = PyDict_New();
|
||||||
tinyobj::mesh_t cm = (*shape).mesh;
|
tinyobj::mesh_t cm = (*shape).mesh;
|
||||||
|
|
||||||
{
|
{
|
||||||
current = PyList_New(0);
|
current = PyList_New(0);
|
||||||
|
|
||||||
for (size_t i = 0; i < cm.indices.size(); i++) {
|
for (size_t i = 0; i < cm.indices.size(); i++) {
|
||||||
// Flatten index array: v_idx, vn_idx, vt_idx, v_idx, vn_idx, vt_idx,
|
// Flatten index array: v_idx, vn_idx, vt_idx, v_idx, vn_idx, vt_idx,
|
||||||
// ...
|
PyObject* value = PyLong_FromLong(cm.indices[i].vertex_index);
|
||||||
PyList_Insert(current, 3 * i + 0,
|
PyList_Insert(current, 3 * i + 0, value);
|
||||||
PyLong_FromLong(cm.indices[i].vertex_index));
|
Py_DECREF(value);
|
||||||
PyList_Insert(current, 3 * i + 1,
|
value = PyLong_FromLong(cm.indices[i].normal_index);
|
||||||
PyLong_FromLong(cm.indices[i].normal_index));
|
PyList_Insert(current, 3 * i + 1, value);
|
||||||
PyList_Insert(current, 3 * i + 2,
|
Py_DECREF(value);
|
||||||
PyLong_FromLong(cm.indices[i].texcoord_index));
|
value = PyLong_FromLong(cm.indices[i].texcoord_index);
|
||||||
|
PyList_Insert(current, 3 * i + 2, value);
|
||||||
|
Py_DECREF(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
PyDict_SetItemString(meshobj, "indices", current);
|
PyDict_SetItemString(meshobj, "indices", current);
|
||||||
|
Py_DECREF(current);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
current = PyList_New(0);
|
current = PyList_New(0);
|
||||||
|
|
||||||
for (size_t i = 0; i < cm.num_face_vertices.size(); i++) {
|
for (size_t i = 0; i < cm.num_face_vertices.size(); i++) {
|
||||||
// Widen data type to long.
|
// Widen data type to long.
|
||||||
PyList_Insert(current, i, PyLong_FromLong(cm.num_face_vertices[i]));
|
PyObject* value = PyLong_FromLong(cm.num_face_vertices[i]);
|
||||||
|
PyList_Insert(current, i, value);
|
||||||
|
Py_DECREF(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
PyDict_SetItemString(meshobj, "num_face_vertices", current);
|
PyDict_SetItemString(meshobj, "num_face_vertices", current);
|
||||||
|
Py_DECREF(current);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
current = PyList_New(0);
|
current = PyList_New(0);
|
||||||
|
|
||||||
for (size_t i = 0; i < cm.material_ids.size(); i++) {
|
for (size_t i = 0; i < cm.material_ids.size(); i++) {
|
||||||
PyList_Insert(current, i, PyLong_FromLong(cm.material_ids[i]));
|
PyObject* value = PyLong_FromLong(cm.material_ids[i]);
|
||||||
|
PyList_Insert(current, i, value);
|
||||||
|
Py_DECREF(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
PyDict_SetItemString(meshobj, "material_ids", current);
|
PyDict_SetItemString(meshobj, "material_ids", current);
|
||||||
|
Py_DECREF(current);
|
||||||
}
|
}
|
||||||
|
|
||||||
PyDict_SetItemString(pyshapes, (*shape).name.c_str(), meshobj);
|
PyDict_SetItemString(pyshapes, (*shape).name.c_str(), meshobj);
|
||||||
|
Py_DECREF(meshobj);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (std::vector<tinyobj::material_t>::iterator mat = materials.begin();
|
for (std::vector<tinyobj::material_t>::iterator mat = materials.begin(); mat != materials.end(); mat++) {
|
||||||
mat != materials.end(); mat++) {
|
|
||||||
PyObject* matobj = PyDict_New();
|
PyObject* matobj = PyDict_New();
|
||||||
PyObject* unknown_parameter = PyDict_New();
|
PyObject* unknown_parameter = PyDict_New();
|
||||||
|
|
||||||
for (std::map<std::string, std::string>::iterator p =
|
for (std::map<std::string, std::string>::iterator p = mat->unknown_parameter.begin(); p != mat->unknown_parameter.end(); ++p) {
|
||||||
mat->unknown_parameter.begin();
|
PyObject* value = PyUnicode_FromString(p->second.c_str());
|
||||||
p != mat->unknown_parameter.end(); ++p) {
|
PyDict_SetItemString(unknown_parameter, p->first.c_str(), value);
|
||||||
PyDict_SetItemString(unknown_parameter, p->first.c_str(),
|
Py_DECREF(value);
|
||||||
PyUnicode_FromString(p->second.c_str()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PyDict_SetItemString(matobj, "shininess",
|
PyObject* value = PyFloat_FromDouble(mat->shininess);
|
||||||
PyFloat_FromDouble(mat->shininess));
|
PyDict_SetItemString(matobj, "shininess", value);
|
||||||
PyDict_SetItemString(matobj, "ior", PyFloat_FromDouble(mat->ior));
|
Py_DECREF(value);
|
||||||
PyDict_SetItemString(matobj, "dissolve",
|
value = PyFloat_FromDouble(mat->ior);
|
||||||
PyFloat_FromDouble(mat->dissolve));
|
PyDict_SetItemString(matobj, "ior", value);
|
||||||
PyDict_SetItemString(matobj, "illum", PyLong_FromLong(mat->illum));
|
Py_DECREF(value);
|
||||||
PyDict_SetItemString(matobj, "ambient_texname",
|
value = PyFloat_FromDouble(mat->dissolve);
|
||||||
PyUnicode_FromString(mat->ambient_texname.c_str()));
|
PyDict_SetItemString(matobj, "dissolve", value);
|
||||||
PyDict_SetItemString(matobj, "diffuse_texname",
|
Py_DECREF(value);
|
||||||
PyUnicode_FromString(mat->diffuse_texname.c_str()));
|
value = PyLong_FromLong(mat->illum);
|
||||||
PyDict_SetItemString(matobj, "specular_texname",
|
PyDict_SetItemString(matobj, "illum", value);
|
||||||
PyUnicode_FromString(mat->specular_texname.c_str()));
|
Py_DECREF(value);
|
||||||
PyDict_SetItemString(
|
value = PyUnicode_FromString(mat->ambient_texname.c_str());
|
||||||
matobj, "specular_highlight_texname",
|
PyDict_SetItemString(matobj, "ambient_texname", value);
|
||||||
PyUnicode_FromString(mat->specular_highlight_texname.c_str()));
|
Py_DECREF(value);
|
||||||
PyDict_SetItemString(matobj, "bump_texname",
|
value = PyUnicode_FromString(mat->diffuse_texname.c_str());
|
||||||
PyUnicode_FromString(mat->bump_texname.c_str()));
|
PyDict_SetItemString(matobj, "diffuse_texname", value);
|
||||||
PyDict_SetItemString(
|
Py_DECREF(value);
|
||||||
matobj, "displacement_texname",
|
value = PyUnicode_FromString(mat->specular_texname.c_str());
|
||||||
PyUnicode_FromString(mat->displacement_texname.c_str()));
|
PyDict_SetItemString(matobj, "specular_texname", value);
|
||||||
PyDict_SetItemString(matobj, "alpha_texname",
|
Py_DECREF(value);
|
||||||
PyUnicode_FromString(mat->alpha_texname.c_str()));
|
value = PyUnicode_FromString(mat->specular_highlight_texname.c_str());
|
||||||
|
PyDict_SetItemString(matobj, "specular_highlight_texname", value);
|
||||||
|
Py_DECREF(value);
|
||||||
|
value = PyUnicode_FromString(mat->bump_texname.c_str());
|
||||||
|
PyDict_SetItemString(matobj, "bump_texname", value);
|
||||||
|
Py_DECREF(value);
|
||||||
|
value = PyUnicode_FromString(mat->displacement_texname.c_str());
|
||||||
|
PyDict_SetItemString(matobj, "displacement_texname", value);
|
||||||
|
Py_DECREF(value);
|
||||||
|
value = PyUnicode_FromString(mat->alpha_texname.c_str());
|
||||||
|
PyDict_SetItemString(matobj, "alpha_texname", value);
|
||||||
|
Py_DECREF(value);
|
||||||
PyDict_SetItemString(matobj, "ambient", pyTupleFromfloat3(mat->ambient));
|
PyDict_SetItemString(matobj, "ambient", pyTupleFromfloat3(mat->ambient));
|
||||||
PyDict_SetItemString(matobj, "diffuse", pyTupleFromfloat3(mat->diffuse));
|
PyDict_SetItemString(matobj, "diffuse", pyTupleFromfloat3(mat->diffuse));
|
||||||
PyDict_SetItemString(matobj, "specular",
|
PyDict_SetItemString(matobj, "specular", pyTupleFromfloat3(mat->specular));
|
||||||
pyTupleFromfloat3(mat->specular));
|
PyDict_SetItemString(matobj, "transmittance", pyTupleFromfloat3(mat->transmittance));
|
||||||
PyDict_SetItemString(matobj, "transmittance",
|
PyDict_SetItemString(matobj, "emission", pyTupleFromfloat3(mat->emission));
|
||||||
pyTupleFromfloat3(mat->transmittance));
|
|
||||||
PyDict_SetItemString(matobj, "emission",
|
|
||||||
pyTupleFromfloat3(mat->emission));
|
|
||||||
PyDict_SetItemString(matobj, "unknown_parameter", unknown_parameter);
|
PyDict_SetItemString(matobj, "unknown_parameter", unknown_parameter);
|
||||||
|
Py_DECREF(unknown_parameter);
|
||||||
PyDict_SetItemString(pymaterials, mat->name.c_str(), matobj);
|
PyDict_SetItemString(pymaterials, mat->name.c_str(), matobj);
|
||||||
PyList_Append(pymaterial_indices, PyUnicode_FromString(mat->name.c_str()));
|
Py_DECREF(matobj);
|
||||||
|
value = PyUnicode_FromString(mat->name.c_str());
|
||||||
|
PyList_Append(pymaterial_indices, value);
|
||||||
|
Py_DECREF(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
PyDict_SetItemString(rtndict, "shapes", pyshapes);
|
PyDict_SetItemString(rtndict, "shapes", pyshapes);
|
||||||
|
Py_DECREF(pyshapes);
|
||||||
PyDict_SetItemString(rtndict, "materials", pymaterials);
|
PyDict_SetItemString(rtndict, "materials", pymaterials);
|
||||||
|
Py_DECREF(pymaterials);
|
||||||
PyDict_SetItemString(rtndict, "material_indices", pymaterial_indices);
|
PyDict_SetItemString(rtndict, "material_indices", pymaterial_indices);
|
||||||
|
Py_DECREF(pymaterial_indices);
|
||||||
PyDict_SetItemString(rtndict, "attribs", attribobj);
|
PyDict_SetItemString(rtndict, "attribs", attribobj);
|
||||||
|
Py_DECREF(attribobj);
|
||||||
return rtndict;
|
return rtndict;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyMethodDef mMethods[] = {
|
static PyMethodDef mMethods[] = {
|
||||||
|
|
||||||
{"LoadObj", pyLoadObj, METH_VARARGS}, {NULL, NULL, 0, NULL}
|
{"LoadObj", pyLoadObj, METH_VARARGS}, {NULL, NULL, 0, NULL}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#if PY_MAJOR_VERSION >= 3
|
#if PY_MAJOR_VERSION >= 3
|
||||||
|
|
||||||
static struct PyModuleDef moduledef = {PyModuleDef_HEAD_INIT, "tinyobjloader",
|
static struct PyModuleDef moduledef = {PyModuleDef_HEAD_INIT, "tinyobjloader", NULL, -1, mMethods};
|
||||||
NULL, -1, mMethods};
|
|
||||||
|
|
||||||
PyMODINIT_FUNC PyInit_tinyobjloader(void) {
|
PyMODINIT_FUNC PyInit_tinyobjloader(void) {
|
||||||
return PyModule_Create(&moduledef);
|
return PyModule_Create(&moduledef);
|
||||||
|
|||||||
Reference in New Issue
Block a user