generator: Dispose ownable parameters in signal callbacks (bxc#237)

A similar situation to what is described in commit e48ac63d54 also
happens with signal callbacks: some signals are passed a native object
that is wrapped in an IDisposable managed object, which is then passed
as an argument to the signal handler. We need to dispose those objects
when the signal handler is done.

Those parameters will now be disposed in a finally {...} block, after
the signal handler has returned. This means that handlers should not
keep a reference to such a parameter, as it will be disposed right after
they return.

This change only affects the Cairo.Context parameter of the Widget.Drawn
signal, but it was badly needed, as shown by the Pixbuf demo in the
GtkDemo sample, which was leaking tens of MBs of memory.
This commit is contained in:
Bertrand Lorentz 2013-11-17 19:10:24 +01:00
parent b7df1d2e13
commit 51f102bc34

View file

@ -25,6 +25,7 @@ namespace GtkSharp.Generation {
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Xml; using System.Xml;
@ -157,7 +158,7 @@ namespace GtkSharp.Generation {
} }
} }
public string GenArgsInitialization (StreamWriter sw) private string GenArgsInitialization (StreamWriter sw, IList<Parameter> dispose_params)
{ {
if (parms.Count > 0) if (parms.Count > 0)
sw.WriteLine("\t\t\t\targs.Args = new object[" + parms.Count + "];"); sw.WriteLine("\t\t\t\targs.Args = new object[" + parms.Count + "];");
@ -172,8 +173,12 @@ namespace GtkSharp.Generation {
sw.WriteLine("\t\t\t\telse {"); sw.WriteLine("\t\t\t\telse {");
sw.WriteLine("\t\t\t\t\targs.Args[" + idx + "] = " + p.FromNative ("arg" + idx) + ";"); sw.WriteLine("\t\t\t\t\targs.Args[" + idx + "] = " + p.FromNative ("arg" + idx) + ";");
sw.WriteLine("\t\t\t\t}"); sw.WriteLine("\t\t\t\t}");
} else } else if (dispose_params.Contains (p)) {
sw.WriteLine("\t\t\t\t" + p.Name + " = " + p.FromNative ("arg" + idx) + ";");
sw.WriteLine("\t\t\t\targs.Args[" + idx + "] = " + p.Name + ";");
} else {
sw.WriteLine("\t\t\t\targs.Args[" + idx + "] = " + p.FromNative ("arg" + idx) + ";"); sw.WriteLine("\t\t\t\targs.Args[" + idx + "] = " + p.FromNative ("arg" + idx) + ";");
}
} }
if ((igen is StructBase || igen is ByRefGen) && p.PassAs != "") if ((igen is StructBase || igen is ByRefGen) && p.PassAs != "")
finish += "\t\t\t\tif (arg" + idx + " != IntPtr.Zero) System.Runtime.InteropServices.Marshal.StructureToPtr (args.Args[" + idx + "], arg" + idx + ", false);\n"; finish += "\t\t\t\tif (arg" + idx + " != IntPtr.Zero) System.Runtime.InteropServices.Marshal.StructureToPtr (args.Args[" + idx + "], arg" + idx + ", false);\n";
@ -185,7 +190,7 @@ namespace GtkSharp.Generation {
return finish; return finish;
} }
public void GenArgsCleanup (StreamWriter sw, string finish) private void GenArgsCleanup (StreamWriter sw, string finish)
{ {
if (retval.IsVoid && finish.Length == 0) if (retval.IsVoid && finish.Length == 0)
return; return;
@ -213,6 +218,13 @@ namespace GtkSharp.Generation {
if (IsEventHandler) if (IsEventHandler)
return; return;
IList<Parameter> dispose_params = new List<Parameter> ();
foreach (Parameter p in parms) {
if (p.IsOwnable) {
dispose_params.Add (p);
}
}
string native_signature = "IntPtr inst"; string native_signature = "IntPtr inst";
if (parms.Count > 0) if (parms.Count > 0)
native_signature += ", " + CallbackSig; native_signature += ", " + CallbackSig;
@ -224,17 +236,30 @@ namespace GtkSharp.Generation {
sw.WriteLine ("\t\tstatic {0} {1} ({2})", retval.ToNativeType, CallbackName, native_signature); sw.WriteLine ("\t\tstatic {0} {1} ({2})", retval.ToNativeType, CallbackName, native_signature);
sw.WriteLine("\t\t{"); sw.WriteLine("\t\t{");
sw.WriteLine("\t\t\t{0} args = new {0} ();", EventArgsQualifiedName); sw.WriteLine("\t\t\t{0} args = new {0} ();", EventArgsQualifiedName);
foreach (Parameter p in dispose_params) {
sw.WriteLine("\t\t\t{0} {1} = null;", p.CSType, p.Name);
}
sw.WriteLine("\t\t\ttry {"); sw.WriteLine("\t\t\ttry {");
sw.WriteLine("\t\t\t\tGLib.Signal sig = ((GCHandle) gch).Target as GLib.Signal;"); sw.WriteLine("\t\t\t\tGLib.Signal sig = ((GCHandle) gch).Target as GLib.Signal;");
sw.WriteLine("\t\t\t\tif (sig == null)"); sw.WriteLine("\t\t\t\tif (sig == null)");
sw.WriteLine("\t\t\t\t\tthrow new Exception(\"Unknown signal GC handle received \" + gch);"); sw.WriteLine("\t\t\t\t\tthrow new Exception(\"Unknown signal GC handle received \" + gch);");
sw.WriteLine(); sw.WriteLine();
string finish = GenArgsInitialization (sw); string finish = GenArgsInitialization (sw, dispose_params);
sw.WriteLine("\t\t\t\t{0} handler = ({0}) sig.Handler;", EventHandlerQualifiedName); sw.WriteLine("\t\t\t\t{0} handler = ({0}) sig.Handler;", EventHandlerQualifiedName);
sw.WriteLine("\t\t\t\thandler (GLib.Object.GetObject (inst), args);"); sw.WriteLine("\t\t\t\thandler (GLib.Object.GetObject (inst), args);");
sw.WriteLine("\t\t\t} catch (Exception e) {"); sw.WriteLine("\t\t\t} catch (Exception e) {");
sw.WriteLine("\t\t\t\tGLib.ExceptionManager.RaiseUnhandledException (e, false);"); sw.WriteLine("\t\t\t\tGLib.ExceptionManager.RaiseUnhandledException (e, false);");
sw.WriteLine("\t\t\t}"); if (dispose_params.Count > 0) {
sw.WriteLine ("\t\t\t} finally {");
foreach (Parameter p in dispose_params) {
string disp_name = "disposable_" + p.Name;
sw.WriteLine ("\t\t\t\tvar " + disp_name + " = " + p.Name + " as IDisposable;");
sw.WriteLine ("\t\t\t\tif (" + disp_name + " != null)");
sw.WriteLine ("\t\t\t\t\t" + disp_name + ".Dispose ();");
}
}
sw.WriteLine ("\t\t\t}");
GenArgsCleanup (sw, finish); GenArgsCleanup (sw, finish);
sw.WriteLine("\t\t}"); sw.WriteLine("\t\t}");
sw.WriteLine(); sw.WriteLine();