[Mono-dev] Control-C handler
Paolo Molaro
lupus at ximian.com
Fri Jan 11 13:56:14 EST 2008
On 01/10/08 Jonathan Pryor wrote:
> Attached is an updated patch set which supports both the existing/new
> Stdlib.signal() semantics (as the previous patch set did) in which the
> signal handler is invoked on an internal helper thread, and adds a new
> Mono.Unix.UnixSignal type to permit polling and/or blocking as lupus
> originally suggested.
I think signal() should just be obsoleted, starting a thread yourself
doesn't provide the same semantics and it is doesn't allow the user much
control.
> The one thing I don't like (but can't think of a workaround) is the
> interaction between UnixSignal and Stdlib.signal(): as currently
> implemented, UnixSignal and Stdlib.signal() can't handle the same signal
> simultaneously -- if UnixSignal registered to handle e.g. SIGINT, then
> Stdlib.signal() will return SIG_ERR, with errno set to EEXIST.
>
> However, the alternate IS possible: UnixSignal can handle e.g. SIGINT if
> Stdlib.signal() was previously called to handle it (and will
> consequently replace the original Stdlib.signal() handler). The
> "original" Stdlib.signal() handler will be replaced when the UnixSignal
> instance is destroyed.
I think there are two choices for Stdlib.signal(): either leave it alone
or make it do nothing/throw an exception (this can happen after some
time it has been obsoleted).
> Of course, this could be used as an argument to dump Stdlib.signal()
> support entirely, but it needs to stick around to permit setting SIG_IGN
> on a given signal. I suppose Stdlib.signal() could be restricted to
> just accepting SIG_IGN, SIG_ERR, and SIG_DFL and error out if any other
Just provide a sane interface to ignore a signal instead of this kludge.
> delegate instance is passed, so that people use UnixSignal for all
> significant signal usage, but I don't like this either as currently each
> UnixSignal.WaitOne() call requires a separate pipe, while
This is an implementation detail of your version, it could be done
differently.
> Index: Mono.Unix.Native/Stdlib.cs
> ===================================================================
> --- Mono.Unix.Native/Stdlib.cs (revision 92060)
> +++ Mono.Unix.Native/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
It's a bad idea to make the whole class unsafe.
> @@ -494,6 +495,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;
This looks ugly.
> + 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);
> + }
> + }
There is no protection here about a callback throwing an exception, so
the thread dies and everything falls apart. It's a bad idea anyway to
try to support the broken interface and provide different semantics at
the same time.
> --- Mono.Unix/UnixSignal.cs (revision 0)
> +++ Mono.Unix/UnixSignal.cs (revision 0)
[...]
> +namespace Mono.Unix {
> + public class UnixSignal : WaitHandle {
> + private Signum signum;
> + private int _signum;
> + private IntPtr signal_info;
> +
> + public UnixSignal (Signum signum)
> + {
> + this.signum = signum;
> + this._signum = NativeConvert.FromSignum (signum);
> + lock (Stdlib.registered_signals) {
> + this.signal_info = install (_signum);
> + }
> + if (this.signal_info == IntPtr.Zero) {
> + throw new ArgumentException ("Unable to handle signal", "signum");
> + }
> + }
> +
> + public Signum Signum {
> + get { 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 void uninstall (int signum);
My proposed interface allows registering multiple handlers per signals
and this code can deal with just one.
> + public unsafe override bool WaitOne (int millisecondsTimeout, bool exitContext)
> + {
> + AssertValid ();
> + if (exitContext)
> + throw new InvalidOperationException ("exitContext is not supported");
> + if (millisecondsTimeout < -1)
> + throw new ArgumentOutOfRangeException ("millisecondsTimeout");
> + int read, write;
> + bool close = true;
> + read = write = -1;
> + try {
> + if (Syscall.pipe (out read, out write) < 0) {
> + close = false;
> + throw UnixMarshal.CreateExceptionForLastError ();
> + }
> + Pollfd[] fds = new Pollfd[1];
> + fds[0].fd = read;
> + fds[0].events = PollEvents.POLLIN;
> + if (IsSet)
> + return true;
> + Info->write_fd = write;
> + int r = Syscall.poll (fds, 1, millisecondsTimeout);
Unless Syscall.poll() actually falls back to select() it shouldn't be
used as for example the POS OS X doesn't implement it.
> + if (r == -1 && Stdlib.GetLastError () != Errno.EINTR) {
> + return false;
> + }
> + else
> + r = Syscall.poll (fds, 1, 0);
> + if (r > 0) {
> + byte c = 0;
> + Syscall.read (read, &c, 1);
No error checking. This stuff should be implemented in C anyway.
> --- signal.c (revision 92060)
> +++ signal.c (working copy)
> +void*
> +Mono_Unix_UnixSignal_install (int sig)
> +{
> + signal_info* n = realloc (registered_signals,
> + sizeof(signal_info)*(registered_signals_count+1));
> + if (n) {
> + signal_info* r;
> + registered_signals = n;
> + r = ®istered_signals [registered_signals_count++];
> +
> + r->signum = sig;
> + r->count = 0;
> + r->write_fd = 0;
> +
> + if ((r->handler = signal (sig, default_handler)) == SIG_ERR) {
> + Mono_Posix_Signal_uninstall (sig);
> + return NULL;
> + }
> + return r;
> + }
> + return NULL;
> +}
The handling of the registered_signals array is not thread safe and not
signal safe.
lupus
--
-----------------------------------------------------------------
lupus at debian.org debian/rules
lupus at ximian.com Monkeys do it better
More information about the Mono-devel-list
mailing list