@@ -225,6 +225,9 @@ static PyMethodDef py_loader_impl_function_type_invoke_defs[] = {
225225static int py_loader_impl_run_main = 1 ;
226226static 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+
228231void 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+
22852309loader_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