[mono/gtk-sharp] e48ac63d: generator: Dispose ownable method parameters in VM callback (bxc#237)

"Bertrand Lorentz ([email protected])" <[email protected]> Sat, 9 Nov 2013 15:30:43 +0000
Newsgroups gmane.comp.gnome.mono.patches
Message-ID <000001423d7c9148-1c761754-8daa-4933-a133-78be4c90222a-000000@email.amazonses.com>
   Branch: refs/heads/master
     Home: https://github.com/mono/gtk-sharp
  Compare: https://github.com/mono/gtk-sharp/compare/29674827628e...e48ac63d54c4

   Commit: e48ac63d54c4c245f25b73c548f90b649acea2c8
   Author: Bertrand Lorentz <[email protected]> (bl8)
     Date: 2013-11-09 15:26:17 GMT
      URL: https://github.com/mono/gtk-sharp/commit/e48ac63d54c4c245f25b73c548f90b649acea2c8

generator: Dispose ownable method parameters in VM callback (bxc#237)

Some virtual methods are passed a native object that is wrapped in an
IDisposable managed object, which is then passed on to the managed
overrides. We need to dispose those objects as soon as possible,
otherwise their native counterpart will not be freed until the next
garbage collection. Requiring the overrides to dispose them would be
cumbersome and error-prone.

Those parameters will now be disposed in a finally {...} block, after
the virtual method has returned. This means that overrides should not
keep a reference to such a parameter outside of the scope of the method,
as it will be diposed when the method returns.

This change only affects Cairo.Context parameter for now, but it was
particularly needed for them, as they could happily hold on to tens of
MBs of memory until the next garbage collection.

Changed paths:
  M generator/ManagedCallString.cs
  M generator/Parameter.cs
  M generator/VirtualMethod.cs

Modified: generator/ManagedCallString.cs
===================================================================
@@ -28,6 +28,7 @@ namespace GtkSharp.Generation {
 	public class ManagedCallString {
 		
 		IDictionary<Parameter, bool> parms = new Dictionary<Parameter, bool> ();
+		IList<Parameter> dispose_params = new List<Parameter> ();
 		string error_param = null;
 		string user_data_param = null;
 		string destroy_param = null;
@@ -57,6 +58,10 @@ public ManagedCallString (Parameters parms)
 					special = true;
 
 				this.parms.Add (p, special);
+
+				if (p.IsOwnable) {
+					dispose_params.Add (p);
+				}
 			}
 		}
 
@@ -70,10 +75,18 @@ public ManagedCallString (Parameters parms)
 			}
 		}
 
+		public bool HasDisposeParam {
+			get { return dispose_params.Count > 0; }
+		}
+
 		public string Unconditional (string indent) {
 			string ret = "";
 			if (error_param != null)
 				ret = indent + error_param + " = IntPtr.Zero;\n";
+
+			foreach (Parameter p in dispose_params) {
+				ret += indent + p.CSType + " my" + p.Name + " = null;\n";
+			}
 			return ret;
 		}
 
@@ -103,6 +116,10 @@ public string Setup (string indent)
 				}
 			}
 
+			foreach (Parameter p in dispose_params) {
+				ret += indent + "my" + p.Name + " = " + p.FromNative (p.Name) + ";\n";
+			}
+
 			return ret;
 		}
 
@@ -119,7 +136,12 @@ public override string ToString ()
 				if (p.Generatable is CallbackGen) {
 					result [i] += p.Name + "_invoker.Handler";
 				} else {
-					result [i] += (parms [p]) ? "my" + p.Name : p.FromNative (p.Name);
+					if (parms [p] || dispose_params.Contains(p)) {
+						// Parameter was declared and marshalled earlier
+						result [i] +=  "my" + p.Name;
+					} else {
+						result [i] +=  p.FromNative (p.Name);
+					}
 				}
 				i++;
 			}
@@ -150,6 +172,22 @@ public string Finish (string indent)
 
 			return ret;
 		}
+
+		public string DisposeParams (string indent)
+		{
+			string ret = "";
+
+			foreach (Parameter p in dispose_params) {
+				string name = "my" + p.Name;
+				string disp_name = "disposable_" + p.Name;
+
+				ret += indent + "var " + disp_name + " = " + name + " as IDisposable;\n";
+				ret += indent + "if (" + disp_name + " != null)\n";
+				ret += indent + "\t" + disp_name + ".Dispose ();\n";
+			}
+
+			return ret;
+		}
 	}
 }
 

Modified: generator/Parameter.cs
===================================================================
@@ -186,6 +186,12 @@ public Parameter (XmlElement e)
 			}
 		}
 
+		public bool IsOwnable {
+			get {
+				return this.Generatable is OwnableGen;
+			}
+		}
+
 		public bool Owned {
 			get {
 				return elem.GetAttribute ("owned") == "true";

Modified: generator/VirtualMethod.cs
===================================================================
@@ -98,14 +98,17 @@ public void GenerateCallback (StreamWriter sw, ClassBase implementor)
 				sw.WriteLine ("\t\t\t\t{0} __obj = GLib.Object.GetObject (inst, false) as {0};", type);
 			}
 
-			sw.Write (call.Setup ("\t\t\t\t"));
-			sw.Write ("\t\t\t\t");
+			string indent = "\t\t\t\t";
 			if (!retval.IsVoid)
-				sw.Write (retval.CSType + " __result = ");
+				sw.WriteLine (indent + retval.CSType + " __result;");
+			sw.Write (call.Setup (indent));
+			sw.Write (indent);
+			if (!retval.IsVoid)
+				sw.Write ("__result = ");
 			if (!this.IsStatic)
 				sw.Write ("__obj.");
 			sw.WriteLine (this.CallString + ";");
-			sw.Write (call.Finish ("\t\t\t\t"));
+			sw.Write (call.Finish (indent));
 			if (!retval.IsVoid)
 				sw.WriteLine ("\t\t\t\treturn " + retval.ToNative ("__result") + ";");
 
@@ -116,6 +119,11 @@ public void GenerateCallback (StreamWriter sw, ClassBase implementor)
 				sw.WriteLine ("\t\t\t\t// NOTREACHED: above call does not return.");
 				sw.WriteLine ("\t\t\t\tthrow e;");
 			}
+
+			if (call.HasDisposeParam) {
+				sw.WriteLine ("\t\t\t} finally {");
+				sw.Write (call.DisposeParams (indent));
+			}
 			sw.WriteLine ("\t\t\t}");
 			sw.WriteLine ("\t\t}");
 			sw.WriteLine ();


_______________________________________________
Mono-patches maillist  -  [email protected]
http://lists.ximian.com/mailman/listinfo/mono-patches