mark@cbnews.ATT.COM (Mark Horton) (07/06/89)
Suppose you're writing an application, like ed or vi, that runs interactively at the terminal, but when the user hits DEL, no matter what is going on, it aborts and comes back for another user command. Suppose this program has to be very portable; it must run on System V, Berkeley, ANSI, POSIX, and V7. In the old days, you would catch SIGINT and have the signal handler do a longjmp back to the main loop. Another reasonable approach was to have the signal handler just return, and the read from the tty that was probably interrupted could check to see if read exited with EINTR, and if so print another prompt and go back for another command. Then Berkeley 4.2BSD changed how signal worked, and if the handler returned, the read would just resume. It became accepted wisdom among authors of highly portable programs that the longjmp approach was the most portable method. (Checking, this is true of 4.2BSD and 4.3BSD, but SunOS 4.0 does it the System V way.) Now ANSI C has decreed that you can't call longjmp, or any other function except signal, from inside a signal handler for any signal except those raised by abort, raise, or SIGFPE. The reason given for this is that you might be in the middle of calling that function already when the interrupt comes in, and C library routine are not required to be reentrant. You can't call longjmp to get out of a signal handler because maybe the code was in the middle of calling longjmp when the signal came in. Obscure, but they're right. (On the other hand, I can't see the harm in reentering longjmp, you'll never get back to the first call.) So where does this leave the developer? ANSI C says the only way to exit from a SIGINT handler is to return. The routine is supposed to pick up where it left off, ala 4.2BSD (although the System V behavior of having the read return EINTR seems to be allowed too.) Even if you pretend 4.*BSD doesn't exist, you can imagine the complexity of checking for EINTR after every terminal read, if you call handy stdio routines like getchar, scanf, and fgets. (Nobody calls gets anymore, right? :-) Has anybody found a portable solution to this problem? Is there any conceivable implementation of longjmp that will have problems when it gets called 1 1/2 times? There is a similar problem with trying to call exit from a signal handler. exit flushes all stdio buffers, and you might have been in the middle of an I/O operation when the signal came in. This could cause a core dump in exit! Is this something that should be fixed in the ANSI C standard? Mark Horton
blarson@basil.usc.edu (bob larson) (07/07/89)
In article <7997@cbnews.ATT.COM> mark@cbnews.ATT.COM (Mark Horton) writes: >Is there >any conceivable implementation of longjmp that will have problems >when it gets called 1 1/2 times? On the Prime 50 series, longjump includes backtracking through the stack for cleanup$ condition handlers (none will be present in portable C) and for restoring registers. I don't think longjump from within longjump would cause problems unless your cleanup$ condition handler was non-reentrant, but I wouldn't guarentee this. Also, you would have to guarentee your second longjump call would have to be to the same or an earlier level, or you could have a real mess. -- Bob Larson Arpa: blarson@basil.usc.edu Uucp: {uunet,cit-vax}!usc!basil!blarson Prime mailing list: info-prime-request%ais1@ecla.usc.edu usc!ais1!info-prime-request
strouckn@nvpna1.prl.philips.nl (Louis Stroucken 42720) (07/07/89)
In article <7997@cbnews.ATT.COM> mark@cbnews.ATT.COM (Mark Horton) writes: [example on the handling of interrupts deleted] >Then Berkeley 4.2BSD changed how signal worked, and if the handler >returned, the read would just resume. It became accepted wisdom >among authors of highly portable programs that the longjmp approach >was the most portable method. (Checking, this is true of 4.2BSD >and 4.3BSD, but SunOS 4.0 does it the System V way.) > >Now ANSI C has decreed that you can't call longjmp, or any other >function except signal, from inside a signal handler for any signal >except those raised by abort, raise, or SIGFPE. Sounds like my programs will not be as portable as I would like them to be :(. Can anybody quote the standard's exact wording on this issue? Especially, will the following trick be legal? jmp_buf env; void handle_by_jump (int sig) { longjmp (env, sig); } void handle_by_raise (int sig) { (void) signal (sig, handle_by_jump); raise (sig); } > The reason given >for this is that you might be in the middle of calling that function >already when the interrupt comes in, and C library routine are not >required to be reentrant. You can't call longjmp to get out of a >signal handler because maybe the code was in the middle of calling >longjmp when the signal came in. Obscure, but they're right. (On >the other hand, I can't see the harm in reentering longjmp, you'll >never get back to the first call.) Sounds like above trick wont work... >So where does this leave the developer? ANSI C says the only way >to exit from a SIGINT handler is to return. The routine is supposed >to pick up where it left off, ala 4.2BSD (although the System V >behavior of having the read return EINTR seems to be allowed too.) >Even if you pretend 4.*BSD doesn't exist, you can imagine the >complexity of checking for EINTR after every terminal read, if >you call handy stdio routines like getchar, scanf, and fgets. (Nobody >calls gets anymore, right? :-) Yeah, where does this leave the developer? In our case, we are developing an application consisting of several cooperating processes. We use handle_by_jump type signal handlers to ensure that a process will not get into serious trouble without at least notifying its partners it's going to die. What other approaches are available? > Mark Horton Louis Stroucken strouckn@nvpna1.prl.philips.nl
gwyn@smoke.BRL.MIL (Doug Gwyn) (07/07/89)
In article <7997@cbnews.ATT.COM> mark@cbnews.ATT.COM (Mark Horton) writes: >Is this something that should be fixed in the ANSI C standard? No -- The Standard doesn't create the problem, it merely documents it. There is no prohibition against C implementations doing a better job than the minimum guaranteed by the C Standard. In fact the C Standard guarantees very little about signal behavior, because of the wide variety of signal() implementations in existence. Remember that the C Standard is not UNIX specific, but rather applies to all operating systems. IEEE Std 1003.1 (POSIX) provides specifications for a much more reliable signal mechanism. Presumably UNIX-like implementations will provide the extended POSIX signal support rather than merely a minimal ANSI C facility for signals.
dfp@cbnewsl.ATT.COM (david.f.prosser) (07/07/89)
In article <7997@cbnews.ATT.COM> mark@cbnews.ATT.COM (Mark Horton) writes: }Suppose you're writing an application, like ed or vi, that runs }interactively at the terminal, but when the user hits DEL, no }matter what is going on, it aborts and comes back for another }user command. Suppose this program has to be very portable; it }must run on System V, Berkeley, ANSI, POSIX, and V7. The version of C (K&R or ANSI) is orthogonal to the operating system. I assume you mean portable to various different UNIX(tm) systems, with old or new C implementations. } }In the old days, you would catch SIGINT and have the signal }handler do a longjmp back to the main loop. Another reasonable }approach was to have the signal handler just return, and the }read from the tty that was probably interrupted could check to }see if read exited with EINTR, and if so print another prompt }and go back for another command. This almost always worked, but you could always interrupt at certain points that caused subsequent code to misbehave. } }Then Berkeley 4.2BSD changed how signal worked, and if the handler }returned, the read would just resume. It became accepted wisdom }among authors of highly portable programs that the longjmp approach }was the most portable method. (Checking, this is true of 4.2BSD }and 4.3BSD, but SunOS 4.0 does it the System V way.) } }Now ANSI C has decreed that you can't call longjmp, or any other }function except signal, from inside a signal handler for any signal }except those raised by abort, raise, or SIGFPE. The reason given }for this is that you might be in the middle of calling that function }already when the interrupt comes in, and C library routine are not }required to be reentrant. You can't call longjmp to get out of a }signal handler because maybe the code was in the middle of calling }longjmp when the signal came in. Obscure, but they're right. (On }the other hand, I can't see the harm in reentering longjmp, you'll }never get back to the first call.) It's more than just a nonreentrant longjmp(): it's that no library function other than signal() is guaranteed not to be mucking around with static data. If you are in the middle of a stdio function, there are windows during which data structures are inconsistent. Short of wrapping all such critical regions with signal-preventing code, the "no functions can be called in an asynchronous signal handler for a portable program. } }So where does this leave the developer? ANSI C says the only way }to exit from a SIGINT handler is to return. The routine is supposed }to pick up where it left off, ala 4.2BSD (although the System V }behavior of having the read return EINTR seems to be allowed too.) }Even if you pretend 4.*BSD doesn't exist, you can imagine the }complexity of checking for EINTR after every terminal read, if }you call handy stdio routines like getchar, scanf, and fgets. (Nobody }calls gets anymore, right? :-) The recommended approach is for your signal handler to set an external volatile sig_atomic_t object and return. The main loop should check the value of this object at appropriate points. The 4.*BSD behavior for certain system calls does get in the way, unfortunately. For such systems, the nonportable longjmp() approach may be the only choice. } }Has anybody found a portable solution to this problem? Is there }any conceivable implementation of longjmp that will have problems }when it gets called 1 1/2 times? } }There is a similar problem with trying to call exit from a signal }handler. exit flushes all stdio buffers, and you might have been }in the middle of an I/O operation when the signal came in. This }could cause a core dump in exit! If you are using POSIX-based systems, you should be able to call _exit() without any problems. This, of course, is not highly portable--it is generally available only on UNIX systems. } }Is this something that should be fixed in the ANSI C standard? The only "fix" to the standard would be either to completely remove any notion of signal handling, or to standardize on a "secure" signal function set. There is nothing that can be done that makes the standard libraries signal-proof. There were enough proponents for the availability of some sort of asynchronous signal handling that signal remained in the standard, despite the technical problems with any sort of guarantees. } } Mark Horton Dave Prosser ...not an official X3J11 answer...
rml@hpfcdc.HP.COM (Bob Lenk) (07/08/89)
> No -- The Standard doesn't create the problem, it merely documents it.
Yes! As a common example, if the signal interrupts a malloc() call in
most implementations, malloc() may have static data structures in an
inconsistent state. If the signal handler calls longjmp(), and some
code later calls malloc() or one of its friends, the standard's
description of "undefined behavior" applies quite well on any UN*X
implementation I know of. Programs that work in these environments may
be avoiding malloc() and other troublesome routines by design (based on
knowledge of what routines are safe in this regard) or by luck, or they
may really have the problem but never (or rarely) encounter it.
Bob Lenk
rml@hpfcla.hp.com
hplabs!hpfcla!rml