Path: utzoo!utgpu!jarvis.csri.toronto.edu!rutgers!gatech!ukma!tut.cis.ohio-state.edu!att!cbnewsl!dfp From: dfp@cbnewsl.ATT.COM (david.f.prosser) Newsgroups: comp.std.c Subject: Re: how to exit from a signal routine Keywords: ANSI, longjmp, signal Message-ID: <1022@cbnewsl.ATT.COM> Date: 7 Jul 89 14:06:02 GMT References: <7997@cbnews.ATT.COM> Reply-To: dfp@cbnewsl.ATT.COM (david.f.prosser) Organization: AT&T Bell Laboratories Lines: 94 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...