On Tue, 2008-01-08 at 22:02 -0500, Jonathan Pryor wrote:
> Thank you for the background on why signal handlers can't be made to
> work with the current Stdlib.signal implementation.
> 
> However...
> I don't see why we need a new API to support this.  It seems that we
> could retrofit the existing Stdlib.signal() API to use the
> implementation you described, with one difference: a runtime internal
> thread could be used to block on the internal pipe, and when the pipe is
> readable it could dispatch the appropriate signal via delegate
> registered with Stdlib.signal().  No polling, and no new public API
> would be required.
> 
> The one thing I'm not sure about is which thread should wait on the
> pipe.  Would a dedicated Mono.Posix-internal thread be more appropriate,
> or would one of the existing threads be beter (e.g. a ThreadPool thread,
> or a Timer-related thread, or...).

Attached are patches to mcs/class/Mono.Posix/Mono.Unix.Native and
mono/support as an initial implementation of this idea.  It currently
uses a dedicated Mono.Posix-internal thread to do managed signal
dispatching (as the ThreadPool is intended for short-lived tasks, and
I'm not familiar enough with the Timer-related infrastructure to see if
it would be a good fit).

Thoughts?

 - Jon

Index: Stdlib.cs
===================================================================
--- Stdlib.cs	(revision 92060)
+++ Stdlib.cs	(working copy)
@@ -31,6 +31,9 @@
 using System.IO;
 using System.Runtime.InteropServices;
 using System.Text;
+using System.Threading;
+
+using Mono.Unix;
 using Mono.Unix.Native;
 
 namespace Mono.Unix.Native {
@@ -359,7 +362,7 @@
 	//        This is the case for using NativeConvert, which will throw an
 	//        exception if an invalid/unsupported value is used.
 	//
-	public class Stdlib
+	public unsafe class Stdlib
 	{
 		internal const string LIBC = "msvcrt";
 		internal const string MPH  = "MonoPosixHelper";
@@ -445,24 +448,21 @@
 			registered_signals = new SignalHandler [(int) signals.GetValue (signals.Length-1)];
 		}
 
-		[DllImport (LIBC, CallingConvention=CallingConvention.Cdecl,
-				SetLastError=true, EntryPoint="signal")]
+		[DllImport (MPH, CallingConvention=CallingConvention.Cdecl,
+				SetLastError=true, EntryPoint="Mono_Posix_Stdlib_signal")]
 		private static extern IntPtr sys_signal (int signum, SignalHandler handler);
 
-		[DllImport (LIBC, CallingConvention=CallingConvention.Cdecl,
-				SetLastError=true, EntryPoint="signal")]
+		[DllImport (MPH, CallingConvention=CallingConvention.Cdecl,
+				SetLastError=true, EntryPoint="Mono_Posix_Stdlib_signal")]
 		private static extern IntPtr sys_signal (int signum, IntPtr handler);
 
 		[CLSCompliant (false)]
 		public static SignalHandler signal (Signum signum, SignalHandler handler)
 		{
+			InitSignalSupport ();
+
 			int _sig = NativeConvert.FromSignum (signum);
 
-			Delegate[] handlers = handler.GetInvocationList ();
-			for (int i = 0; i < handlers.Length; ++i) {
-				Marshal.Prelink (handlers [i].Method);
-			}
-
 			lock (registered_signals) {
 				registered_signals [(int) signum] = handler;
 			}
@@ -494,6 +494,45 @@
 #endif
 		}
 
+		[DllImport (MPH, CallingConvention=CallingConvention.Cdecl,
+				SetLastError=true, EntryPoint="_mph_set_signal_write_fd")]
+		private static extern void set_signal_write_fd (int signum);
+
+		static object signal_dispatcher;
+		static int    signal_read_fd;
+
+		private static void InitSignalSupport ()
+		{
+			if (Path.DirectorySeparatorChar == '\\')
+				return;
+
+			if (signal_dispatcher == null) {
+				object c = new Thread (SignalDispatcher);
+				Thread.MemoryBarrier ();
+				while (Interlocked.CompareExchange (ref signal_dispatcher, c, null) == null) {
+					int writing;
+					if (Syscall.pipe (out signal_read_fd, out writing) < 0)
+						throw UnixMarshal.CreateExceptionForLastError ();
+					set_signal_write_fd (writing);
+					Thread _c = (Thread) c;
+					_c.IsBackground = true;
+					_c.Name = "Mono.Unix.Native Signal Dispatcher";
+					_c.Start ();
+				}
+				Thread.MemoryBarrier ();
+			}
+		}
+
+		private static unsafe void SignalDispatcher ()
+		{
+			byte c;
+			while (Syscall.read (signal_read_fd, &c, 1) >= 1) {
+				SignalHandler h = registered_signals [c];
+				if (h != null)
+					h (c);
+			}
+		}
+
 		[DllImport (LIBC, CallingConvention=CallingConvention.Cdecl, EntryPoint="raise")]
 		private static extern int sys_raise (int sig);
 
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 92274)
+++ ChangeLog	(working copy)
@@ -1,3 +1,9 @@
+2008-01-09  Jonathan Pryor  <[EMAIL PROTECTED]>
+
+	* Stdlib.cs: Update Stdlib.signal() so that it's safe; see also: 
+	  http://lists.ximian.com/pipermail/mono-devel-list/2008-January/026501.html
+	  http://lists.ximian.com/pipermail/mono-devel-list/2008-January/026503.html
+
 2008-01-05  Jonathan Pryor  <[EMAIL PROTECTED]>
 
 	* Syscall.cs: Add ST_NOEXEC, ST_REMOUNT, ST_BIND to MountFlags.  Patch from
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 92275)
+++ ChangeLog	(working copy)
@@ -1,3 +1,9 @@
+2008-01-09  Jonathan Pryor  <[EMAIL PROTECTED]>
+
+	* signal.c: Provide a safe version of signal(3), as it isn't safe for
+	  marshaled delegates to be invoked from signal handler context.
+	* map.h: Flush (adds signal(3)-related prototypes).
+
 2008-01-05  Jonathan Pryor  <[EMAIL PROTECTED]>
 
 	* map.h, map.c: Flush; add new ST_NOEXEC, ST_REMOUNT, and ST_BIND MountFlags 
Index: signal.c
===================================================================
--- signal.c	(revision 92060)
+++ signal.c	(working copy)
@@ -9,6 +9,10 @@
 
 #include <signal.h>
 
+#ifndef PLATFORM_WIN32
+#include <unistd.h>
+#endif
+
 #include "map.h"
 #include "mph.h"
 
@@ -49,6 +53,40 @@
 	psignal (sig, s);
 	return errno == 0 ? 0 : -1;
 }
+
+static int signal_write;
+
+void
+_mph_set_signal_write_fd (int fd)
+{
+	signal_write = fd;
+}
+
+static void
+default_signal_handler (int signum)
+{
+	// FIXME: currently we assume that all signums are < 255
+	char c = signum;
+	write (signal_write, &c, 1);
+}
+
+void*
+Mono_Posix_Stdlib_signal (int signum, void* handler)
+{
+	if (handler == SIG_DFL || handler == SIG_ERR || handler == SIG_IGN) {
+		return signal (signum, handler);
+	}
+	return signal (signum, default_signal_handler);
+}
+
+#else /* ndef PLATFORM_WIN32 */
+
+void*
+Mono_Posix_Stdlib_signal (int signum, void* handler)
+{
+	return signal (signum, handler);
+}
+
 #endif /* ndef PLATFORM_WIN32 */
 
 
Index: map.h
===================================================================
--- map.h	(revision 92275)
+++ map.h	(working copy)
@@ -1397,6 +1397,7 @@
  * Delegate Declarations
  */
 
+typedef void (*SignalHandler) (int signal);
 
 /*
  * Structures
@@ -1545,6 +1546,7 @@
 /*
  * Functions
  */
+void _mph_set_signal_write_fd (int signum);
 char* helper_Mono_Posix_GetGroupName (int gid);
 char* helper_Mono_Posix_GetUserName (int uid);
 char* helper_Mono_Posix_readdir (void* dir);
@@ -1587,6 +1589,7 @@
 void* Mono_Posix_Stdlib_SIG_DFL (void);
 void* Mono_Posix_Stdlib_SIG_ERR (void);
 void* Mono_Posix_Stdlib_SIG_IGN (void);
+void* Mono_Posix_Stdlib_signal (int signum, void* handler);
 void* Mono_Posix_Stdlib_stderr (void);
 void* Mono_Posix_Stdlib_stdin (void);
 void* Mono_Posix_Stdlib_stdout (void);
_______________________________________________
Mono-devel-list mailing list
[email protected]
http://lists.ximian.com/mailman/listinfo/mono-devel-list

Reply via email to