[Mono-dev] Control-C handler

Jonathan Pryor jonpryor at vt.edu
Wed Jan 9 13:30:41 EST 2008


On Wed, 2008-01-09 at 11:52 -0500, Avery Pennarun wrote:
> On 08/01/2008, Jonathan Pryor <jonpryor at vt.edu> wrote:
> > 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.
> 
> It seems to me that lupus's suggestion is more general than this.  In
> other words, you could implement your desired API above on top of his
> without any extra overhead (ie. the overhead is inherent to the
> interface), while you couldn't implement his on top of yours without
> doing a lot of unnecessary work (ie. bouncing into a thread and then
> back again).

Fair point.

> Since mono's signal API is already (presumably) non-standard, it
> should be harmless to correct it by introducing a new nonstandard one
> that works properly.

Now what do you mean by "non-standard," i.e. what's the "standard"
here?  .NET?  Windows has no signals, so .NET won't expose such a
concept.

On the other hand, the Mono.Unix.Native types have existed for quite
some time and intend to follow the Standard C and POSIX interfaces as
closely as possible.  signal(2) is part of the C standard, and is thus
exposed via Stdlib.signal().  It's as standard as you're going to get.

> Note that polling a variable is a fine way to handle signals in many C
> programs.  I often write mainloops of the form "while (!want_to_die)",
> where want_to_die is set by my SIGTERM/SIGINT/etc handler.  Involving
> a thread for this purpose would be overkill.  While this method does
> count as "polling", it is syscall-free polling and only happens at the
> places where you would want to shut down cleanly anyway.
> 
> This reminds me, though: what happens to things like socket
> read/write/connect system calls in mono when a signal comes in?  In C,
> they get interrupted and return EINTR/EAGAIN, which is why you pop
> back to the mainloop and get a chance to poll your signal variable.

The same thing would happen if you use the low-level Syscall methods --
-1 would be returned and Stdlib.GetLastError() would return Errno.EINTR
or Errno.EAGAIN.  The higher-level Mono.Unix types like UnixStream
ignore EINTR.

However, this does raise an interesting problem with a thread-based
approach: since signal dispatching would happen on another thread, there
is no guarantee (and no way to guarantee) that the signal will be
dispatched before the system call returns:

        1. Thread 1: Syscall.write()
        2. kernel dispatches signal to process
        3. Thread 1: Syscall.write() returns -1/EINTR
        # some future point in time...
        4. Thread 2: Dispatches signal originally raised in (2).

Thread 1 can't safely check e.g. a variable after the EINTR return (in
the normal C fashion) because it might not have been set by the signal
handler yet.  It would instead need to wait on a semaphore/condition
variable, which the signal handler could set, to ensure that the threads
are properly synchronized.

On the plus side, the signal handler thread can actually use
semaphores/etc. as it's not running within signal handler context.

So perhaps it would make more sense to expose lupus' approach and layer
Stdlib.signal() atop of it.  Though to do that we'd need a better name:
there is already a Mono.Unix.Native.SignalHandler type, and trying to
describe the difference between SignalHandler and UnixSignalHandler is
something I don't look forward to.  Perhaps instead:

        public class Signal  : System.Threading.WaitHandle {
                public Signal (Signum signal);
                ~Signal ();
                public Signum Signum { get; }
                public bool IsSignaled { get; set; }
        }

though the WaitHandle base type might be pure crack (not sure);
regardless, IDisposable should certainly be implemented.

 - Jon





More information about the Mono-devel-list mailing list