diff --git a/lgi/callable.c b/lgi/callable.c index dd272ece..47659259 100644 --- a/lgi/callable.c +++ b/lgi/callable.c @@ -1429,6 +1429,15 @@ closure_callback (ffi_cif *cif, void *ret, void **args, void *closure_arg) /* This is NOT called by Lua, so we better leave the Lua stack we used pretty much tidied. */ lua_settop (L, stacktop); + + /* For autodestroy closures, nudge Lua's GC after resetting the stack. + ffi_closure_alloc() memory is invisible to Lua's allocator, so GC + does not naturally respond to ffi memory pressure. A single step + here ensures the guard created above is finalized promptly, which in + turn allows the callback's Lua closure chain to be freed. */ + if (closure->autodestroy) + lua_gc (block->callback.L, LUA_GCSTEP, 10); + if (L != marshal_L) lua_settop (marshal_L, 0); diff --git a/lgi/marshal.c b/lgi/marshal.c index acf86383..c00cfea2 100644 --- a/lgi/marshal.c +++ b/lgi/marshal.c @@ -915,8 +915,22 @@ marshal_2c_callable (lua_State *L, GICallableInfo *ci, GIArgInfo *ai, *lgi_guard_create (L, lgi_closure_destroy) = user_data; nret++; } + else if (scope == GI_SCOPE_TYPE_ASYNC || scope == GI_SCOPE_TYPE_NOTIFIED) + { + /* ASYNC: callback fired once, autodestroy cleans up. + NOTIFIED without destroy param: no destroy will come; treat as + autodestroy and warn since this is unusual. */ + if (scope == GI_SCOPE_TYPE_NOTIFIED) + g_warning ("lgi: NOTIFIED-scope callback without destroy parameter;" + " treating as autodestroy"); + } + else if (scope == GI_SCOPE_TYPE_FOREVER) + { + /* Intentionally no cleanup: callback lives until process exit. */ + } else - g_assert (scope == GI_SCOPE_TYPE_ASYNC); + g_warning ("lgi: unexpected scope type %d for callback without " + "user_data; closure may leak", (int) scope); } /* Create the closure. */