Document: WG14 N1318

longjmp() from signal handler

Submitter: Fred Tydeman (USA)
Submission Date: 2008-07-13
Previous version of paper: N1305
Related WG14 documents: C90 Defect Report 152, N689, N735, N773, N756, N787, N789, N821, N841, N868, N873
Other documents: POSIX
Subject: longjmp() from signal handler

Problem being solved:

Currently, there is no way to leave a signal handler for SIGFPE and continue execution. abort() and _Exit() are the only two functions that can be called from a signal handler; both result in program termination. Both return (for SIGFPE, SIGILL, SIGSEGV, or implementation-defined values) and longjmp() (for any signal) being used by a signal handler results in undefined behaviour.

Since a SIGFPE need not happen during a floating-point instruction, that is, it may happen tens or hundreds of integer instructions later, doing a return from the signal handler for SIGFPE makes no sense and remains undefined behaviour with this feature. Also, if the SIGFPE does happen with the exceptional floating-point instruction, doing a return would go back to the same bad place, so unless a portable way of altering the instruction's operands were found, the same SIGFPE would re-happen.

Existing practice (of longjmp() from signal handlers of at least SIGFPE): Several compilers support this in some contexts (but I had to use my own feenabletrap() function to turn a floating-point exception into a raise of SIGFPE). It is unknown if it works in all contexts.

One paper that I found that mentions the use of signal handlers is: Xiaoye Li and James Demmel, Faster Numerical Algorithms via Exception Handling, IEEE Transactions on Computers, 43(8), 1994, pp. 983-992.

After doing more research, here is a summary of the history of this idea.

C89: The longjmp Function had a 3rd paragraph in the Description (it is missing in C99, which is an undocumented quiet change): As it bypasses the usual function call and return mechanisms, the longjmp function shall execute correctly in contexts of interrupts, signals and any of their associated functions. However, if the longjmp function is invoked from a nested signal handler (that is, from a function invoked as a result of a signal raised during the handling of another signal), the behavior is undefined.

The C99 Rationale in The longjmp function has: Some implementations leave a process in a special state while a signal is being handled. Explicit reassurance must be given to the environment when the signal handler returns. To keep this job manageable, the C89 Committee agreed to restrict longjmp to only one level of signal handling. No mention is made that the ability to longjmp from signal handlers was removed in C99.

C90 Defect Report 152 raised the issue that signal function and longjmp function appeared to contradict each other. The committee response was: The longjmp function shall execute correctly when called from a non-nested signal handler invoked through calls to the raise or abort functions; if longjmp is called from a signal handler invoked by other means, or from a nested signal handler, the behavior is undefined.

Item 13 in WG14 papers N689, N735, N773 was proposing for C99 that longjmp be added to the list of functions callable in signal handlers.

N756 was proposing for C99 that floating-point exceptions, after trapping was enabled for them, be turned into raise of SIGFPE. It also required that signal handlers be able to longjmp as the way to continue program execution.

The minutes, N787, item 32, committee discussion on signal handlers, says that [signal handlers for] SIGINT and SIGFPE cannot call longjmp().

N789 is the C99 proposal that removed the paragraph from longjmp about one level of signal handling. It also has a discussion on 3 ways a signal handler can be invoked, the 4 ways a signal handler can complete, and some potential problems with signal handlers.

N821, item PC-UK0097 (major writeup on longjmp and signal handlers)
N841, item PC-UK0097: committee response: no change.
N868, UK comments: longjmp() and exceptions flags
N873, UK comments: longjmp() and exceptions flags: committee response

Action Item: Post to WG14 email reflector and the POSIX/Austin group email reflector to see if it breaks any implementations? The following are a summary of those responses.

One person said the requiring longjmp() to be callable from signal handlers will have problems with signal masks and would break binary compatibility. One way around that is to introduce a new set of interfaces (such as the POSIX ones).

Several people suggested that I investigate POSIX's siglongjmp() and sigaction(). POSIX's signal() and sigaction() have words similar to what was in C89 (that was removed for C99). However, longjmp() and siglongjmp() are not in the async-signal-safe functions list in XSH 2.4.3, therefore, in most cases, using either in a signal handler results in undefined behaviour.

One implementor of Ada (on top of C) used longjmp() from signal handler to implement the Ada selective abort construct.

One vendor said longjmp() from signal handler is known to break some implementations due to register handling optimization; and that is why the feature was not adopted into C89.

One person said that an implementation on x86 must include the x87 floating-point environment in the object saved by setjmp(), otherwise there will be problems with the x87 stack due to the longjmp().

One person said the some platforms require decoding FPU status bits to be able to recover from SIGFPE. And that those implementations provided that information as an additional argument to the signal handler. Hence, that is non-portable and an extension to standard C.

One person said that because of imprecise interrupts and parallelism, a SIGPFE caused by user's mainline code might signal when one is in the middle of a stateful library function, thereby, causing undefined behaviour.

CERT, in the C coding guidelines, has CERT SIG32-C. Do not call longjmp() from signal handler.

For this feature to be useful, one would need new functions to enable, disable, and query trapping of floating-point enceptions, such as feenabletrap(), fedisabletrap(), and fetesttrap(). A trapped FP exception would be turned into a raise of SIGFPE [as required by the LIA annex in C99]. At least one commercial implementation has already done that many years ago.

IEEE-754:2008 (as compared to IEEE-754:1985) moved away from general trap handlers in favor of alternate exception handling. Alternate exception handlers specify lists of exceptions and actions to be taken for each listed exception if it signaled. This exception handling is on a block by block basis, including nested blocks.

Choices, as I see them: