[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--