[comp.lang.c] longjmp out of signal handler

rtm@christmas.UUCP (Richard Minner) (11/13/90)

In article <27608@mimsy.umd.edu> chris@mimsy.umd.edu (Chris Torek) writes:
>
>As soon as you mention `longjmp out of signal handlers' you have left
>either portability or reliability far behind (even if you are careful,
>doing *anything* with signals tends to make software unportable).
>
Please Chris, could you spare a few paragraphs to elaborate on this?
(You have so many :-)  (that was a compliment, by the way)

I was compelled recently (by the devil no doubt) to do this thing.
In short:
	catch_sigsegv();
	if (setjmp(sigsegv_jmp_buf) == 0)
		<do naughty stuff that might cause SIGSEGV>
	else
		<make note of the SIGSEGV>
	release_sigsegv();
	...
	void handler(sig) int sig;
	{
		<release the signal, check sig value...>
		longjmp(sigsegv_jmp_buf, 1);
	}

Is this really all that unportable and/or unreliable?  I asked about
this a while back when (I believe) Peter da Silva said it was evil,
but no one answered.  If it's some kind of secret just say so and
I'll understand.

-- 
Richard Minner  || {uunet,sun,well}!island!rtm     (916) 736-1323 ||
                || Island Graphics Corporation     Sacramento, CA ||

henry@zoo.toronto.edu (Henry Spencer) (11/17/90)

In article <15@christmas.UUCP> rtm@island.uu.net (Richard Minner) writes:
>>As soon as you mention `longjmp out of signal handlers' you have left
>>either portability or reliability far behind (even if you are careful,
>>doing *anything* with signals tends to make software unportable).
>>
>Please Chris, could you spare a few paragraphs to elaborate on this?

In case Chris doesn't have paragraphs to spare right now, here's a few
sentences. :-)  It is extremely difficult to guarantee that anything
will work portably in signal handlers, because the stranger C environments
put bizarre restrictions on them due to difficulties with calling conventions
and the like.  ANSI C specifies as "undefined" almost any interaction with
the world outside the signal handler except (a) calling signal() to reset
that particular signal, and (b) assigning a variable to a flag of a specific
(implementation-defined) type.  In particular, it is impossible to guarantee
that longjmp() will work from within a signal handler, because there are
machines where it won't.  This is really annoying in the presence of 4BSD's
gratuitous breakage of the default semantics of signals, but there is
nothing that can be done about it.
-- 
"I don't *want* to be normal!"         | Henry Spencer at U of Toronto Zoology
"Not to worry."                        |  henry@zoo.toronto.edu   utzoo!henry

lanzo@wgate.UUCP (Mark Lanzo) (11/17/90)

In a prior article rtm@island.uu.net (Richard Minner) wrote:
    
    I was compelled recently (by the devil no doubt) to do this thing.
    In short:
    	catch_sigsegv();
    	if (setjmp(sigsegv_jmp_buf) == 0)
    		<do naughty stuff that might cause SIGSEGV>
    	else
    		<make note of the SIGSEGV>
    	release_sigsegv();
    	...
    	void handler(sig) int sig;
    	{
    		<release the signal, check sig value...>
    		longjmp(sigsegv_jmp_buf, 1);
    	}
    
    Is this really all that unportable and/or unreliable?  I asked about
    this a while back when (I believe) Peter da Silva said it was evil,
    but no one answered.  If it's some kind of secret just say so and
    I'll understand.


I too ran into a situation where I had to do exactly what you have here.
Unfortunately, it seems to be the only way to handle some problems
(I despise using setjmp/longjmp for anything if it can be avoided).
In particular, it was mandatory that I longjmp out of the signal handler
since if I returned from the signal handler I got caught in an
infinite loop!  It seems that the system returns to exactly the same
instruction that caused the exception in the first place, thus causing 
the same error all over again.  {is this what it is supposed to do?}.

I did however use "_setjmp" and "_longjmp" rather than "setjmp" and
"longjmp", because the latter two functions screw around with the
process signal mask.  My equivalents to your "catch_sigsegv" and
"release_sigsegv" did all the setup & restoration of the signal mask
and exception vectors (they also trapped SIGBUS in addition to SIGSEGV).

Disclaimer:  this was under HP-UX 7.0 on HP9000 s300 series.
Your mileage may vary.  I don't know if "_setjmp" and "_longjmp"
are standard things or HP'isms.

-- 
Mark Lanzo                      | Wandel & Goltermann Technologies, Inc.
uunet!wgate!lanzo               | 1030 Swabia Court, Research Triangle Park
lanzo@wgate.wgate.com ??        | North Carolina 27709-3585
                                | Phone: (919) 941-5730  FAX: (919) 941-5751

rh@smds.UUCP (Richard Harter) (11/20/90)

In article <1990Nov16.181436.21736@zoo.toronto.edu>, henry@zoo.toronto.edu (Henry Spencer) writes:

> In case Chris doesn't have paragraphs to spare right now, here's a few
> sentences. :-)  It is extremely difficult to guarantee that anything
> will work portably in signal handlers, because the stranger C environments
> put bizarre restrictions on them due to difficulties with calling conventions
> and the like.  ANSI C specifies as "undefined" almost any interaction with
> the world outside the signal handler except (a) calling signal() to reset
> that particular signal, and (b) assigning a variable to a flag of a specific
> (implementation-defined) type.  In particular, it is impossible to guarantee
> that longjmp() will work from within a signal handler, because there are
> machines where it won't.  This is really annoying in the presence of 4BSD's
> gratuitous breakage of the default semantics of signals, but there is
> nothing that can be done about it.

Is it allowed to ask for help?  :-)

Let me present a simple scenario.  I have a command line driven interactive
program.  I want to be able to give it an interrupt and have it respond by
aborting the processing in the current command and returning control to the
main command loop (read/interpret/execute).  Since the interrupt can come
in the middle of a data structure manipulation I have carefully (I hope)
defined safe and unsafe states.  At the beginning of an unsafe section
of code I set a flag; at the end I lift it.  The handler checks whether
the flag is set; if it is not it does a longjmp.  If the flag is set the
handler sets another flag which is checked by the "unprotect" code which
does the longjmp.  I haven't seen any problems with this under SUN 3,
SUN 4, AIX, AUX, COFF, MIPS, ULTRIX, Concurrent, Apollo, and some others
which I forget off hand.  Maybe I've been lucky!  Is this a kludge?
What is the right way to do this, if any.  Here is some representative
code:

inthndler () {
	extern int interrupt;
	extern int protection;
	extern jmp_buf csienv;

	signal(SIGNINT,SIG_IGN);
	interrupt = 1;
	if (!protection) longjmp(csienv,1);
	}

.... schematic main loop ....

	while (more) {
		setjmp(csienv);
		if (interrupt) {
			signal(SIGINT,SIG_IGN);
			... sundry cleanup ...
			interrupt = 0;
			signal(SIGINT,inthndlr);
			}
		... main loop body ...
		}

.... and the macros for delimiting 'unsafe' code ....

#define PROTECT {extern int protection;protection = 1;}

#define UNPROTECT {extern int protection;\
	extern int interrupt;\
	protection = 0;\
	if (interrupt) inthndlr();}
 
[Before anyone says RTFM, believe me, I have RTFM -- a number of them.]
Mostly I can puzzle these things out, but the rules for manipulating
signal and setjmp/longjmp are somewhat obscure.  From what Henry is
saying I infer that the above scheme is not quite legitimate.  I am
quite willing to believe that it is a total crock.  However I have to
admit that I neither am sure that the above scheme is bona fide (and
portable) nor, if it is not, how one does it right.
-- 
Richard Harter, Software Maintenance and Development Systems, Inc.
Net address: jjmhome!smds!rh Phone: 508-369-7398 
US Mail: SMDS Inc., PO Box 555, Concord MA 01742
This sentence no verb.  This sentence short.  This signature done.

sarima@tdatirv.UUCP (Stanley Friesen) (11/21/90)

In article <15@christmas.UUCP> rtm@island.uu.net (Richard Minner) writes:
>Please Chris, could you spare a few paragraphs to elaborate on this?
>(You have so many :-)  (that was a compliment, by the way)

I am not Chris, but I will elaborate anyway.
The ANSI standard states that very few things are strictly conforming in
a signal handler.  In particular, a portable handler is mostly restricted to
updating volatile globals.

>I was compelled recently (by the devil no doubt) to do this thing.
>In short:
>	catch_sigsegv();
>	if (setjmp(sigsegv_jmp_buf) == 0)
>		<do naughty stuff that might cause SIGSEGV>
>	else
>		<make note of the SIGSEGV>
>	release_sigsegv();
>	...
>	void handler(sig) int sig;
>	{
>		<release the signal, check sig value...>
>		longjmp(sigsegv_jmp_buf, 1);
>	}
>
>Is this really all that unportable and/or unreliable?

Yes, it relies on a signal handling mechanism that does not have to be cleaned
up by the compiler/system before resuming normal code. (Such as operating
on a special stack, or leaving the hardware in a special no-interrupt state).

A more portable version is as follows:

	volatile int got_segv = 0;

	catch_sigsegv();
	<do naughty stuff that might cause SIGSEGV>
	if(got_segv)
	{
    		/* PROBLEM */
	}
	release_sigsegv();
	got_segv = 0;


	void handler(sig) int sig;
	{
		got_segv = 1;
	}

OOPS, even this is not portable, since it assumes that the return from
handler() does not restart the same instruction and generate a loop.


Answer, there is *no* portable solution.
-- 
---------------
uunet!tdatirv!sarima				(Stanley Friesen)

henry@zoo.toronto.edu (Henry Spencer) (11/22/90)

In article <249@smds.UUCP> rh@smds.UUCP (Richard Harter) writes:
>Is it allowed to ask for help?  :-)

No. (!)  The exact wording is

	If the signal occurs other than as the result of calling the
	`abort' or `raise' function, the behavior is undefined if the
	signal handler calls any function in the standard library other
	than the `signal' function itself (with a first argument of the
	signal number corresponding to the signal that caused the
	invocation of the handler) or refers to any object with static
	storage duration other than by assigning a value to a static
	storage duration variable of type `volatile sig_atomic_t'.
-- 
"I don't *want* to be normal!"         | Henry Spencer at U of Toronto Zoology
"Not to worry."                        |  henry@zoo.toronto.edu   utzoo!henry

sarima@tdatirv.UUCP (Stanley Friesen) (11/22/90)

In article <1990Nov16.181436.21736@zoo.toronto.edu> henry@zoo.toronto.edu (Henry Spencer) writes:
>In case Chris doesn't have paragraphs to spare right now, here's a few
>sentences. :-)  It is extremely difficult to guarantee that anything
>will work portably in signal handlers, because the stranger C environments
>put bizarre restrictions on them due to difficulties with calling conventions
>and the like.

Just to emphasize this point, I have recently come across a real implementation
where such a restriction exists.

I am currently learning the library interface to AmigaDOS, and its
implementation of 'signals' totally prohibits longjumps out of the handler.
This is due to several factors - mostly involving system resource handling
and task scheduling. (Get this, by default the handler is activated in
*supervisor* mode!! You really want to long jump out of that!?!?)
-- 
---------------
uunet!tdatirv!sarima				(Stanley Friesen)

rh@smds.UUCP (Richard Harter) (11/24/90)

In article <1990Nov21.181358.8660@zoo.toronto.edu>, henry@zoo.toronto.edu (Henry Spencer) writes:
> In article <249@smds.UUCP> rh@smds.UUCP (Richard Harter) writes:
> >Is it allowed to ask for help?  :-)

> No. (!)  The exact wording is

	[... text deleted -- read Henry's article for exact text.]

I assume this is from the ANSI standard.  From this I gather that in
fact there is no guaranteed portable way to longjmp out of a signal
handler.  From this I infer (perhaps incorrectly) there is no way
in ANSI C to do what I want that is safe.  On the other hand it is
*probably* the case that the code schema I posted will work fine.

Thanks for the response.
-- 
Richard Harter, Software Maintenance and Development Systems, Inc.
Net address: jjmhome!smds!rh Phone: 508-369-7398 
US Mail: SMDS Inc., PO Box 555, Concord MA 01742
This sentence no verb.  This sentence short.  This signature done.

henry@zoo.toronto.edu (Henry Spencer) (11/25/90)

In article <257@smds.UUCP> rh@smds.UUCP (Richard Harter) writes:
>I assume this is from the ANSI standard.  From this I gather that in
>fact there is no guaranteed portable way to longjmp out of a signal
>handler.  From this I infer (perhaps incorrectly) there is no way
>in ANSI C to do what I want that is safe.  On the other hand it is
>*probably* the case that the code schema I posted will work fine.

Correct.
-- 
"I'm not sure it's possible            | Henry Spencer at U of Toronto Zoology
to explain how X works."               |  henry@zoo.toronto.edu   utzoo!henry

rtm@christmas.UUCP (Richard Minner) (11/27/90)

Henry Spencer wrote:
> ...here's a few sentences. :-)
> It is extremely difficult to guarantee that anything
> will work portably in signal handlers...
> ANSI C specifies as "undefined" almost any interaction with
> the world outside the signal handler except (a) calling signal() to reset
> that particular signal, and (b) assigning a variable to a flag of a specific
> (implementation-defined) type.  In particular, it is impossible to guarantee
> that longjmp() will work from within a signal handler, because there are
> machines where it won't.  This is really annoying in the presence of 4BSD's
> gratuitous breakage of the default semantics of signals, but there is
> nothing that can be done about it.

Thanks for the sentences Henry, BUT :-)

Is my funky old Dec 7, 1988 draft way off or what?
On longjmp, from 4.6.2.1:

	As it bypasses the usual function call and return
	mechanism, 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.

And 4.7.1.1 on the signal function states explicitly that a signal
handler may terminate by executing the longjmp function.

Is it the `no nesting' clause that kills it?  If not, then I believe
that ANSI guarantees at least minimal support for longjmp'ing out of
handlers.  Mind you, it's not something I intend to do every day, but
I had good reason to do it this one time and it bothers me when Chris
says (paraphrased) "even if you're careful, you've left reliability
and/or portability far behind".  (In my case, portability is
secondary because the need is peculiar to my environment anyway,
but I'm very concerned about reliability.)

-- 
Richard Minner  || {uunet,sun,well}!island!rtm     (916) 736-1323 ||
                || Island Graphics Corporation     Sacramento, CA ||

henry@zoo.toronto.edu (Henry Spencer) (12/01/90)

In article <17@christmas.UUCP> rtm@christmas.UUCP (Richard Minner) writes:
>Is it the `no nesting' clause that kills it?  If not, then I believe
>that ANSI guarantees at least minimal support for longjmp'ing out of
>handlers...

Although the wording under longjmp() and under signal() could stand to be
a bit more consistent, there is an important distinction under signal()
that I glossed over.  In ANSI C, it is possible to explicitly cause a
signal with the raise() function (or abort(), for that matter).  Signal
handlers invoked thusly are specifically exempt from the restrictions,
so they can call longjmp() portably.  Hence the demand that longjmp() be
usable in signal handlers.  However, the harsher rules still apply to,
so to speak, accidental signals.
-- 
"The average pointer, statistically,    |Henry Spencer at U of Toronto Zoology
points somewhere in X." -Hugh Redelmeier| henry@zoo.toronto.edu   utzoo!henry

rtm@christmas.UUCP (Richard Minner) (12/06/90)

In article <1990Nov30.172846.23669@zoo.toronto.edu> henry@zoo.toronto.edu (Henry Spencer) writes:
>Although the wording under longjmp() and under signal() could stand to be
>a bit more consistent, ...

You're too kind.  I'd say it borders on contradiction:
4.6.2.1 : "...the longjmp function shall execute correctly in contexts
of interrupts, signals and any of their related functions."

But of course not signals caused by interrupts.  Anyway, 4.7
is more restrictive so it wins.  Thanks for helping me understand
the situation, I won't bring it up again (and I'll be much more
wary of longjmp's in handlers).  You forgot to mention another
relevant part of the Standard, on pg.1 footnote:
	"The Standard ... is not a tutorial."

-- 
Richard Minner  || {uunet,sun,well}!island!rtm     (916) 736-1323 ||
                || Island Graphics Corporation     Sacramento, CA ||