Public Comment Number PC-UK0097 ISO/IEC CD 9899 (SC22N2620) Public Comment =========================================== Date: 1998-02-25 Author: N.M Maclaren Author Affiliation: Self Postal Address: University of Cambridge, Computer Laboratory, New Museums Site, Pembroke Street, Cambridge CB3 3QG, United Kingdom E-mail Address: Telephone Number: +44 1223 334761 Fax Number: +44 1223 334679 Number of individual comments: 1 Comment 1. Category: Normative change to existing feature retaining the original intent Committee Draft subsection: 5.1.2, 7.11 Title: Reliable signal handling Detailed description: Section 7.10.2.1 The longjmp function has removed any statement about longjmp being supported in signal handlers. As it didn't work anyway, for other reasons, this is a major quiet change that has no effect! But some people will be unhappy. The following is a possible change that would improve (though not resolve) quite a lot of the problems. It is couched in terms of recommended practice, where an implementation is required to say whether it follows such practice. The handling of system-generated signals is a very nasty area indeed, and few (if any) languages get it 'right'. Most simply opt out and leave it undefined. Unfortunately, the specification in C89 is both extremely hard to implement successfully (even under Unix) and is almost unusable in portable programs because of the number of exclusions necessary to make it implementable at all. There have been several heated debates on comp.std.c on this. There are some well-known problems with returning from signal handlers, but there are as many or more with jumping out of handlers. In particular, all updated non-volatile global variables become undefined (see the "least requirements" section in 5.1.2.3). And I can witness that this corresponds precisely with many or most implementations. Lastly, and most cogently, C9X has (correctly) removed any that jumping out of signal handlers is supported! In that sense, C9X has introduced a major quiet change, but (as mentioned above) the facility never was usable. This proposal defines recommended practice that restores the functionality that was intended by C89. This wording should be added to section 5.1.2 Execution environments to encourage implementors to do better in a defined and consistent fashion (thus enhancing portability), without making life impossible for them. It should be possible to implement usefully under almost all modern systems with reasonable signal handling (including MVS and Unix). The author has considerable experience of precisely this area under MVS, and a fair amount under Unix and several other systems. Note that I am assuming that the implementation can provide signal-safe library functions, except in cases where efficiency is critical. This can be done on every modern architecture that I know of, with minimal overhead (though with possible storage leaks etc.) The cases where it would be seriously inefficient are permitted to be handled differently by the following description. I know that some vendors will say that implementing a safe malloc() is hard. But I have done that. It isn't hard - it just takes thought and care. In fact, most of the exclusion sections below are unnecessary, but were put there to prevent an implementor from having to jump through hoops if the system is more than usually perverse. If people want to ask me questions directly, my Email address is nmm1@cam.ac.uk. All of the following text is proposed for inclusion. 5.1.2.4 Reliable signal handling A conforming implementation shall define whether it supports reliable signal handling and, if so, any environment that needs to be specified during compilation or execution or both to select that support and how a conforming program can determine that it is executing in this state. When such conditions are satisfied, the program will be described as being in reliable signal mode.999 999. The least requirement on a conforming implementation is that it defines that it does not support reliable signal handling. The remainder of this section applies only to programs in reliable signal mode, and defines or implementation-defines semantics for behaviour that would otherwise be undefined. Nothing in this section changes the validity or meaning of any strictly conforming program, or any program not executing in reliable signal mode. A facility is supported by an implementation if it has the effect described in this section or elsewhere in the standard. The behaviour of conforming programs is well-defined for signals raised by calls to the raise and abort functions, so any reference to signals in this section should be taken to refer to system-generated signals. An implementation shall define any restriction on the handling of signals raised by calls to the raise and abort functions that is not shared by the handling of system-generated signals. 5.1.2.4.1 Extra defined semantics If a static storage duration variable of type volatile sig_atomic_t has a defined value at one sequence point and is updated solely between that and the following sequence point by being assigned defined values, then then at all times between those sequence points (inclusively) it shall contain one of its initial or assigned values. How many times any such assignments occur (if at all) and in what order is unspecified. It is implementation-defined whether static duration variables of type volatile void * have the same consistency semantics as static duration variables of type volatile sig_atomic_t (as described in the previous paragraph), and whether any special environment must be specified for this to be the case. It is implementation-defined whether nested signals (that is signals raised during the execution of a signal handler) are supported. This support may be signal-dependent and there may be other implementation-defined constraints. It is implementation-defined whether multiple signals (that is a second signal raised before a previous one has been ignored or its signal handler has started executing) are supported and, if so, whether a second signal will be suspended until after the first signal has been processed or whether the second one will handled as a nested signal. This support may be dependent on which signals occur and in which order, and there may be other implementation-defined constraints. It is implementation-defined whether signals raised during the execution of functions registered by atexit are supported. 5.1.2.4.2 Signal sequence points The execution of the setjmp macro after the arguments have been evaluated shall be a special sequence point, described in this section as a signal sequence point. The sequence points following the evaluation of the arguments of calls to the longjmp, signal, raise, abort or exit functions shall be signal sequence points. If a signal handler is called as the result of raising a signal, there shall be a signal sequence point following the creation of the argument list for the call to the signal handler. If a signal handler is called as the result of raising a signal and it returns (whether by executing a return statement or by reaching the end of the function), then there shall be a signal sequence point immediately following the return from the handler. It is implementation-defined whether there are any signal sequence points associated with a call to the system function. An implementation shall satisfy the following conditions at a signal sequence point: * All the conditions for a sequence point shall be satisfied. * Unless the signal sequence point is associated with the call to a signal handler, all objects with static storage duration and all automatic objects defined without the register attribute prior to the latest entry to the function that contains the signal sequence point shall be stable in the sense that previous evaluations are complete and subsequent evaluations have not yet occurred. * All signals raised as a direct consequence of executing previous code shall have been handled, and ones raised as a direct consequence of executing subsequent code shall not yet have had any effect on the program. Signals raised as the result of computational exceptions are direct consequences of executing the code, but those raised as the result of operating on files may be indirect, except as restricted by the following paragraph. * All previous calls of the remove, rename, fopen, freopen, fflush and fclose functions shall have been completed and subsequent calls shall not have been started, and all input and output to interactive devices required by the standard shall have taken place. The intention is that the actions of these calls shall have been synchronised with the environment. 5.1.2.4.3 Handling reliable signals If a system-generated signal is raised, then any call to a signal handler shall behave as if it were invoked by a simple call to the raise function, except for the following: * The call to the raise function need not correspond with a well-defined location in the code, except as defined in 5.1.2.3 and the following constraints. * The values of any volatile objects updated since the previous sequence point are indeterminate, except for static storage duration variables of type volatile sig_atomic_t updated only by assigning defined values, and those of type volatile void * if the implementation defines them to have the same semantics. * The values of any non-volatile objects updated since the previous signal sequence point are indeterminate, including those objects and system variables that the standard defines may be updated by any of the library functions called since that signal sequence point. * If any library function that returns a pointer to storage that may be overwritten by subsequent calls (such as the getenv function, 7.14.4.4, or the time conversion functions, 7.16.3) has been called since the previous signal sequence point, the contents of any such storage returned by any previous call are indeterminate. * If any library function that uses internal storage or state (such as the strtok function, 7.15.5.8, the multibyte character functions, 7.14.7, or the multibyte to wide-character conversion functions, 7.19.7) has been called since the previous signal sequence point, the contents of any such internal storage or state are indeterminate. They may be reset to a defined state by a call that is defined to initialise such storage or state (e.g. by calling the strtok function with a non-null pointer in its argument s1 or by calling the mblen function with a null pointer in its argument s). * If the setlocale function has been called since the previous signal sequence point, the locale is indeterminate and the effect of calling any library function that uses the locale is undefined. The locale may be set to a defined state by a call to setlocale with first argument LC_ALL and a second argument that is not NULL. * If any of the floating-point control modes have been modified since the previous signal sequence point, using the functions defined in 7.6 or otherwise, the floating-point environment will be indeterminate on entry to the handler. An implementation may define other circumstances under which the floating-point environment is indeterminate on entry to a signal handler. The floating-point environment may be set to a defined state by a call to fesetenv with an argument of FE_DFL_ENV. * If any of the floating-point control modes are modified during the execution of a signal handler, the state of the floating-point environment upon leaving the handler is implementation-defined and may be defined to be indeterminate. The floating-point environment may be set to a defined state by a call to fesetenv with an argument of FE_DFL_ENV. * An implementation may define circumstances under which the floating-point exception flags are indeterminate upon entry to a signal handler. * Upon leaving a signal handler by return or by calling the exit function, it is implementation-defined whether the floating-point exception flags are restored to the values that they had immediately before the signal was raised, or whether they preserve the values that they had immediately before leaving the signal handler. * Upon leaving a signal handler by calling the longjmp function, it is implementation-defined whether the floating-point exception flags are restored to the values that they had immediately before the signal was raised, whether they are restored to the values that they had immediately before the setjmp macro was executed, or whether they preserve the values that they had immediately before leaving the signal handler. * Upon leaving a signal handler by calling the longjmp function, the behaviour is undefined unless the jump buffer was set up either at a time when no signal handler was executing or earlier in the execution of signal handler for the current signal. An implementation may define other circumstances under which leaving a signal handler by longjmp leads to undefined behaviour. * If the signal function (7.11.1.1) has been called for a particular signal since the previous signal sequence point, the signal handler for that signal is indeterminate and the effect of raising that signal is undefined. It may be reset by another call to the signal function for that signal, but the return value will be indeterminate. * The state of any file object that has been accessed other than by calls to the ferror or feof functions since the previous signal sequence point is indeterminate. * If the rand or srand function (7.14.2) has been called since the previous signal sequence point, the effect of calling the rand function subsequently is undefined. The rand function may be reset to a defined condition by a call to the setrand function. * If the realloc function (7.14.3.4) has been called since the previous signal sequence point, the state of the storage pointed to by its argument ptr is indeterminate. * If the atexit function (7.14.4.2) has been called since the previous signal sequence point, it is undefined whether the function is registered. * If a signal is raised during a call to the system function (7.14.4.5), the behaviour is implementation-defined.