[net.lang.c] What is the setjump call

nazgul@apollo.uucp (Kee Hinckley) (09/28/84)

<->
    Setjump stores the current state of the stack and any relevant
registers and hands them back to the program to hang onto.  At some
later date the program can then call longjmp and everything will
return to the state of the machine at the time of the setjump.
Essentially it is just a way of implementing a non-local goto.
For example:

    char[8] buf;        /* Also sometimes known as type: jmp_buf */
    foo()
    {

        /*
         * Save state (when we return it will be as
         * though we had just returned from the setjmp
         * function.
         */
        setjmp(buf);

        bar();
        ...
    }

    bar()
    {
        ...
        longjmp(buf);   /* Jump back into foo */
        /* Nothing after this get's executed */
    }


Note that the size of the buffer is going to be implementation dependent
(since different compilers are going to require different amounts of state
information) but generally it is on the order of a few bytes.  Since
(at least on the Apple in Aztec C) all you have to store are the stack
pointers, the return vector, and a few other pieces of state.  When
you restore all of these the rest of the stack gets thrown away and
you're right back where you started from.

One other thing, you can do a longjmp BACK to a setjmp, but doing one
forward probably won't work (eg. if you returned from "foo" in the above
example, and THEN tried to do a longjmp you would have to be very lucky
to have it work.

Actually, this is probably all a horrible explanation, does someone at
Brown have a graphical demo using Pecan or one of their other trees?


                                            -kee

moss@BRL-VLD.ARPA (10/01/84)

From:      "Gary S. Moss (AMXBR-VLD-V)" <moss@BRL-VLD.ARPA>

Do not use char buf[8] as the storage area for setjmp().  The portable
way is to use the typedef jmp_buf from <setjmp.h>.  This is defined in
Doug Gwyn's System V emulation as follows;

/*	@(#)setjmp.h	1.3	*/
#ifndef _JBLEN

#if vax || u3b5
#define _JBLEN	10
#endif

#if pdp11
#define _JBLEN	4	/* DAG -- one larger for overlays */
#endif

#if u370
#define _JBLEN	4
#endif

#if u3b
#define _JBLEN	11
#endif

typedef int jmp_buf[_JBLEN];

extern int setjmp();
extern void longjmp();

#endif

The size of the buffer differs with machine and version of UNIX and the
whim of whoever modifies the compiler, so USE THE TYPEDEF BY INCLUDING
SETJMP.H!!!

I got bit by this one because I defined it as int buf[3] rather than
including <setjmp.h>.  Since my buffer was one integer short, I stomped
on an integer variable when I called setjmp(), and Boy was that a tough
bug to track down.

-- Moss.
PS, I know this was not your question.

bwf@ihldt.UUCP (Bill Fecht) (10/01/84)

Careful, the setjmp() call saves only a few registers and stack pointers,
it does not save the stack.  Not understanding this could cause two problems:

	1)  thinking that a longjmp() will restore automatic variables, i.e.

		#include <setjmp.h>
		
		jmp_buf jb;
		
		main()
		{
			int x;
			x = 1;
			setjmp(jb);
			if (x == 2) {
				printf("bailout\n");
				exit();
			}
			x = 2;
			longjmp(jb);
		}

	2)  thinking that a longjmp() will restore calling sequence, i.e.

		jmp_buf jb; 
		foo1() { foo2(); bar(); }
		foo2() { foo3(); }
		foo3() { setjmp(jb); }

		bar() { longjmp(jb); }

Also, you should use 'jmp_buf jb', NOT  'char[8] jb' (:-)), 'jmp_buf' will
reserve all you need to do the setjmp()/longjmp().


I think setjmp() and longjmp() were included in Unix to recover from
convoluted nesting in the event of errors, then were included in C for
the user, I think.  I know uucp uses the mechanism for this a lot.

bill (ihack!bwf) fecht

thomson@uthub.UUCP (Brian Thomson) (10/01/84)

Kee Hinckley almost gets it right:
>     Setjump stores the current state of the stack and any relevant
> registers and hands them back to the program to hang onto.  At some
> later date the program can then call longjmp and everything will
> return to the state of the machine at the time of the setjump.

In fact, a correct setjmp/longjmp implementation doesn't just store
and restore register contents.  Rather, longjmp() should do a 'deep return',
restoring registers only if they have been salted away as part of
normal program execution.  In particular, the program

jmp_buf env;

main()
{
	register int i;

	i = 0;
	if(setjmp(&env)) {
		printf("%d\n", i);
		exit(0);
	}
	i = 1;
	longjmp(&env, 1);
}

should print 1, not 0.  Check the manual page -- ours (4.2) says
"All accessible data have values as of the time longjmp was called."
Note that, with this definition, the behaviour of a variable is independent
of whether it was declared "register".
-- 
		    Brian Thomson,	    CSRI Univ. of Toronto
		    {linus,ihnp4,uw-beaver,floyd,utzoo}!utcsrgv!uthub!thomson

guy@rlgvax.UUCP (Guy Harris) (10/06/84)

> 			longjmp(jb);
	.
	.
	.
> 		bar() { longjmp(jb); }

While we're on the subject of proper use of "longjmp", please note that
"longjmp" takes two arguments, not 1.  The second argument is the "return
value" that the "longjmp"ed-to "setjmp" should "return" when it's "longjmp"ed
to; if control is coming out of the "setjmp" from a regular call, it returns
0.

The "libPW" library on System V Release 2 (and, presumably, all earlier
versions) calls "longjmp" with only one argument; this works (I presume)
on System V on the VAX-11 by sheer accident, as there must have been a non-zero
value on the stack at the point where "longjmp" expected its argument.
It does *not* work on 4.2BSD, as there can happen to be a zero value on the
stack at that point.  Furthermore, there is no guarantee that it will work
on any other implementation of UNIX, or any non-UNIX C implementation.

Moral: run your code through "lint" whenever you can.  It will yell and scream
if you call a routine with the wrong number of arguments (unless you say
the routine can optionally take more than a given number, in which case it
will check only the required arguments.)  It looks like the S5R2 "lint" will
even check calls to "printf"!  (There's a new "lint" declaration "PRINTFLIKE"
which can be used for any routine that takes a "printf" string as an
formatting string and has a "printf"-like calling sequence.)

	Guy Harris
	{seismo,ihnp4,allegra}!rlgvax!guy

jim@ism780b.UUCP (10/10/84)

#R:apollo:-22099700:ism780b:25500028:000:815
ism780b!jim    Oct  7 16:58:00 1984

#include <setjmp.h>

jmp_buf jmpbuf;

main()
{
	/* register */ int i;

	i = 0;
	setjmp(jmpbuf);
	for( ;; i++ )
	    foo(i);

	printf("%d\n", i);
}
foo(i)
{
	if( i == 5 ) longjmp(jmpbuf, 1);
}

If you get 5 from the above, but 0 if you remove the /* */,
your setjmp/longjmp is BROKEN!!  It should always give 5.
Note that the manual says that all data have values as of the time
*longjmp* was called, not as of the time the setjmp was called
(which is unimplementable; why do you get 5 instead of 0 in the above?).
To do this, longjmp must unwind the stack, restoring registers
as it goes.  Note that this only requires jmp_buf to have 3 words, instead
of the brain-damaged 10 in the 4.1 implementation, where the registers
are saved at setjmp time, which is all wrong.

-- Jim Balter, INTERACTIVE Systems (ima!jim)

jim@ism780b.UUCP (10/10/84)

#R:apollo:-22099700:ism780b:25500029:000:142
ism780b!jim    Oct  8 01:46:00 1984

Whoops!  That should have been

	if( setjmp(jmpbuf) == 0 )
	    for( ;; i++ )
		foo(i);

Sorry.

-- Jim Balter, INTERACTIVE Systems (ima!jim)

ka@hou3c.UUCP (Kenneth Almquist) (10/14/84)

> The "libPW" library on System V Release 2 (and, presumably, all earlier
> versions) calls "longjmp" with only one argument; this works (I presume)
> on System V on the VAX-11 by sheer accident, as there must have been a
> non-zero value on the stack at the point where "longjmp" expected its
> argument.

Actually, the fact that longjmp works with one argument is not an
accident.  The SVR2 manual page for setjmp states, "If longjmp is
invoked with a second argument of 0, setjmp will return 1."  This
was done to make the two argument version of setjmp backward com-
patable with the one argument version.  The BSD version of setjmp
does not do this, so you have to convert all longjmp calls to use
two arguments when porting code to Berkely UN*X.
				Kenneth Almquist

henry@utzoo.UUCP (Henry Spencer) (10/15/84)

> Actually, the fact that longjmp works with one argument is not an
> accident.  The SVR2 manual page for setjmp states, "If longjmp is
> invoked with a second argument of 0, setjmp will return 1."  This
> was done to make the two argument version of setjmp backward com-
> patable with the one argument version.

It's still an accident.  Calling a function (e.g. longjmp) with fewer
parameters than expected is **NOT** guaranteed to make the missing
parameters look like zeros.  This may work on some machines, but it
is not portable.
-- 
				Henry Spencer @ U of Toronto Zoology
				{allegra,ihnp4,linus,decvax}!utzoo!henry

guy@rlgvax.UUCP (Guy Harris) (10/19/84)

> > Actually, the fact that longjmp works with one argument is not an
> > accident.  The SVR2 manual page for setjmp states, "If longjmp is
> > invoked with a second argument of 0, setjmp will return 1."  This
> > was done to make the two argument version of setjmp backward com-
> > patable with the one argument version.
> 
> It's still an accident.  Calling a function (e.g. longjmp) with fewer
> parameters than expected is **NOT** guaranteed to make the missing
> parameters look like zeros.  This may work on some machines, but it
> is not portable.

Well, one could argue that if all one cares about is whether "setjmp"
returns a 0 (normal return) or non-zero (nonlocal goto) value, then calling
"longjmp" with only one argument will work under all circumstances (either
the non-existent argument gets a zero value by accident, in which case it
gets mapped into 1, or it gets a non-zero value by accident, in which case
it is non-zero).  However, it's still not guaranteed to work everywhere.
(Admittedly, there are a few functions in the C library that take a
variable number of arguments, like "printf" and the "open" system call in
USDL UNIX and in 4.2BSD; however, whether the extra arguments are present
or not can be intuited from the values of the required arguments, and the
code can avoid touching the extra arguments if they are missing.  This is
emphatically not the case for "longjmp".  There may be implementations of
C in which not touching the missing arguments may not be enough; these
implementations may special-case "printf", but not "setjmp" or "longjmp" -
or "open", for that matter.)

	Guy Harris
	{seismo,ihnp4,allegra}!rlgvax!guy