// // ThreadNotify.cs: implements a notification for the thread running the Gtk main // loop from another thread // // Authors: // Miguel de Icaza (miguel@ximian.com). // Gonzalo Paniagua Javier (gonzalo@ximian.com) // // (C) 2002 Ximian, Inc. // (C) 2004 Novell, Inc. // namespace Gtk { using System.Runtime.InteropServices; using System.Threading; using System; // // This delegate will be invoked on the main Gtk thread. // public delegate void ReadyEvent (); /// /// Utility class to help writting multi-threaded Gtk applications /// /// /// public class ThreadNotify : IDisposable { // // DllImport functions from Gtk // [DllImport ("libgtk-win32-2.0-0.dll")] static extern uint gdk_input_add (int s, int cond, GdkInputFunction f, IntPtr data); public delegate void GdkInputFunction (IntPtr data, int source, int cond); [DllImport ("gtksharpglue")] static extern int pipe_create (int [] fd); [DllImport ("gtksharpglue")] static extern unsafe int pipe_read (int fd, byte *b, int count); [DllImport ("gtksharpglue")] static extern unsafe int pipe_write (int fd, byte *b, int count); [DllImport ("gtksharpglue")] static extern int pipe_close (int [] fd); GdkInputFunction notify_pipe; int [] pipes; bool disposed; uint tag; ReadyEvent re; /// /// The ReadyEvent delegate will be invoked on the current thread (which should /// be the Gtk thread) when another thread wakes us up by calling WakeupMain /// public ThreadNotify (ReadyEvent re) { notify_pipe = new GdkInputFunction (NotifyPipe); pipes = new int [2]; pipe_create (pipes); tag = gdk_input_add (pipes [0], 1, notify_pipe, (IntPtr) 0); this.re = re; } void NotifyPipe (IntPtr data, int source, int cond) { byte s; unsafe { lock (this) { pipe_read (pipes [0], &s, 1); notified = false; } } re (); } bool notified = false; /// /// Invoke this function from a thread to call the `ReadyEvent' /// delegate provided in the constructor on the Main Gtk thread /// public void WakeupMain () { unsafe { byte s; lock (this){ if (notified) return; pipe_write (pipes [1], &s, 1); notified = true; } } } public void Close () { Dispose (true); GC.SuppressFinalize (this); } ~ThreadNotify () { Dispose (false); } void IDisposable.Dispose () { Close (); } protected virtual void Dispose (bool disposing) { if (!disposed) { disposed = true; GLib.Source.Remove (tag); pipe_close (pipes); } pipes = null; notify_pipe = null; } } }