svn commit: r208868 - in head/usr.bin: bc dc

Jilles Tjoelker jilles at stack.nl
Sun Jun 6 16:31:28 UTC 2010


On Mon, Jun 07, 2010 at 01:17:34AM +1000, Bruce Evans wrote:
> The old code attempted to deal with this by using the usual hack of
> only setting a variable of type sig_atomic_t (bmachine.interrupted)
> in the interrupt handler, but that usually doesn't work on any BSDish
> system since BSDish systems by default restart most syscalls after an
> interrupt, so if the syscall is waiting for user input, it will be
> restarted after a user SIGINT and resume waiting.

> top(1) is broken in FreeBSD in the same way that dc was, while in the
> vendor version it is broken in theory but rarely in practice by the
> undefined behaviour.  Try this: start top and enter command mode,
> e.g. by "s<Enter>.  Now ^C at the prompt appears not to work.  But
> a newline (possibly preceded by other input) makes the old ^C work
> unexpectedly.  This mostly works in the vendor version because the
> signal handler is unsafe and just exits.  Someone "fixed" this in
> FreeBSD using the flag hack, without adding the (possibly very large)
> complications needed to make the flag hack actually work.

> To make the hack work you must either:
> - turn off SA_RESTART for most signal actions.  Then deal with most
>    syscalls possibly failing with errno = EINTR.  Code portable to
>    old non-BSD systems need complications to deal with these EINTRS.
>    Now POSIX with XSI extensions in 2001 (maybe standard now) has
>    SA_RESTART, so a not-so-portable portable application can turn on
>    SA_RESTART so as not to have to deal with the EINTRS, and have
>    the BSD problems instead.

In most cases, relying on EINTR like this introduces a race condition:
it could be that the signal handler is called between the last check of
the flag and blocking in a system call (checked for EINTR). In that
case, the system call must complete before the program reacts to the
signal, which could take very long.

Exceptions include sigsuspend(2) and pselect(2) (note some FreeBSD
versions have a pselect(3) purely in libc, which does not avoid this
race condition). Another function of interest is sigtimedwait(2).

A kludge for this race condition and problems where the flag is not
checked often enough is an alarm(3) or similar call in the signal
handler, forcing the program to be terminated bluntly if it does not
react fast enough.

Other methods include using signal masks or flags to make terminating
the program or longjmp'ing from the signal handler safe. This works best
if there are few places where the program can block for longer periods.
(If you arrange that your signal handler never interrupts an
async-signal-unsafe function, or if it is known that this particular
invocation did not interrupt an async-signal-unsafe function, it may
call async-signal-unsafe functions without problems.)

> - locate all syscalls for i/o on interactive devices and other syscalls
>    of interest, and turn off SA_RESTART only while making these syscalls
>    and deal with the resulting EINTRs only for these syscalls.  This is
>    probably easier than a global turnoff of SA_RESTART.  This is probably
>    practical in a small application like top or dc (there seems to be only
>    1 critical read() syscall in top), but probably impossible in a large
>    application and difficult in one where the i/o is in libraries.

Many libraries retry potentially blocking system calls on EINTR.

If you are wasting two system calls per "slow" system call anyway, it
would likely be better if they were unmask/mask signals rather than
toggling SA_RESTART.

-- 
Jilles Tjoelker


More information about the svn-src-head mailing list