[Gtk-sharp-list] More on signals

Gonzalo Paniagua Javier gonzalo@ximian.com
26 Feb 2003 00:39:53 +0100


--=-V8MjVegXKqhHSJOY0Tif
Content-Type: text/plain
Content-Transfer-Encoding: 7bit

Hi there!

Radek provided a sample test case that registers and unregisters a
handle for a signal, and the handler is being invoked after it has been
unregistered (attachment Test.cs).

Why does it invoke the handler?

The class that handles this signal,
GtkSharp.voidObjectTextIterstringintSignal has the _handler in its base
class (SignalCallback) still being a valid delegate (a) *and* we are
still connected to that signal (b).

(a) Is fixed by doing:
....
((GtkSharp.SignalCallback) Signals ["insert_text"]).RemoveDelegate
(value);
.....
Just before removing "insert_text" from Signals in
TextBuffer.InsertText.

Once this is done, the static method that handles the signal dies in
this line:
inst._handler.DynamicInvoke(argv);

because _handler is null, which takes us to...

(b) This can be fixed by implementing the IDisposable pattern in
SignalCallback and its subclasses. The Dispose method in the subclasses
should, if _Instances.Count reaches 0, disconnect the signal. The
Dispose method is called when removing a handler from the event or when
finalizing the instance.

The patch that does this is attached. It works fine.

Now I wonder...:
        -Do we need EventList? We can make SignalCallback.RemoveDelegate
        return the resulting delegate or a boolean to tell us if there
        are any other handlers left and disconnect the signal if not.
        
        -Should we finalize, ie. call Dispose, on all the objects in
        Signals hashtable (if any) when finalizing GLib.Objects?

Ok to commit?

-Gonzalo


--=-V8MjVegXKqhHSJOY0Tif
Content-Disposition: attachment; filename=Test.cs
Content-Type: text/plain; name=Test.cs; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit

using System;
using Gtk;

class Test : Application
{
	static GtkSharp.InsertTextHandler myEvent;

	public static void Main() 
	{
		Init();

		Gtk.TextBuffer buffer = new TextBuffer(new Gtk.TextTagTable());

		buffer.InsertAtCursor("hello");

		Console.WriteLine("buffer: {0}", buffer.Text);

		//buffer.InsertText += new GtkSharp.InsertTextHandler(my_handler);
		myEvent = new GtkSharp.InsertTextHandler (my_handler);
		buffer.InsertText += myEvent;
		buffer.InsertText -= myEvent;

		buffer.InsertAtCursor(" world");
		
		Console.WriteLine("buffer: {0}", buffer.Text);
	}


	static bool inside = false;

	static void my_handler (object obj, GtkSharp.InsertTextArgs args)
	{
		Console.WriteLine("my_handler called inside: {0}", inside);
		if (inside)
			return; 
	
		inside = true;
	
		Gtk.TextBuffer buffer = (Gtk.TextBuffer) obj;
		
		Console.WriteLine("insert begin");
		buffer.InsertText -= myEvent;
		//buffer.InsertAtCursor("<extra>");
		//buffer.InsertText += myEvent;
		Console.WriteLine("insert end");

		inside = false;
	}
}


--=-V8MjVegXKqhHSJOY0Tif
Content-Disposition: attachment; filename=signals-2.patch
Content-Type: text/x-patch; name=signals-2.patch; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit

Index: generator/Parser.cs
===================================================================
RCS file: /cvs/public/gtk-sharp/generator/Parser.cs,v
retrieving revision 1.14
diff -u -r1.14 Parser.cs
--- generator/Parser.cs	26 Oct 2002 08:03:15 -0000	1.14
+++ generator/Parser.cs	25 Feb 2003 23:29:57 -0000
@@ -8,6 +8,7 @@
 
 	using System;
 	using System.Collections;
+	using System.IO;
 	using System.Xml;
 
 	public class Parser  {
@@ -20,7 +21,9 @@
 
 			try {
 
-				doc.Load (filename);
+				Stream stream = File.OpenRead (filename);
+				doc.Load (stream);
+				stream.Close ();
 
 			} catch (XmlException e) {
 
Index: generator/Signal.cs
===================================================================
RCS file: /cvs/public/gtk-sharp/generator/Signal.cs,v
retrieving revision 1.12
diff -u -r1.12 Signal.cs
--- generator/Signal.cs	6 Feb 2003 00:58:02 -0000	1.12
+++ generator/Signal.cs	25 Feb 2003 23:29:57 -0000
@@ -169,10 +169,16 @@
 			sw.WriteLine("\t\t\t}");
 			sw.WriteLine("\t\t\tremove {");
 			sw.WriteLine("\t\t\t\tEventList.RemoveHandler(" + cname + ", value);");
-			sw.WriteLine("\t\t\t\tif (EventList[" + cname + "] == null)");
+			sw.WriteLine("\t\t\t\tGtkSharp.SignalCallback cb = Signals [{0}] as GtkSharp.SignalCallback;", cname);
+			sw.WriteLine("\t\t\t\tif (cb == null)");
+			sw.WriteLine("\t\t\t\t\treturn;");
+			sw.WriteLine();
+			sw.WriteLine("\t\t\t\tcb.RemoveDelegate (value);");
+			sw.WriteLine();
+			sw.WriteLine("\t\t\t\tif (EventList[" + cname + "] == null) {");
 			sw.WriteLine("\t\t\t\t\tSignals.Remove(" + cname + ");");
-			sw.WriteLine("\t\t\t\telse");
-			sw.WriteLine("\t\t\t\t\t((GtkSharp.SignalCallback) Signals [{0}]).RemoveDelegate (value);", cname);
+			sw.WriteLine("\t\t\t\t\tcb.Dispose ();");
+			sw.WriteLine("\t\t\t\t}");
 			sw.WriteLine("\t\t\t}");
 			sw.WriteLine("\t\t}");
 			sw.WriteLine();
Index: generator/SignalHandler.cs
===================================================================
RCS file: /cvs/public/gtk-sharp/generator/SignalHandler.cs,v
retrieving revision 1.20
diff -u -r1.20 SignalHandler.cs
--- generator/SignalHandler.cs	19 Feb 2003 06:23:36 -0000	1.20
+++ generator/SignalHandler.cs	25 Feb 2003 23:29:57 -0000
@@ -109,6 +109,9 @@
 			sw.WriteLine();
 			sw.WriteLine("\t\tprivate static " + dname + " _Delegate;");
 			sw.WriteLine();
+			sw.WriteLine("\t\tprivate IntPtr _raw;");
+			sw.WriteLine("\t\tprivate uint _HandlerID;");
+			sw.WriteLine();
 			sw.Write("\t\tprivate static " + p_ret + " ");
 			sw.WriteLine(cbname + "(" + pinv + ", int key)");
 			sw.WriteLine("\t\t{");
@@ -162,8 +165,8 @@
 				sw.WriteLine("\t\t}");
 				sw.WriteLine();
 			}
-			sw.Write("\t\t[DllImport(\"gobject-2.0\")]");
-			sw.Write("\t\tstatic extern void g_signal_connect_data(");
+			sw.WriteLine("\t\t[DllImport(\"gobject-2.0\")]");
+			sw.Write("\t\tstatic extern uint g_signal_connect_data(");
 			sw.Write("IntPtr obj, String name, " + dname + " cb, int key, IntPtr p,");
 			sw.WriteLine(" int flags);");
 			sw.WriteLine();
@@ -173,14 +176,19 @@
 			sw.WriteLine("\t\t\tif (_Delegate == null) {");
 			sw.WriteLine("\t\t\t\t_Delegate = new " + dname + "(" + cbname + ");");
 			sw.WriteLine("\t\t\t}");
-			sw.Write("\t\t\tg_signal_connect_data(raw, name, ");
+			sw.WriteLine("\t\t\t_raw = raw;");
+			sw.Write("\t\t\t_HandlerID = g_signal_connect_data(raw, name, ");
 			sw.WriteLine("_Delegate, _key, new IntPtr(0), 0);");
 			sw.WriteLine("\t\t}");
 			sw.WriteLine();
-			sw.WriteLine("\t\t~" + sname + "()");
+			sw.WriteLine("\t\t[DllImport(\"gobject-2.0\")]");
+			sw.WriteLine("\t\tstatic extern void g_signal_handler_disconnect (IntPtr instance, uint handler);");
+			sw.WriteLine();
+			sw.WriteLine("\t\tprotected override void Dispose (bool disposing)");
 			sw.WriteLine("\t\t{");
 			sw.WriteLine("\t\t\t_Instances.Remove(_key);");
 			sw.WriteLine("\t\t\tif(_Instances.Count == 0) {");
+			sw.WriteLine("\t\t\t\tg_signal_handler_disconnect (_raw, _HandlerID);");
 			sw.WriteLine("\t\t\t\t_Delegate = null;");
 			sw.WriteLine("\t\t\t}");
 			sw.WriteLine("\t\t}");
Index: glib/SignalCallback.cs
===================================================================
RCS file: /cvs/public/gtk-sharp/glib/SignalCallback.cs,v
retrieving revision 1.5
diff -u -r1.5 SignalCallback.cs
--- glib/SignalCallback.cs	24 Feb 2003 06:39:30 -0000	1.5
+++ glib/SignalCallback.cs	25 Feb 2003 23:29:58 -0000
@@ -18,7 +18,7 @@
 	///	Base Class for GSignal to C# event marshalling.
 	/// </remarks>
 
-	public abstract class SignalCallback {
+	public abstract class SignalCallback : IDisposable {
 
 		// A counter used to produce unique keys for instances.
 		protected static int _NextKey = 0;
@@ -58,5 +58,26 @@
 		{
 			_handler = Delegate.Remove (_handler, d);
 		}
+
+		public void Dispose ()
+		{
+			Dispose (true);
+			GC.SuppressFinalize (this);
+		}
+
+		protected virtual void Dispose (bool disposing)
+		{
+			if (disposing) {
+				_obj = null;
+				_handler = null;
+				_argstype = null;
+			}
+		}
+
+		~SignalCallback ()
+		{
+			Dispose (false);
+		}
 	}
 }
+

--=-V8MjVegXKqhHSJOY0Tif--