[comp.std.c] Question: signal and program termination

meranda@iguana.cis.ohio-state.edu (deron meranda) (01/30/91)

I have some questions about the ANSI defined behavior of signals
and program termination.

1) Supposedly, any signal handler can be exited by returning from the
   handler, or calling one of abort(), exit(), or longjmp().  However,
   the use of longjmp() is discouraged because the signal could have
   interrupted a non-atomic operation.  But couldn't the same scenario
   occur by calling exit() within a handler?  Clearly, any cleanup
   functions set up by atexit() would have to be called.  However, if
   some data was left in an undefined state because of the signal,
   then couldn't these exit functions completely fail?

2) I am not quite sure of the connection (or allowable connection)
   between abort() and raise(SIGABRT).  What happens if the handler
   for SIGABRT is set to SIG_IGN and later abort() is called?  Also,
   what happens if a user supplied handler is installed for SIGABRT...
   Since abort() is not permitted to return, what would happen if
   the handler decided to call exit(), longjmp(), or even worse
   abort() again, rather than strictly returning?  Would this defeat
   the "no-return-permitted" characteristic of abort()?

3) I would assume that signal handlers are permitted to raise other
   signals.  Say, if the default action for SIGINT's handler was to
   terminate the program, would it be more appropriate to raise
   SIGABRT or SIGTERM -- or should it just call exit() and suffer the
   same problems mentioned in question 1?

4) A simple one:  supposedly all signal handlers are permitted to
   simply return, _except_ for those signals that indicate a
   computational exception.  Among the ANSI defined signals, which
   ones do indicate a computational exception?

Thanks!

Deron E. Meranda  ( meranda@cis.ohio-state.edu )

gwyn@smoke.brl.mil (Doug Gwyn) (01/31/91)

In article <87792@tut.cis.ohio-state.edu> <meranda@iguana.cis.ohio-state.edu> writes:
>   occur by calling exit() within a handler?  Clearly, any cleanup
>   functions set up by atexit() would have to be called.  However, if
>   some data was left in an undefined state because of the signal,
>   then couldn't these exit functions completely fail?

Sure; your code could easily have a bug like that.

Note that it is quite difficult to write RELIABLE programs that consist
of "concurrent" (or at least, asynchronous) communicating processes.
This reliability issue is why I invariably recommend that the ONLY thing
you do in a signal handler be to set a flag (of type sig_atomic_t),
possibly alter the signal state via a call to signal(), and then resume
execution, with the actual exception handling logic performed at one or
more appropriate points within your main application code (where you
know that the data structures are consistent).  Otherwise, you have to
lock out signals during "critical regions" etc. throughout the
application, which is tedious and error-prone.

>   What happens if the handler
>   for SIGABRT is set to SIG_IGN and later abort() is called?

The abort() implementation must in effect reset the state to SIG_DFL
unless there is a signal handler function established already, before
performing the raise(SIGABRT).  SIG_IGN is not honored by abort().

>   what happens if a user supplied handler is installed for SIGABRT...
>   Since abort() is not permitted to return, what would happen if
>   the handler decided to call exit(), longjmp(), or even worse
>   abort() again, rather than strictly returning?

abort() never returns to the point at which it was invoked (I would say
"never returns a value", except as a void function it doesn't have a
value to return anyway).  If the signal handler returns, abort() then
terminates the process.  Otherwise, it is possible for the program to
continue by e.g. a longjmp() from the signal handler.  If the signal
handler invokes exit(), the normal semantics for exit() apply.  If the
signal handler invokes abort(), then the semantics for abort() are
recursively invoked.  Indeed, it is possible to produce a loop this
way (presumably a stack overflow would eventually occur).

>3) I would assume that signal handlers are permitted to raise other
>   signals.  Say, if the default action for SIGINT's handler was to
>   terminate the program, would it be more appropriate to raise
>   SIGABRT or SIGTERM -- or should it just call exit() and suffer the
>   same problems mentioned in question 1?

In the usual case that a SIGINT would be externally (asynchronously)
generated, the standard says that the only standard function you may
call in a strictly conforming signal handler would be signal(SIGINT,).
In the case of a synchronous signal generated by raise() or abort(),
you may safely use all the standard library functions, including raise().

As I mentioned previously, I don't recommend that an asynchronously
invoked signal handler attempt to do much more than set a flag.

>   computational exception.  Among the ANSI defined signals, which
>   ones do indicate a computational exception?

SIGFPE.  I would also expect that many implementations will choose to
define the "computational exceptions" to include SIGILL and SIGSEGV,
in order to be allowed to have undefined behavior if the signal handler
attempts to return in those cases.

scjones@thor.UUCP (Larry Jones) (01/31/91)

In article <87792@tut.cis.ohio-state.edu>, meranda@iguana.cis.ohio-state.edu (deron meranda) writes:
> 1) Supposedly, any signal handler can be exited by returning from the
>    handler, or calling one of abort(), exit(), or longjmp().  However,
>    the use of longjmp() is discouraged because the signal could have
>    interrupted a non-atomic operation.  But couldn't the same scenario
>    occur by calling exit() within a handler?  Clearly, any cleanup
>    functions set up by atexit() would have to be called.  However, if
>    some data was left in an undefined state because of the signal,
>    then couldn't these exit functions completely fail?

The difference is that exit() exits from the program without the
interrupted operation ever being resumed.  Although it is possible
that the exit handlers could fail because an interrupted operation
left some global data in a fatally inconsistent state, careful
coding should be able to prevent fatally inconsistent states.  A
quality implementation should code all the library routines this
way.

> 2) I am not quite sure of the connection (or allowable connection)
>    between abort() and raise(SIGABRT).  What happens if the handler
>    for SIGABRT is set to SIG_IGN and later abort() is called?  Also,
>    what happens if a user supplied handler is installed for SIGABRT...
>    Since abort() is not permitted to return, what would happen if
>    the handler decided to call exit(), longjmp(), or even worse
>    abort() again, rather than strictly returning?  Would this defeat
>    the "no-return-permitted" characteristic of abort()?

abort() is required to raise(SIGABRT) and then, if that returns, exit().
That prevents problems with ignoring SIGABRT or catching it and then
returning.  If the handler calls exit(), that's fine too, since that
prevents abort from returning as well.

> 4) A simple one:  supposedly all signal handlers are permitted to
>    simply return, _except_ for those signals that indicate a
>    computational exception.  Among the ANSI defined signals, which
>    ones do indicate a computational exception?

You must be working from an old draft -- the final version explicitly
lists SIGFPE (which is the only computation exception signal defined)
and then mentions the possibility of other implementation defined
signals.
----
Larry Jones, SDRC, 2000 Eastman Dr., Milford, OH  45150-2789  513-576-2070
Domain: scjones@thor.UUCP  Path: uunet!sdrc!thor!scjones
This sounds suspiciously like one of Dad's plots to build my character.
-- Calvin

guido@cwi.nl (Guido van Rossum) (01/31/91)

scjones@thor.UUCP (Larry Jones) writes:

>The difference is that exit() exits from the program without the
>interrupted operation ever being resumed.  Although it is possible
>that the exit handlers could fail because an interrupted operation
>left some global data in a fatally inconsistent state, careful
>coding should be able to prevent fatally inconsistent states.  A
>quality implementation should code all the library routines this
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>way.
 ^^^^

I doubt it.  Both the C standard and POSIX explicitly forbid the use in
signal handlers of printf(), malloc() and related things for exactly
this reason: if you get an asynchronous signal while one of these is
manipulating its global state, you may be in trouble if you try to use
that global state from the handler.  Relying on "quality
implementations" only passes the problem on to the poor guy who has to
port your code to a platform with only a lesser-quality implementation
available.  BTW, most of today's stdio and malloc implementations are
lesser-quality, according tou your standards...

--
Guido van Rossum, CWI, Amsterdam <guido@cwi.nl>
"It's a bit runny, sir"