简体   繁体   中英

Python C++ extension - memory leak or access violation

I've written a Python C++ extension, however I have a problem with one of its functions. The function provided by this extension takes 2 arrays as inputs and produces one as an output.

I've only left the relevant part of function's code

float* forward(float* input, float* kernels, npy_intp* input_dims, npy_intp* kernels_dims){

    float* output = new float[output_size];

    //some irrelevant matrix operation code

    return output;
}

And the wrapper:

static PyObject *module_forward(PyObject *self, PyObject *args)
{
    PyObject *input_obj, *kernels_obj;

    if (!PyArg_ParseTuple(args, "OO", &input_obj, &kernels_obj))
        return NULL;

    PyObject *input_array = PyArray_FROM_OTF(input_obj, NPY_FLOAT, NPY_IN_ARRAY);
    PyObject *kernels_array = PyArray_FROM_OTF(kernels_obj, NPY_FLOAT, NPY_IN_ARRAY);

    if (input_array == NULL || kernels_array == NULL) {
        Py_XDECREF(input_array);
        Py_XDECREF(kernels_array);
        return NULL;
    }


    float *input = (float*)PyArray_DATA(input_array);
    float *kernels = (float*)PyArray_DATA(kernels_array);

    npy_intp *input_dims = PyArray_DIMS(input_array);
    npy_intp *kernels_dims = PyArray_DIMS(kernels_array);

    /////////THE ACTUAL FUNCTION
    float* output = forward(input, kernels, input_dims, kernels_dims);


    Py_DECREF(input_array);
    Py_DECREF(kernels_array);

    npy_intp output_dims[4] = {input_dims[0], input_dims[1]-kernels_dims[0]+1, input_dims[2]-kernels_dims[1]+1, kernels_dims[3]};

    PyObject* ret_output = PyArray_SimpleNewFromData(4, output_dims, NPY_FLOAT, output);

    delete output;//<-----THE PROBLEMATIC LINE////////////////////////////

    PyObject *ret = Py_BuildValue("O", ret_output);

    Py_DECREF(ret_output);

    return ret;
}

The delete operator that I highlighted is where the magic happens: without it this function leaks memory, with it it crashes because of memory access violation.

The fun thing is I wrote another method, that returns two arrays. So the function returns a float** pointing to two float* elements:

float** gradients = backward(input, kernels, grads, input_dims, kernel_dims, PyArray_DIMS(grads_array));

Py_DECREF(input_array);
Py_DECREF(kernels_array);
Py_DECREF(grads_array);

PyObject* ret_g_input = PyArray_SimpleNewFromData(4, input_dims, NPY_FLOAT, gradients[0]);
PyObject* ret_g_kernels = PyArray_SimpleNewFromData(4, kernel_dims, NPY_FLOAT, gradients[1]);

delete gradients[0];
delete gradients[1];
delete gradients;

PyObject* ret_list = PyList_New(0);

PyList_Append(ret_list, ret_g_input);
PyList_Append(ret_list, ret_g_kernels);

PyObject *ret = Py_BuildValue("O", ret_list);

Py_DECREF(ret_g_input);
Py_DECREF(ret_g_kernels);

return ret;

Notice that the second example works flawlessly, no crashes or memory leaks, while still calling delete on arrays after they have been built into PyArray objects.

Could someone enlighten me about what's going on in here?

From the PyArray_SimpleNewFromData docs :

Create an array wrapper around data pointed to by the given pointer.

If you create an array with PyArray_SimpleNewFromData , it's going to create a wrapper around the data you give it, rather than making a copy. That means the data it wraps has to outlive the array. delete -ing the data violates that.

You have several options:

  • You could create the array differently so you don't just make a wrapper around the original data.
  • You could carefully control access to the array and make sure its lifetime ends before you delete the data.
  • You could create a Python object that owns the data and will delete the data when the object's lifetime ends, and set the array's base to that object with PyArray_SetBaseObject , so the array keeps the owner object alive until the array itself dies.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM