[MLton] interrupted system call

Stephen Weeks MLton@mlton.org
Thu, 25 Mar 2004 08:59:50 -0800


> All I was trying to say is that there seemed to me to be two different
> programming models on the table.  I was thinking back to Stephen's orignal
> argument against using SA_RESTART:
> 
> > I've been playing around with using itimers to do timeouts, and I
> > think there is a problem if we use SA_RESTART.  Suppose we have an SML
> > program that installs an SML signal handler for Posix.Signal.usr1 and
> > then makes a call to Posix.IO.readVec, which blocks at the read system
> > call.  If a SIGUSR1 arrives, then the C runtime system handler will
> > run, setting the limit so that at the next limit check the SML handler
> > will run, and then terminate.  The system call will then restart and
> > block.
> >
> > So, it looks like to me that the SML signal handler will never get a
> > chance to run.  This seems wrong.

Now I understand.  My original argument was that it was wrong for the
signal handler not to get a chance to run applied because we weren't
in a critical section.  I made no claims about being in a critical
section.

> If I always want a chance to abort the read when a signal arrives, then I
> need to program differently, depending on what model is being used.

Agreed, although I can't think of the situation when you would always
want the chance (and I would like to see one).

> If the model is along the lines of 2abc, then in order to abort, I
> can't call Posix.IO.readVec from within a critical region.  (Which
> makes perfect sense, since entering the region I gave up the ability
> to run the signal handler.)  But, I can put the abort code in the
> signal handler (or a thread the signal handler switches to).  If the
> model is along the lines of the canHandle check that I suggested,
> then I can call Posix.IO.readVec from within a critical section, but
> I will need to handle the EINTR SysError exception; but, I can put
> the abort code in the exception handler.

Agreed. 

> So, I still claim that with 2abc, you need to program along A.

Agreed.  How about the following tweak?

In the case where one wants to abort the read, it does seem simpler
programming to do it via an exception handler rather than a signal
handler.  If we need this, perhaps we could add a bool ref that lets
one control the restarting behavior of system calls.  Of course, we'd
want to make sure (via fluidLet) to set the flag correctly in the
places in the basis library that assume the signal is restarted.  With
the flag you can use A, which is simpler, most of the time, and use B
when you need it.

> What I'm worried about is a system call that needs this kind of pre- or
> post- processing that can error with EINTR.  (I don't know if we have any,
> but it seems likely.)

It does seem likely.  But it seems like the critical section works
fine in that case.  It will cause the system call to be restarted
until it finishes.  (Unless you switch to model B, but I don't see why
you would)

> > I think this problem also goes away once we get CML in.
> 
> Watch this space. ;-)

Excellent.

> Because if you block all signals, you're beholden to unblock all signals
> when the ML signal handler gets a chance to run.

No.  You can save the mask and then restore it.

 let
    val m = Signal.Mask.getBlocked ()     (* not there yet *)
    val () = Signal.Mask.block Signal.mask.all
 in
    dynamicWind (fn () => call raiseSys,
                 fn () => Signal.Mask.setBlocked m)
 end