On Mon, 2008-01-28 at 17:13 -0500, Jonathan Pryor wrote:
> On Mon, 2008-01-28 at 21:10 +0100, Paolo Molaro wrote:
> > Deregistration is handled incorrectly: if there are two handlers for the
> > same signal it gets disabled at the first uninstall.

This has been fixed.

> > > The only other major change is the addition of a
> > > UnixSignal.WaitAny(UnixSignal[]) method, which allows waiting on more
> > > than one handle (since WaitHandle.WaitAny() can't currently be used with
> > > WaitHandle subclasses like UnixSignal).
> > 
> > I suggest using params in the array argument.
> 
> I'm not sure this is a good idea, as WaitAny() is overloaded:
> 
>       static bool WaitAny(params UnixSignal[] signals);
>       static bool WaitAny(UnixSignal[] signals, TimeSpan timeout);
>       static bool WaitAny(UnixSignal[] signals, int timeout);
> 
> Consequently, moving from WaitAny(UnixSignal[]) to one of the other
> overloads would result in inconsistent usage (adding `new
> UnixSignal[]{...}', etc.).
> 
> Alternatively, we could alter the ordering so that things are always
> consistent, though this would result in a different order from the
> WaitHandle.WaitAny() methods:
> 
>       static bool WaitAny(params UnixSignal[] signals);
>       static bool WaitAny(TimeSpan timeout, params UnixSignal[] signals);
>       static bool WaitAny(int timeout, params UnixSignal[] signals);
> 
> Consequently, I'm conflicted: we either keep things as they are,
> allowing consistency with WaitHandle.WaitAny() and making it easy to
> change the overload used, or provide params everywhere, and differ from
> WaitHandle.

Still not sure about this; would appreciate some additional feedback
before making this change.

> > An extensive test program that shows this stuff works before being
> > committed would also help.

Attached.  I should probably migrate it to NUnit and add it to the
Mono.Posix tests code...

So the only remaining question, to me, is the type and ordering of the
UnixSignal.WaitAny() methods (i.e. the 'params' question).

Thanks,
 - Jon

Index: ChangeLog
===================================================================
--- ChangeLog	(revision 92275)
+++ ChangeLog	(working copy)
@@ -1,3 +1,9 @@
+2008-01-28  Jonathan Pryor  <[EMAIL PROTECTED]>
+
+	* signal.c: Provide support functions for Mono.Unix.UnixSignal, which 
+	  supports both polling and waiting on signal emission.
+	* map.h: Flush (adds UnixSignal-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)
@@ -3,18 +3,30 @@
  *
  * Authors:
  *   Jonathan Pryor ([EMAIL PROTECTED])
+ *   Jonathan Pryor ([EMAIL PROTECTED])
  *
  * Copyright (C) 2004-2005 Jonathan Pryor
+ * Copyright (C) 2008 Novell, Inc.
  */
 
 #include <signal.h>
 
+#ifndef PLATFORM_WIN32
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#endif
+
 #include "map.h"
 #include "mph.h"
 
 G_BEGIN_DECLS
 
 typedef void (*mph_sighandler_t)(int);
+typedef struct Mono_Unix_UnixSignal_SignalInfo signal_info;
 
 void*
 Mono_Posix_Stdlib_SIG_DFL (void)
@@ -42,6 +54,7 @@
 }
 
 #ifndef PLATFORM_WIN32
+
 int
 Mono_Posix_Syscall_psignal (int sig, const char* s)
 {
@@ -49,6 +62,220 @@
 	psignal (sig, s);
 	return errno == 0 ? 0 : -1;
 }
+
+#define NUM_SIGNALS 64
+static signal_info signals[NUM_SIGNALS];
+
+static void
+default_handler (int signum)
+{
+	int i;
+	for (i = 0; i < NUM_SIGNALS; ++i) {
+		signal_info* h = &signals [i];
+		if (h->signum != signum)
+			continue;
+		++h->count;
+		if (h->write_fd > 0) {
+			char c = signum;
+			write (h->write_fd, &c, 1);
+		}
+	}
+}
+
+static pthread_mutex_t signals_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+void*
+Mono_Unix_UnixSignal_install (int sig)
+{
+	int i, mr;
+	signal_info* h = NULL; 
+	int have_handler = 0;
+	void* handler;
+
+	mr = pthread_mutex_lock (&signals_mutex);
+	if (mr != 0) {
+		errno = mr;
+		return NULL;
+	}
+
+	for (i = 0; i < NUM_SIGNALS; ++i) {
+		if (h == NULL && signals [i].signum == 0) {
+			h = &signals [i];
+			h->handler = signal (sig, default_handler);
+			if (h->handler == SIG_ERR) {
+				h->handler = NULL;
+				h = NULL;
+				break;
+			}
+			else {
+				h->signum       = sig;
+				h->count        = 0;
+				h->have_handler = 1;
+			}
+		}
+		if (!have_handler && signals [i].signum == sig &&
+				signals [i].handler != default_handler) {
+			have_handler = 1;
+			handler = signals [i].handler;
+		}
+		if (h && have_handler)
+			break;
+	}
+
+	if (h && have_handler) {
+		h->have_handler = 1;
+		h->handler      = handler;
+	}
+
+	pthread_mutex_unlock (&signals_mutex);
+
+	return h;
+}
+
+static int
+count_handlers (int signum)
+{
+	int i;
+	int count = 0;
+	for (i = 0; i < NUM_SIGNALS; ++i) {
+		if (signals [i].signum == signum)
+			++count;
+	}
+	return count;
+}
+
+int
+Mono_Unix_UnixSignal_uninstall (void* info)
+{
+	signal_info* h;
+	int mr, r = -1;
+
+	mr = pthread_mutex_lock (&signals_mutex);
+	if (mr != 0) {
+		errno = mr;
+		return -1;
+	}
+
+	h = info;
+
+	if (h == NULL || h < signals || h > &signals [NUM_SIGNALS])
+		errno = EINVAL;
+	else {
+		/* last UnixSignal -- we can unregister */
+		if (h->have_handler && count_handlers (h->signum) == 1) {
+			mph_sighandler_t p = signal (h->signum, h->handler);
+			if (p != SIG_ERR)
+				r = 0;
+			h->handler      = NULL;
+			h->have_handler = 0;
+		}
+		h->signum = 0;
+	}
+
+	pthread_mutex_unlock (&signals_mutex);
+
+	return r;
+}
+
+static int
+setup_pipes (signal_info** signals, int count, fd_set *read_fds, int *max_fd)
+{
+	int i, r;
+	for (i = 0; i < count; ++i) {
+		signal_info* h;
+		int filedes[2];
+
+		if ((r = pipe (filedes)) != 0) {
+			break;
+		}
+		h = signals [i];
+		h->read_fd  = filedes [0];
+		h->write_fd = filedes [1];
+		if (h->read_fd > *max_fd)
+			*max_fd = h->read_fd;
+		FD_SET (h->read_fd, read_fds);
+	}
+	return r;
+}
+
+static void
+teardown_pipes (signal_info** signals, int count)
+{
+	int i;
+	for (i = 0; i < count; ++i) {
+		signal_info* h = signals [i];
+		if (h->read_fd != 0)
+			close (h->read_fd);
+		if (h->write_fd != 0)
+			close (h->write_fd);
+		h->read_fd  = 0;
+		h->write_fd = 0;
+	}
+}
+
+static int
+wait_for_any (signal_info** signals, int count, int max_fd, fd_set* read_fds, int timeout)
+{
+	int r;
+	do {
+		struct timeval tv;
+		struct timeval *ptv = NULL;
+		if (timeout != -1) {
+			tv.tv_sec  = timeout / 1000;
+			tv.tv_usec = (timeout % 1000)*1000;
+			ptv = &tv;
+		}
+
+		r = select (max_fd + 1, read_fds, NULL, NULL, ptv);
+	} while (r == -1 && errno == EINTR);
+
+	if (r > 0) {
+		int i;
+		for (i = 0; i < count; ++i) {
+			signal_info* h = signals [i];
+			if (FD_ISSET (h->read_fd, read_fds)) {
+				char c;
+				read (h->read_fd, &c, 1); /* ignore error */
+			}
+		}
+	}
+
+	return r;
+}
+
+/*
+ * returns: -1 on error:
+ *           0 on timeout
+ *         > 0 on success
+ */
+int
+Mono_Unix_UnixSignal_WaitAny (void** _signals, int count, int timeout /* milliseconds */)
+{
+	fd_set read_fds;
+	int mr, r;
+	int max_fd = 0;
+
+	signal_info** signals = (signal_info**) _signals;
+
+	mr = pthread_mutex_lock (&signals_mutex);
+	if (mr != 0) {
+		errno = mr;
+		return -1;
+	}
+
+	FD_ZERO (&read_fds);
+
+	r = setup_pipes (signals, count, &read_fds, &max_fd);
+	if (r == 0) {
+		r = wait_for_any (signals, count, max_fd, &read_fds, timeout);
+	}
+	teardown_pipes (signals, count);
+
+	pthread_mutex_unlock (&signals_mutex);
+
+	return r;
+}
+
 #endif /* ndef PLATFORM_WIN32 */
 
 
Index: map.h
===================================================================
--- map.h	(revision 92275)
+++ map.h	(working copy)
@@ -1382,6 +1382,7 @@
 struct Mono_Posix_Timeval;
 struct Mono_Posix_Timezone;
 struct Mono_Posix_Utimbuf;
+struct Mono_Unix_UnixSignal_SignalInfo;
 
 /*
  * Inferred Structure Declarations
@@ -1541,7 +1542,16 @@
 Mono_Posix_ToUtimbuf (struct utimbuf *from, struct Mono_Posix_Utimbuf* to);
 
 
+struct Mono_Unix_UnixSignal_SignalInfo {
+	int   signum;
+	int   count;
+	int   read_fd;
+	int   write_fd;
+	int   have_handler;
+	void* handler;
+};
 
+
 /*
  * Functions
  */
@@ -1706,6 +1716,9 @@
 int Mono_Posix_Syscall_WSTOPSIG (int status);
 int Mono_Posix_Syscall_WTERMSIG (int status);
 int Mono_Posix_ToStatvfs (void* source, struct Mono_Posix_Statvfs* destination);
+void* Mono_Unix_UnixSignal_install (int signum);
+int Mono_Unix_UnixSignal_uninstall (void* info);
+int Mono_Unix_UnixSignal_WaitAny (void** infos, int count, int timeout);
 int wexitstatus (int status);
 int wifexited (int status);
 int wifsignaled (int status);
Index: Mono.Unix.Native/Stdlib.cs
===================================================================
--- Mono.Unix.Native/Stdlib.cs	(revision 92060)
+++ Mono.Unix.Native/Stdlib.cs	(working copy)
@@ -267,6 +267,12 @@
 	}
 
 
+	public enum SignalAction {
+		Default,
+		Ignore,
+		Error
+	}
+
 	//
 	// Right now using this attribute gives an assert because it
 	// isn't implemented.
@@ -454,6 +460,8 @@
 		private static extern IntPtr sys_signal (int signum, IntPtr handler);
 
 		[CLSCompliant (false)]
+		[Obsolete ("This is not safe; " + 
+				"use Mono.Unix.UnixSignal for signal delivery or SetSignalAction()")]
 		public static SignalHandler signal (Signum signum, SignalHandler handler)
 		{
 			int _sig = NativeConvert.FromSignum (signum);
@@ -494,6 +502,28 @@
 #endif
 		}
 
+		public static int SetSignalAction (Signum signal, SignalAction action)
+		{
+			IntPtr handler = IntPtr.Zero;
+			switch (action) {
+				case SignalAction.Default:
+					handler = _SIG_DFL;
+					break;
+				case SignalAction.Ignore:
+					handler = _SIG_IGN;
+					break;
+				case SignalAction.Error:
+					handler = _SIG_ERR;
+					break;
+				default:
+					throw new ArgumentException ("Invalid action value.", "action");
+			}
+			IntPtr r = sys_signal (NativeConvert.FromSignum (signal), handler);
+			if (r == _SIG_ERR)
+				return -1;
+			return 0;
+		}
+
 		[DllImport (LIBC, CallingConvention=CallingConvention.Cdecl, EntryPoint="raise")]
 		private static extern int sys_raise (int sig);
 
Index: Mono.Unix.Native/ChangeLog
===================================================================
--- Mono.Unix.Native/ChangeLog	(revision 92274)
+++ Mono.Unix.Native/ChangeLog	(working copy)
@@ -1,3 +1,10 @@
+2008-01-28  Jonathan Pryor  <[EMAIL PROTECTED]>
+
+	* Stdlib.cs: Obsolete Stdlib.signal(), as it's not 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
+	  Add SetSignalAction() as a replacement.
+
 2008-01-05  Jonathan Pryor  <[EMAIL PROTECTED]>
 
 	* Syscall.cs: Add ST_NOEXEC, ST_REMOUNT, ST_BIND to MountFlags.  Patch from
Index: Mono.Posix.dll.sources
===================================================================
--- Mono.Posix.dll.sources	(revision 92060)
+++ Mono.Posix.dll.sources	(working copy)
@@ -25,6 +25,7 @@
 ./Mono.Unix/UnixPath.cs
 ./Mono.Unix/UnixPipes.cs
 ./Mono.Unix/UnixProcess.cs
+./Mono.Unix/UnixSignal.cs
 ./Mono.Unix/UnixStream.cs
 ./Mono.Unix/UnixSymbolicLinkInfo.cs
 ./Mono.Unix/UnixUserInfo.cs
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 92060)
+++ ChangeLog	(working copy)
@@ -1,3 +1,7 @@
+2008-01-10  Jonathan Pryor  <[EMAIL PROTECTED]>
+
+	* Mono.Posix.dll.sources: Add Mono.Unix/UnixSignal.cs.
+
 2006-10-24  Jonathan Pryor  <[EMAIL PROTECTED]>
 
 	* Makefile: Don't build make-map.exe.
Index: Mono.Unix/UnixSignal.cs
===================================================================
--- Mono.Unix/UnixSignal.cs	(revision 0)
+++ Mono.Unix/UnixSignal.cs	(revision 0)
@@ -0,0 +1,167 @@
+//
+// Mono.Unix/UnixSignal.cs
+//
+// Authors:
+//   Jonathan Pryor ([EMAIL PROTECTED])
+//
+// (C) 2008 Novell, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+using Mono.Unix.Native;
+
+namespace Mono.Unix {
+	public class UnixSignal : WaitHandle {
+		private Signum signum;
+		private IntPtr signal_info;
+
+		public UnixSignal (Signum signum)
+		{
+			this.signum = signum;
+			// ensure signum is a valid signal
+			int _signum = NativeConvert.FromSignum (signum);
+			this.signal_info = install (_signum);
+			if (this.signal_info == IntPtr.Zero) {
+				throw new ArgumentException ("Unable to handle signal", "signum");
+			}
+		}
+
+		public Signum Signum {
+			get {
+				AssertValid ();
+				return signum; 
+			}
+		}
+
+		[DllImport (Stdlib.MPH, CallingConvention=CallingConvention.Cdecl,
+				EntryPoint="Mono_Unix_UnixSignal_install")]
+		private static extern IntPtr install (int signum);
+
+		[DllImport (Stdlib.MPH, CallingConvention=CallingConvention.Cdecl,
+				EntryPoint="Mono_Unix_UnixSignal_uninstall")]
+		private static extern int uninstall (IntPtr info);
+
+		[DllImport (Stdlib.MPH, CallingConvention=CallingConvention.Cdecl,
+				EntryPoint="Mono_Unix_UnixSignal_WaitAny")]
+		private static extern int WaitAny (IntPtr[] infos, int count, int timeout);
+
+		private void AssertValid ()
+		{
+			if (signal_info == IntPtr.Zero)
+				throw new ObjectDisposedException (GetType().FullName);
+		}
+
+		private unsafe SignalInfo* Info {
+			get {
+				AssertValid ();
+				return (SignalInfo*) signal_info;
+			}
+		}
+
+		public bool IsSet {
+			get {
+				return Count > 0;
+			}
+		}
+
+		public unsafe bool Reset ()
+		{
+			int n = Info->count;
+			Info->count = 0;
+			return n != 0;
+		}
+
+		public unsafe int Count {
+			get {return Info->count;}
+			set {Info->count = value;}
+		}
+
+		[Map]
+		struct SignalInfo {
+			public int signum, count, read_fd, write_fd, have_handler;
+			public IntPtr handler;
+		}
+
+		#region WaitHandle overrides
+		protected unsafe override void Dispose (bool disposing)
+		{
+			base.Dispose (disposing);
+			if (signal_info == IntPtr.Zero)
+				return;
+			uninstall (signal_info);
+			signal_info = IntPtr.Zero;
+		}
+
+		public override bool WaitOne ()
+		{
+			return WaitOne (-1, false);
+		}
+
+		public override bool WaitOne (TimeSpan timeout, bool exitContext)
+		{
+			long ms = (long) timeout.TotalMilliseconds;
+			if (ms < -1 || ms > Int32.MaxValue)
+				throw new ArgumentOutOfRangeException ("timeout");
+			return WaitOne ((int) ms, exitContext);
+		}
+
+		public override bool WaitOne (int millisecondsTimeout, bool exitContext)
+		{
+			AssertValid ();
+			if (exitContext)
+				throw new InvalidOperationException ("exitContext is not supported");
+			return WaitAny (new UnixSignal[]{this}, millisecondsTimeout);
+		}
+		#endregion
+
+		public static bool WaitAny (UnixSignal[] signals)
+		{
+			return WaitAny (signals, -1);
+		}
+
+		public static bool WaitAny (UnixSignal[] signals, TimeSpan timeout)
+		{
+			long ms = (long) timeout.TotalMilliseconds;
+			if (ms < -1 || ms > Int32.MaxValue)
+				throw new ArgumentOutOfRangeException ("timeout");
+			return WaitAny (signals, (int) ms);
+		}
+
+		public static unsafe bool WaitAny (UnixSignal[] signals, int millisecondsTimeout)
+		{
+			IntPtr[] infos = new IntPtr [signals.Length];
+			for (int i = 0; i < signals.Length; ++i) {
+				infos [i] = signals [i].signal_info;
+				if (infos [i] == IntPtr.Zero)
+					throw new InvalidOperationException ("Disposed UnixSignal");
+			}
+			int r = WaitAny (infos, infos.Length, millisecondsTimeout);
+			if (r > 0)
+				return true;
+			return false;
+		}
+	}
+}
+
Index: Mono.Unix/ChangeLog
===================================================================
--- Mono.Unix/ChangeLog	(revision 92060)
+++ Mono.Unix/ChangeLog	(working copy)
@@ -1,3 +1,8 @@
+2008-01-28  Jonathan Pryor  <[EMAIL PROTECTED]>
+
+	* UnixSignal.cs: Added; Polling and blocking based Unix signal mechanism.
+	  http://lists.ximian.com/pipermail/mono-devel-list/2008-January/026501.html
+
 2007-12-17  Jonathan Pryor  <[EMAIL PROTECTED]>
 
 	* UnixEnvironment.cs: Update MachineName property accesor to use uname(2)
// UnixSignal test

using System;
using System.Threading;
using Mono.Unix;
using Mono.Unix.Native;

class Test {
	public static void Main ()
	{
		CheckRaise ();
		CheckSeparation ();
		CheckNoEmit ();
		CheckDispose1 ();
		CheckDispose2 ();
		CheckSignalActionInteraction ();
	}

	static void Assert<T> (T actual, T expected)
	{
		if (!object.Equals (actual, expected))
			throw new InvalidOperationException (
					string.Format ("Assertion Failed: {0} != {1}", actual, expected));
	}

	static void CheckRaise ()
	{
		Thread t1 = new Thread ( () => {
				using (UnixSignal a = new UnixSignal (Signum.SIGINT)) {
					DateTime start = DateTime.Now;
					bool r = a.WaitOne (5000, false);
					DateTime end = DateTime.Now;
					Assert (r, true);
					Assert (a.Count, 1);
					if ((end - start) > new TimeSpan (0, 0, 5))
						throw new InvalidOperationException ("Signal slept too long");
				}
		});
		Thread t2 = new Thread ( () => {
				Thread.Sleep (1000);
				Stdlib.raise (Signum.SIGINT);
		});
		t1.Start ();
		t2.Start ();
		t1.Join ();
		t2.Join ();
	}

	static void CheckSeparation ()
	{
		Thread t1 = new Thread ( () => {
				using (UnixSignal a = new UnixSignal (Signum.SIGINT))
				using (UnixSignal b = new UnixSignal (Signum.SIGTERM)) {
					DateTime start = DateTime.Now;
					bool r = UnixSignal.WaitAny (new UnixSignal[]{a, b}, 5000);
					DateTime end = DateTime.Now;
					Assert (r, true);
					Assert (a.Count, 0);
					Assert (b.Count, 1);
					if ((end - start) > new TimeSpan (0, 0, 5))
						throw new InvalidOperationException ("Signal slept too long");
				}
		});
		Thread t2 = new Thread ( () => {
				Thread.Sleep (1000);
				Stdlib.raise (Signum.SIGTERM);
		});
		t1.Start ();
		t2.Start ();
		t1.Join ();
		t2.Join ();
	}

	static void CheckNoEmit ()
	{
		using (UnixSignal u = new UnixSignal (Signum.SIGINT)) {
			DateTime start = DateTime.Now;
			bool r = u.WaitOne (5100, false);
			Assert (r, false);
			DateTime end = DateTime.Now;
			if ((end - start) < new TimeSpan (0, 0, 5))
				throw new InvalidOperationException ("Signal didn't block for 5s; blocked for " + (end-start).ToString());
		}
	}

	static void CheckDispose1 ()
	{
		UnixSignal a = new UnixSignal (Signum.SIGINT);
		UnixSignal b = new UnixSignal (Signum.SIGINT);

		Stdlib.raise (Signum.SIGINT);

		Assert (a.Count, 1);
		Assert (b.Count, 1);

		a.Close ();
		b.Reset ();

		Stdlib.raise (Signum.SIGINT);
		Assert (b.Count, 1);

		b.Close ();
	}

	static void CheckDispose2 ()
	{
		UnixSignal a = new UnixSignal (Signum.SIGINT);
		UnixSignal b = new UnixSignal (Signum.SIGINT);

		Stdlib.raise (Signum.SIGINT);

		Assert (a.Count, 1);
		Assert (b.Count, 1);

		b.Close ();
		a.Reset ();

		Stdlib.raise (Signum.SIGINT);
		Assert (a.Count, 1);

		a.Close ();
	}

	static void CheckSignalActionInteraction ()
	{
		using (UnixSignal a = new UnixSignal (Signum.SIGINT)) {
			Stdlib.SetSignalAction (Signum.SIGINT, SignalAction.Ignore);
			Stdlib.raise (Signum.SIGINT);
			Assert (a.Count, 0); // never invoked
		}
	}
}
_______________________________________________
Mono-devel-list mailing list
Mono-devel-list@lists.ximian.com
http://lists.ximian.com/mailman/listinfo/mono-devel-list

Reply via email to