[MLton] cvs commit: better blocking/unblocking of signals in runtime and basis

Stephen Weeks MLton@mlton.org
Thu, 15 Apr 2004 15:32:14 -0700


> It really depends on what we want to happen when block/unblock-ing
> signals while running a handler.  Given the way the MLton.Signal library
> is designed, I would want it to have essentially the same semantics as
> what happens when you  sigprocmask  while in a C handler installed by
> sigaction.

I'd love to have some input from others here, but I don't know of any
reason why sigprocmask would behave any differently while in a handler
than in normal code.

What happens in the C world is that when the handler is installed one
specifies the signals that one wants to be blocked whenever the
handler runs (implicitly, this always includes the signal being
handled).

On the other hand, in our SML world, one does not specify anything
when the handler is installed about signals to be blocked whenever the
handler runs.  Instead, all signal handlers run with all signals that
are handled by ML blocked.  Also, we always run signal handlers within
a critical section.

One reason why we did this is that the signal handler thread loops
over gcState.signalsPending, running a handler for each signal whose
bit is set.  Once all the handlers have been run, we call
GC_finishHandler, with empties gcState.signalsPending.  So, we could
lose signals if we allowed gcState.signalsPending to be modified while
we are running handlers.  We could cut down the period when signals
need to be blocked dependency a lot by having the signal handler
thread first loop over signalsPending and collect all the signals that
need to be handled.  It could then empty signalsPending and unblock
signals (restoring the mask to whatever it was when we first blocked
at the start of the signal handler).  So, the rest of the signal
handler would run just as user code with regard to blocked signals.
Except, of course, it would be in a critical section.  So, it could be
interrupted on the C side to record receipt of a signal, but it could
not be interrupted on the SML side.

If we did this, then there would be no need for a difference in the
behavior of Posix_Signal_sigprocmask when in a signal handler versus
normal code.  Also, GC_finishHandler wouldn't need to unblockSignals.

In fact, maybe we could get rid of {block,unblock}Signals from
enter/leave.  We are using them right now to prevent GC_handler from
mucking with limit while we are running C code.  What if instead we
treat enter/leave as a critical section, with enter doing canHandle++
and leave doing canHandle--.  Then GC_handler will do the right thing.
And we won't need to keep track of gcState.signalsBlocked at all.  And
we won't need to keep an ML-side representation of the signal mask.
And the calls to Signal.Mask.{block,unblock,setBlocked} can go back to
using sigprocmask with the appropriate how, doing no other
computation.

How does that sound?

On another note in reading through things while thinking about this
mail, I noticed that the fast branch in GC_switchToThread doesn't do
the blockSignals that would be performed by enter () as in the slow
branch.  That seems wrong (but may go away if we do what I suggest
above).

Also, the user guide is incorrect.  It says

	All signals are automatically blocked for the duration of a
	handler. 

Maybe it should just say signal handlers are run in a critical
section.  Especially if do what I suggest above.

> The reason it matters, is that if we allow the set of blocked signals to
> be changed in a handler, then we still need to update
> gcState.signalsBlocked, so that when the runtime leaves the ML signal
> handler, it will restore the correct set of blocked signals.  In that
> case, we're still building the sigset and only skipping the call to
> sigprocmask.

Thanks.  I understand.