Skip to content

Commit 4f3832c

Browse files
committed
Add destruction of python function wrapping metacall function by hooking into tp_dealloc method of PyCFunction_Type.
1 parent 30f18ec commit 4f3832c

File tree

1 file changed

+32
-4
lines changed

1 file changed

+32
-4
lines changed

source/loaders/py_loader/source/py_loader_impl.c

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,9 @@ static PyMethodDef py_loader_impl_function_type_invoke_defs[] = {
225225
static int py_loader_impl_run_main = 1;
226226
static char *py_loader_impl_main_module = NULL;
227227

228+
/* Holds reference to the original PyCFunction.tp_dealloc method */
229+
static void (*py_loader_impl_pycfunction_dealloc)(PyObject *) = NULL;
230+
228231
void py_loader_impl_value_invoke_state_finalize(value v, void *data)
229232
{
230233
PyObject *capsule = (PyObject *)data;
@@ -1290,10 +1293,6 @@ PyObject *py_loader_impl_value_to_capi(loader_impl impl, type_id id, value v)
12901293

12911294
invoke_state->impl = impl;
12921295
invoke_state->py_impl = loader_impl_get(impl);
1293-
1294-
/* TODO: This line causes a memory leak because the function copy is never destroyed.
1295-
We should add a tp_finalizer field into the returned PyCFunction in order to execute
1296-
a callback where value_type_destroy is executed against invoke_state->callback */
12971296
invoke_state->callback = value_type_copy(v);
12981297

12991298
invoke_state_capsule = PyCapsule_New(invoke_state, NULL, NULL);
@@ -2282,6 +2281,31 @@ int py_loader_impl_initialize_argv(loader_impl_py py_impl, int argc, char **argv
22822281
return 1;
22832282
}
22842283

2284+
static void PyCFunction_dealloc(PyObject *obj)
2285+
{
2286+
/* Check if we are passing our own hook to the callback */
2287+
if (PyCFunction_Check(obj) && PyCFunction_GET_FUNCTION(obj) == py_loader_impl_function_type_invoke)
2288+
{
2289+
PyObject *error_type, *error_value, *error_traceback;
2290+
2291+
/* Save the current exception if any */
2292+
PyErr_Fetch(&error_type, &error_value, &error_traceback);
2293+
2294+
PyObject *invoke_state_capsule = PyCFunction_GET_SELF(obj);
2295+
2296+
loader_impl_py_function_type_invoke_state invoke_state = PyCapsule_GetPointer(invoke_state_capsule, NULL);
2297+
2298+
value_type_destroy(invoke_state->callback);
2299+
2300+
Py_DECREF(invoke_state_capsule);
2301+
2302+
PyErr_Restore(error_type, error_value, error_traceback);
2303+
}
2304+
2305+
/* Call to the original meth_dealloc function */
2306+
py_loader_impl_pycfunction_dealloc(obj);
2307+
}
2308+
22852309
loader_impl_data py_loader_impl_initialize(loader_impl impl, configuration config)
22862310
{
22872311
(void)impl;
@@ -2298,6 +2322,10 @@ loader_impl_data py_loader_impl_initialize(loader_impl impl, configuration confi
22982322
goto error_alloc_py_impl;
22992323
}
23002324

2325+
/* Hook the deallocation of PyCFunction */
2326+
py_loader_impl_pycfunction_dealloc = PyCFunction_Type.tp_dealloc;
2327+
PyCFunction_Type.tp_dealloc = PyCFunction_dealloc;
2328+
23012329
Py_InitializeEx(0);
23022330

23032331
if (Py_IsInitialized() == 0)

0 commit comments

Comments
 (0)