[comp.lang.c] Behaviour of setjmp/longjmp and registers

david@torsqnt.UUCP (David Haynes) (01/20/89)

What should be the result of running the following program?

#include <setjmp.h>

main()
{
	register int j;
	jmp_buf env;

	j = 1;
	if(setjmp(env) == 1) {
		printf("j = %d\n", j);
		exit(1);
	}
	printf("j = %d\n", j);
	j += 3;
	longjmp(env, 1);
}

Sequent, Ultrix and Vax C give results of j = 1, j = 4.
Gcc gives a result of j = 1, j = 1.
What does the ANSI standard say about this?

-david-

henry@utzoo.uucp (Henry Spencer) (01/21/89)

In article <25@torsqnt.UUCP> david@torsqnt.UUCP (David Haynes) writes:
>What does the ANSI standard say about this?

If function foo() calls setjmp(), and then it or a function called by
it calls longjmp(), the values of any local variables which are not
volatile and which have changed since the setjmp() are *indeterminate*.
Your program could give j = 1, j = 666 if the implementor felt like it.
Note, this behavior is *not* limited to variables declared "register".

In practice, it is highly likely that any self-respecting implementation
will restrict this nasty behavior to variables declared "register", or
even eliminate it entirely.  (Some of us thought that it was both feasible
and important to require one of these two approaches, in fact, but X3J11
did not agree.)  It is also highly likely that the value will not in
fact be random, but will be either the value as of setjmp() time or the
value as of longjmp() time.  However, strictly conforming programs cannot
rely on any of this.
-- 
Allegedly heard aboard Mir: "A |     Henry Spencer at U of Toronto Zoology
toast to comrade Van Allen!!"  | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

guy@auspex.UUCP (Guy Harris) (01/21/89)

>What does the ANSI standard say about this?

The May 13, 1988 draft says that since "j" is of automatic storage
duration, isn't declared "volatile", and was changed between the calls
to "setjmp" and "longjmp", its value will be indeterminate after the
call to "longjmp".  Either behavior is conformant, as would be setting
"j" to 666.... 

chris@mimsy.UUCP (Chris Torek) (01/21/89)

In article <25@torsqnt.UUCP> david@torsqnt.UUCP (David Haynes) writes:
>What should be the result of running the following program?

[example of register variables and longjmp deleted]

>Sequent, Ultrix and Vax C give results of j = 1, j = 4.
>Gcc gives a result of j = 1, j = 1.
>What does the ANSI standard say about this?

The ANSI standard says that register variables, like all other
automatic variables, must be declared `volatile' for you to be able to
assume the output will be `j = 1, j = 4'.  Of course, since there is no
`volatile' qualifier in most existing C compilers, in practise this
means that you must not declare variables as register in order to count
on them (so that `#define volatile /*empty*/' will work).

(I guess you were not reading when I posted this the last two or three
times last month. . . .)
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

moore%cdr.utah.edu@wasatch.UUCP (Tim Moore) (01/21/89)

In article <25@torsqnt.UUCP> david@torsqnt.UUCP (David Haynes) writes:

	#include <setjmp.h>
	
	main()
	{
		register int j;
		jmp_buf env;
	
		j = 1;
		if(setjmp(env) == 1) {
			printf("j = %d\n", j);
			exit(1);
		}
		printf("j = %d\n", j);
		j += 3;
		longjmp(env, 1);
	}
	
	Sequent, Ultrix and Vax C give results of j = 1, j = 4.
	Gcc gives a result of j = 1, j = 1.
	What does the ANSI standard say about this?
	
	-david-

Gcc follows the ANSI standard which, when strictly interpreted, says
that "the only automatic variables guaranteed to remain valid are
those declared volatile" (quoted from the gcc manual). Traditional
compilers put local variables in the stack in functions that call
setjump. If you look at the assembly code output of  Sequent,
Ultrix, Vax C, and gcc with the "-traditional" flag you will see that
j isn't really being stored in a register.
			-Tim Moore
	4560 M.E.B.		   internet:moore@cs.utah.edu
	University of Utah	   ABUSENET:{ut-sally,hplabs}!utah-cs!moore
	Salt Lake City, UT 84112

scs@adam.pika.mit.edu (Steve Summit) (01/21/89)

Here are my rules for using setjmp/longjmp and remembering which
variables are preserved:

     1.	Never use setjmp and longjmp.

     2.	If rule 1 must be broken, do not depend on the values of
	any variables after setjmp has "returned" due to a call
	to longjmp.

These rules are admittedly conservative, but as long as I follow
them I never have problems with setjmp or longjmp or which
variables are preserved.  I believe I have broken rule 1 twice; I
have had problems with variable preservation approximately 50% of
those times.  Therefore I conclude that rule 1 is a good rule.

                                            Steve Summit
                                            scs@adam.pika.mit.edu

Ten people will now sanctimoniously flame me, saying that the
problem is not with setjmp/longjmp but with my (lack of)
understanding of them.  Save it.  I could remember the details
and rationale of the variable preservation rules if I wanted to.
Since I consider longjmp an abysmal idea in the first place, and
I have been able to get along without it virtually all of the
time, I don't choose to keep my knowledge on it current.  It is
also unnecessary to point out to me all of the cases where
longjmp has been found indispensable; I'm familiar with the
arguments.

cquenel@polyslo.CalPoly.EDU (96 more school days) (01/22/89)

Since we're on the subject again, I have a question coming from
the IMPLEMENTATION department.

Assuming that volatile variables are written out to memory
before any procedure call.  (Even if this is not sufficient,
assume that volatile variables have been dealt with.)

Should it ever be necessary/desirable to restore any registers
on a long-jump BESIDES the frame-pointer and/or stack-pointer ?

That is to say:
	If I can get all my volatile variables out of my
	registers, then I don't need to restore ANY of them ?
	(According to ANSI)

I mention this because I remember implementing it once,
and at the time, I seem to recall the "common", vax-ish, old-fashioned
implementation restored a gob of regular registers as well.

--chris


What you don't know can hurt you, only you won't know it.
-------------------------------------------------------------------------------
| Nothing the God of Bio-Mechanics wouldn't let you in heaven for ?           |
-------------------------------------------------------------------------------
| Chris Quenelle            | Smart Mailers -> cquenel@polyslo.CalPoly.EDU    |
| Cal Poly State Univ.      |-------------------------------------------------|
| San Luis Obispo, CA 93407 | On a clear disk you can seek forever.           |
-------------------------------------------------------------------------------

ark@alice.UUCP (Andrew Koenig) (01/23/89)

In article <7222@polyslo.CalPoly.EDU>, cquenel@polyslo.CalPoly.EDU (96 more school days) writes:

> Should it ever be necessary/desirable to restore any registers
> on a long-jump BESIDES the frame-pointer and/or stack-pointer ?

No, unfortunately.

Once upon a time, the VAX setjmp worked by saving all the
registers in the jmp_buf and longjmp restored it.  That means
that if you say:

	register foo;
	jmp_buf jb;

	foo = 3;
	if (setjmp(jb)) {
		/* exception stuff */
	}
	foo = 7;
	func();

and func calls longjmp(jb), foo will be 3 and not 7 when the
`exception stuff' is executed.

John Reiser figured out a way around it -- a nifty version of
longjmp that unwound the stack, restoring the correct registers
at each iteration -- and that went into at least some VAX C
implementations.

However, when it came to the ANSI committee, they decided it
would be too hard to mandate this kind of implementation.
So they took the least restrictive route possible -- longjmp
doesn't have to work at all unless you beg it.
-- 
				--Andrew Koenig
				  ark@europa.att.com

gwyn@smoke.BRL.MIL (Doug Gwyn ) (01/23/89)

In article <8812@alice.UUCP> ark@alice.UUCP (Andrew Koenig) writes:
>Once upon a time, the VAX setjmp worked by ...

I seem to recall at least three distinct implementations of setjmp/logjmp
in PDP-11 UNIX and something like four implementations in various versions
of VAX UNIX.  Reiser's was pretty good, but it would have been foolish to
rely on it for any sort of portable code, since other implementations
didn't necessarily work the same way.

>However, when it came to the ANSI committee, they decided it
>would be too hard to mandate this kind of implementation.
>So they took the least restrictive route possible -- longjmp
>doesn't have to work at all unless you beg it.

longjmp() has to do the essential "goto" action and return the
right value for the setjmp() invocation (which is constrained
to be in one of the contexts for which this can reasonably be
guaranteed).  After a longjmp, all accessible objects are
required to have the values they had when longjmp() (NOT
setjmp()) was invoked, except that cached autos inside the
function containing the setjmp are allowed to be out of sync.
That's to keep aggressive optimizers from having to treat
setjmp() invocations in a "magic" way.

Really, setjmp/longjmp is just a groady "goto" and should be
avoided for the same reasons as the ordinary "goto".  In fact
it is even more violent than an ordinary "goto", which is why
it has to be more loosely constrained.  I don't think I've
even seen a case in which setjmp/longjmp was the best way to
design code, let alone essential.  The nearest to a valid use
I know of is to allow a SIGINT to abort processing and fly
back to a main command loop (e.g. in the "ed" editor), but
even there the longjmp can cause stdio data structures to be
left in an inconsistent state.  Asynchronous concurrent
processes really need to be designed around better tools than
setjmp/longjmp.

henry@utzoo.uucp (Henry Spencer) (01/23/89)

In article <7222@polyslo.CalPoly.EDU> cquenel@polyslo.CalPoly.EDU (96 more school days) writes:
>Assuming that volatile variables are written out to memory
>before any procedure call.  (Even if this is not sufficient,
>assume that volatile variables have been dealt with.)
>
>Should it ever be necessary/desirable to restore any registers
>on a long-jump BESIDES the frame-pointer and/or stack-pointer ?

Assuming that any other registers needed by the compiler -- not all
implementations get by with just an FP and SP -- have been dealt with,
ANSI C conformance doesn't require anything more.

However, an implementation that simply trashes the registers (rather,
allows them to be trash) or, still worse, does likewise for automatic
variables not declared "register" (i.e. by promoting them to registers
and then not preserving them), is probably not going to sell well when
word gets around.  The old de-facto rule was that register variables
had either current values or values as of the setjmp call, and that
other automatics weren't touched at all.  Minimal ANSI conformance
will thus break essentially every longjmp-using program in existence.
Don't expect the customers to be too delighted about that.  Your best
quick solution is to save all registers at setjmp time and then restore
them at longjmp time, and avoid promoting automatic variables.  The
preferred solution, more work, is to notice the use of setjmp (there
are enough restrictions on it that you can do that) and save/restore
everything around all function calls within a function that uses
setjmp.
-- 
Allegedly heard aboard Mir: "A |     Henry Spencer at U of Toronto Zoology
toast to comrade Van Allen!!"  | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

jas@ernie.Berkeley.EDU (Jim Shankland) (01/23/89)

In article <9480@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn) writes:
>Really, setjmp/longjmp is just a groady "goto" and should be
>avoided for the same reasons as the ordinary "goto".  In fact
>it is even more violent than an ordinary "goto", which is why
>it has to be more loosely constrained.  I don't think I've
>even seen a case in which setjmp/longjmp was the best way to
>design code, let alone essential.

You can build a pretty nice exception mechanism on top of setjmp/longjmp.
Having some functions (memory allocation is a good example) raise
exceptions rather than returning an error status can unclutter your
code and give your weary typing fingers a rest.  (Not as much as not
checking error statuses at all, but we all know better than that --
don't we?)

Jim Shankland
jas@ernie.berkeley.edu

"I've been walking in a river all my life, and now my feet are wet"

cquenel@polyslo.CalPoly.EDU (101 more school days) (01/23/89)

I write:
> Should it ever be necessary/desirable to restore any registers
> on a long-jump BESIDES the frame-pointer and/or stack-pointer ?

Andrew Koenig explains:
[at one time VAX implementations would restore "register" variables]
[this was arguably useful.]

I see this as a BAD thing to try to guarantee.
I agree with the way ANSI did it.
To the best of my understanding the "register"
storage class has always been a "hint" to the compiler.
Since many modern compilers will fairly often out-right ignore
these hints, it makes the use of this feature EXTREMELY unportable at
best.  Consider a very straight-forward example:
I use this restoral feature to restore a register variable
through a longjump.  How many variables can I restore this way ?
How many registers variables can I effectively use ?
Depending on a "register" variable to actually BE in a register 
is (as far as I'm concerned) a BAD thing.

I hope there weren't too many people who were in favor of this 
in the ANSI committees.

Since setjmp/longjmp is such a work-around/violate-all-the-rules/
just-get-me-the-f*ck-back-to-where-I-started/ sorta feature,
(That is to say, a LAST RESORT, to be used almost exclusively
in system dependant routines.) I would say that it SHOULDN'T
actually restore anything.  If you need anything to be restored,
then save it yourself when you do a setjump in a volatile variable.

I think this newer definition of setjmp/longjmp is much cleaner
and provides only what is necessary and most useful.  As a compiler
writer, it certainly makes sense to me to only implement what can
be guaranteed.  That is, to provide well-defined services that
can be relied on, and don't provide anything that might/might-not work.

Comments anyone ?

--chris


If you understand what you're doing, you're not learning anything.
-------------------------------------------------------------------------------
| Nothing the God of Bio-Mechanics wouldn't let you in heaven for ?           |
-------------------------------------------------------------------------------
| Chris Quenelle            | Smart Mailers -> cquenel@polyslo.CalPoly.EDU    |
| Computer Systems Lab      | Dumb Mailers  -> !ucbvax!voder!polyslo!cquenel  |
| Cal Poly State Univ.      |-------------------------------------------------|
| San Luis Obispo, CA 93407 | On a clear disk you can seek forever.           |
-------------------------------------------------------------------------------

geoff@warwick.UUCP (Geoff Rimmer) (01/23/89)

In article <25@torsqnt.UUCP> david@torsqnt.UUCP (David Haynes) writes:
>What should be the result of running the following program?
>
>#include <setjmp.h>
>
>main()
>{
>	register int j;
>	jmp_buf env;
>
>	j = 1;
>	if(setjmp(env) == 1) {
>		printf("j = %d\n", j);
>		exit(1);
>	}
>	printf("j = %d\n", j);
>	j += 3;
>	longjmp(env, 1);
>}
>
>Sequent, Ultrix and Vax C give results of j = 1, j = 4.
>Gcc gives a result of j = 1, j = 1.
>What does the ANSI standard say about this?

I don't know what the ANSI standard says, but our manual page for
setjmp() mentions in passing that

	register variables have unpredictable values code after the
	return from longjmp 

So, it's probably just luck that the Sequent, Ultrix and Vax C
compiler give what you'd expect from a non-register variable: 
j = 1, j = 4 

Geoff

	------------------------------------------------------------
	Geoff Rimmer, Computer Science, Warwick University, England.
			geoff@uk.ac.warwick.emerald

	"Oo, have we got a video?"
	"If anyone else asks me that question, I'm going to put
	head through the window."
		- The Young Ones, 1984 (?)
	------------------------------------------------------------

dave@micropen (David F. Carlson) (01/24/89)

In article <25@torsqnt.UUCP>, david@torsqnt.UUCP (David Haynes) writes:
> What should be the result of running the following program?

> 	register int j;
> 	jmp_buf env;
> 
> Sequent, Ultrix and Vax C give results of j = 1, j = 4.
> Gcc gives a result of j = 1, j = 1.
> What does the ANSI standard say about this?
> 

According to SVr3.0 setjmp(3):

BUGS: The values of the registers on the second return from setjmp
are the register values at the time of the first call to setjmp, not
those at the time longjmp.

This doesn't answer the ANSI question per se, but it does point out that
using register variables around setjmp/longjmp is a fool's game.  Play
it safe, use a condom.


-- 
David F. Carlson, Micropen, Inc.
micropen!dave@ee.rochester.edu

"The faster I go, the behinder I get." --Lewis Carroll

henry@utzoo.uucp (Henry Spencer) (01/24/89)

In article <8812@alice.UUCP> ark@alice.UUCP (Andrew Koenig) writes:
>...John Reiser figured out a way around it -- a nifty version of
>longjmp that unwound the stack, restoring the correct registers
>at each iteration -- and that went into at least some VAX C
>implementations.
>
>However, when it came to the ANSI committee, they decided it
>would be too hard to mandate this kind of implementation...

Don't forget that it was (relatively) easy on the VAX because one of the
things that the all-singing-all-dancing VAX procedure call does is to save
a register mask.  This makes the VAX stack pretty much self-describing,
at the cost of slowing down every function call and increasing the use
of stack space.  Most other implementations consider that too high a
price to pay.
-- 
Allegedly heard aboard Mir: "A |     Henry Spencer at U of Toronto Zoology
toast to comrade Van Allen!!"  | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

crossgl@ingr.com (Gordon Cross) (01/24/89)

In article <9480@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>
>I don't think I've even seen a case in which setjmp/longjmp was the best
>way to design code, let alone essential.

I tend to disagree with this.  I do avoid the use of setjmp/longjmp where they
are not needed but try to correctly handle error recovery in a large
interactive program without them!  I have developed a caller-configurable
error processor that takes care of such monsters nicely:  an error is reported,
handled, and recovered from simply by using something like

     if (read (descriptor, buffer, sizeof (buffer)) == -1)
       report_error (READFAIL, ERR_FATAL);

     do_normal_stuff ();   /* report_error does NOT return on FATAL!! */
-- 

Gordon Cross             UUCP:      uunet!ingr!crossgl     "all opinions are
111 Westminister Way     INTERNET:  crossgl@ingr.com        mine and not those
Madison, AL 35758        MA BELL:   (205) 772-7842          of my employer."

cquenel@polyslo.CalPoly.EDU (96 more school days) (01/24/89)

henry@utzoo.uucp writes :
>However, an implementation that simply trashes the registers (rather,
>allows them to be trash) or, still worse, does likewise for automatic
>variables not declared "register" (i.e. by promoting them to registers
>and then not preserving them), is probably not going to sell well when
>word gets around.

	Promoting register variables to automatic is not
	strictly necessary to not restore it on a longjmp.
	(if you can follow that sentance structure).

	Automatic variables need not be restored according to ANSI.

	Older machines had the effect of restoring variables that
	were left on the stack, but my argument is this :

	That there is actually not that much code out there (that
	purports to be portable :-) ) that DEPENDS on this 
	behaviour.  I looked at the bourne shell, and it uses
	setjmp/longjmp only for goto-purposes.

	Given the current breed of compiler writers, I would expect
	a fair number of compilers NOT to attempt to save miscellaneous
	registers.

	Consider a register file that is divided into 
	SAVED and NOTSAVED registers which (by convention)
	are/are-not saved on a procedure call. Do we save
	the NOTSAVED class registers on a setjmp ?

	What about a machine like the Pyramid with sliding register
	windows.  It has 4 classes of registers.
	Global Registers    : not touched on a procedure call.
	Parameter Registers : Contain parameters on entry to routine
	Local Registers     : For normal automatic variables
	Temporary Registers : For transient temporaries in expressions



>The old de-facto rule was that register variables
>had either current values or values as of the setjmp call, and that
>other automatics weren't touched at all.

If this "existing practice" could be supported with a reasonable
expense, I would say go for it, but the following things argue against it:

-- With many new architectures, it's not easy to do.
   (It doesn't come for free when restoring the frame-pointer)

-- Compiler technology has improved to the point where the
   concept of what is "in registers" and what isn't is not
   only hard to guarantee, but in some of the more different 
   architectures, might not even make sense.

   (As I've said in a previous posting) 
-- The real purpose of setjmp/longjmp is as a global goto
   when you have to go around the stack.  It shouldn't
   need any "features" that make it easier to use.o

-- When a signal handler (or other equivalently nasty program)
   needs to save state, then it can store (!) just the variables
   it needs in volatile variables that it keeps associated with 
   the setjmp.

-- No semi-legitimate uses of setjmp/longjmp that come to mind
   actually would require the restoral of variables to function.

>will thus break essentially every longjmp-using program in existence.

	Do you have some evidence here ?

--chris

The life of a signal-handler is nasty, brutish and short.

chris@mimsy.UUCP (Chris Torek) (01/24/89)

In article <7249@polyslo.CalPoly.EDU> cquenel@polyslo.CalPoly.EDU
(101 more school days) writes:
>I see [making registers work as well as automatics around setjmp/longjmp]
>as a BAD thing to try to guarantee.

and bases this argument on the idea that `register' is a compiler hint
and need not actually use a machine register.

>As a compiler writer, it certainly makes sense to me to only implement
>what can be guaranteed. ...

But the contents of *all* automatic variables (register or not) *can* be
guaranteed without too much trouble, and without requiring `volatile'
qualifiers.

If you will accept as the definition of a `very good' compiler `one that
does interprocedural analysis and register allocation', then a good compiler
must (since it may allocate registers to variables that are not automatic)
make guarantees that will stand up for automatic variables even in the
presence of longjmp, and the problem vanishes entirely.  So a very good
compiler will make such guarantees.  Other compilers could simply turn
off optimisation in functions containing calls to setjmp().

>I think this newer definition of setjmp/longjmp is much cleaner
>and provides only what is necessary and most useful.

This is a separate (and much more sensible) line of argument with
which I happen not to agree: opinion as to elegance.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

haahr@phoenix.Princeton.EDU (Paul Gluckauf Haahr) (01/25/89)

henry@utzoo.uucp (Henry Spencer) writes:
[ quoting andrew koenig (alice!ark) on john reiser's implementation
of longjmp() that unwound the stack to restore all register variables
that had been saved in previous stack frames ]
> >However, when it came to the ANSI committee, they decided it
> >would be too hard to mandate this kind of implementation...
> 
> Don't forget that it was (relatively) easy on the VAX because one of the
> things that the all-singing-all-dancing VAX procedure call does is to save
> a register mask.  This makes the VAX stack pretty much self-describing,
> at the cost of slowing down every function call and increasing the use
> of stack space.  Most other implementations consider that too high a
> price to pay.

a setjmp/longjmp implementation that restores all variables (including
those in registers) falls out of a caller saves function call protocol.
no muss, no fuss, just restore the stack pointer and pc.  this will
work in the presence of inter-procedural optimization, if setjmp() is
marked as a function that trashes all registers.

this is one of the several reasons i consider caller-saves a better
approach to function call protocol.

paul haahr
princeton!haahr or haahr@princeton.edu

gregg@ihlpb.ATT.COM (Wonderly) (01/25/89)

From article <5771@phoenix.Princeton.EDU>, by haahr@phoenix.Princeton.EDU (Paul Gluckauf Haahr):
> a setjmp/longjmp implementation that restores all variables (including
> those in registers) falls out of a caller saves function call protocol.
> no muss, no fuss, just restore the stack pointer and pc.  this will
> work in the presence of inter-procedural optimization, if setjmp() is
> marked as a function that trashes all registers.
> 
> this is one of the several reasons i consider caller-saves a better
> approach to function call protocol.

Except that this kills any efficency that one might try to gain through
the use of normal scratch registers at the lowest level of function call.

What is the expense of 

	for (i=0; i < cnt; ++i)
		cnt[i] = strlen (str[i]);

when caller saves verses the case where called saves?  If caller uses a
lot of registers, it will continually save/restore them if caller saves
whereas when called saves, it can decide when all of that work should
really be done.

It strikes me that setjmp() could save all GP registers.  I know of at
least one implementation were a jmp_buf is enough space for the entire
register set.  longjmp() of course just reloads the registers grabs the
return value passed, and jumps to the return address given to it (on the
stack or elsewhere).  The expense is a consideration but the results are
guaranteed in the cases I can think of.

Unwinding the stack doesn't seem necessary and in fact seems counter
productive.  Of course there are those that would argue that
setjmp()/longjmp() must be efficient because they want to call setjmp in
a tight loop some place!

-- 
Gregg Wonderly                             DOMAIN: gregg@ihlpb.att.com
AT&T Bell Laboratories                     UUCP:   att!ihlpb!gregg

chris@mimsy.UUCP (Chris Torek) (01/25/89)

In article <7283@polyslo.CalPoly.EDU> cquenel@polyslo.CalPoly.EDU
(96 more school days) writes:
>-- The real purpose of setjmp/longjmp is as a global goto
>   when you have to go around the stack.  It shouldn't
>   need any "features" that make it easier to use. ...

>-- No semi-legitimate uses of setjmp/longjmp that come to mind
>   actually would require the restoral of variables to function.

These are the key points (and, incidentally, tie in to the `B&D'
discussion in comp.lang.misc).  For the sake of argument (which is
to say that I do not necessarily believe this myself), consider the
following use of setjmp/longjmp:

	/* return type of signal functions, either int or void */
	typedef void sigreturn_t;

	static struct jbstack {
		jmp_buf cur;
		jmp_buf *prev;
	} *jbstack;

	sigreturn_t stop(int sig) {
		longjmp(jbstack->cur, 1);
	}

	struct foo *timed_fiddle_with(struct foo *base) {
		int fd;
		sigreturn_t (*oldalarm)();
		struct jbstack j;

		j.prev = jbstack;
		jbstack = &j;
		if (setjmp(&j.cur)) {
			/* timeout */
			jbstack = j.prev;
			(void) signal(SIGALRM, oldalarm);
			free((void *)base);
			return NULL;
		}
		(void) alarm(base->timeout);
		oldalarm = signal(SIGALARM, stop);
		fd = open(base->name, base->mode);
		(void) alarm(0);
		base->fd = fd;
		jbstack = j.prev;
		return base;
	}

(longjmp is required wherever signals do not interrupt system calls.)

This is really the beginning of a general exception mechanism with
protection against stack unwinding, so that routines can clean up
after themselves if interrupted (even if the interruption leaps
from a low-level routine all the way back to main()).

It can be argued that implementing such an exception mechanism using
longjmp is a reasonable thing.  Indeed, a number of languages provide
more direct methods (e.g., Mesa and various Lisps); C provides
sufficient primitives, leaving the discipline to use them to the
programmer.

Now, clearly one can sprinkle `volatile' qualifiers into the
declarations in timed_fiddle_with(), so that it will work under pANS
C.  But (at least some of) the variables so declared are not in fact
volatile---the value of `base', in particular, *never changes!*  The
qualifier is being (mis)used strictly for its side effect of inhibiting
optimisation, not because the variable that is so qualified behaves in
a way that is not described by the virtual machine.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

tim@crackle.amd.com (Tim Olson) (01/26/89)

In article <7283@polyslo.CalPoly.EDU> cquenel@polyslo.CalPoly.EDU (96 more school days) writes:
| henry@utzoo.uucp writes :
| >However, an implementation that simply trashes the registers (rather,
| >allows them to be trash) or, still worse, does likewise for automatic
| >variables not declared "register" (i.e. by promoting them to registers
| >and then not preserving them), is probably not going to sell well when
| >word gets around.
| 
| 	What about a machine like the Pyramid with sliding register
| 	windows.  It has 4 classes of registers.
| 	Global Registers    : not touched on a procedure call.
| 	Parameter Registers : Contain parameters on entry to routine
| 	Local Registers     : For normal automatic variables
| 	Temporary Registers : For transient temporaries in expressions

Register-windowed machines are usually very easy to write
setjmp()/longjmp() for, because the registers are treated as a
"stack-cache".  Thus, all you have to save is the current stack-cache
pointer in the jmpbuf, and restore it upon a longjmp().  This makes
automatic variables act just as statics do: their value is that as of
the time of the longjmp().  This is much cleaner than the botch of
saving the register contents in the jmpbuf and restoring them.

Of course, you still have to worry about optimizations such as register
coalescing, etc.



	-- Tim Olson
	Advanced Micro Devices
	(tim@crackle.amd.com)

ark@alice.UUCP (Andrew Koenig) (01/26/89)

In article <9467@ihlpb.ATT.COM>, gregg@ihlpb.ATT.COM (Wonderly) writes:

> It strikes me that setjmp() could save all GP registers.  I know of at
> least one implementation were a jmp_buf is enough space for the entire
> register set.  longjmp() of course just reloads the registers grabs the
> return value passed, and jumps to the return address given to it (on the
> stack or elsewhere).  The expense is a consideration but the results are
> guaranteed in the cases I can think of.

Yup, that's what the folks thought who did the first VAX
implementation, too.

It doesn't work.  Consider this case:

	register x;
	jmp_buf j;

	x = 3;
	if (setjmp(j))
		{ stuff }
	x = 4;
	longjmp(j,1);

You will see that when `stuff' is executed, x will have been
restored to 3.
-- 
				--Andrew Koenig
				  ark@europa.att.com

cquenel@polyslo.CalPoly.EDU (94 more school days) (01/26/89)

Sorry for the length guys, but I think this will clear a lot up.
Present are responses follow to the following people:

henry@utzoo.uucp writes :
ark@alice.UUCP (Andrew Koenig) writes:
chris@mimsy.UUCP (Chris Torek) writes:
geoff@warwick.UUCP (Geoff Rimmer) writes:
haahr@phoenix.Princeton.EDU (Paul Gluckauf Haahr) writes :
tim@crackle.amd.com (Tim Olson) writes:

In the last few articles I'm afraid I've been misunderstood a 
little bit.  I'd like to try again.

I think some people have there has been some confusion over
two behaviours associated with variables at longjmp time.

I will use the word CONSERVED when a variable is unaffected by
a long jump.  I will use the word RESTORED when a variable
is given the value that it had at setjmp-time (either "for free"
or by copying it's old value back into it).

Under older systems, automatic variables were CONSERVED, and
register variables were RESTORED.

To illustrate the difference consider the following pseudo-code:

	routine()
	{
		/* register */ int i;
		i = BEFORE_VALUE;
		call to set_jmp();
		i = AFTER_VALUE;
		call to routine_which_calls_long_jmp();
	}

If i is declared REGISTER, then "normally" it would be RESTORED.
It's value would be BEFORE_VALUE.
If i is NOT declared register, then normally it would be CONSERVED.
It's value would be AFTER_VALUE.

ANSI guarantees that variables declared volatile will be CONSERVED.  
It does not say that ANY variables have to be restored.

On stack-based machines with conventional registers and calling
sequences you get automatic variable CONSERVATION for free.
(You have to write out all your automatic variables
 that are alive in registers.)

On those conventional systems, you also get RESTORED automatics
under the following circumstances:  If you declare it to be
of register storage class, AND the compiler ACTUALLY puts it in
a register.  While I don't know of any compiler that GUARANTEES
that ALL "register" declarations result in actual registers being
used, I'm sure some people would assume that it is a safe bet that
ONE or TWO register declarations will be honored. Although this
is non-portable strictly speaking, it probably works most of the time.

I see no problem with a compiler going beyond the ANSI spec and
guaranteeing that ALL automatic variables will be CONSERVED.
I think this is a very reasonable thing.  And it can be achieved
without added setjmp-time baggage, on a fairly conventional machine.
No general registers need to stored for this to happen !

My argument is that no code would rely on registers being RESTORED.

Returning from a longjmp and having all your local registers trashed
would be a hassle to deal with, I admit.  But the alternative is
to have them be the values at the time the setjmping procedure
called the longjmping procedure.

From the Pyramid man page on setjmp/longjmp:

                                    ...	 All memory-bound data
     have values as of the time	longjmp	was called.  The machine
     registers are restored to the values they had at the time
     that setjmp was called.  But, because the register	storage
     class is only a hint to the C compiler, variables declared
     as	register variables may not necessarily be assigned to
     machine registers,	so their values	are unpredictable after	a
     longjmp.  This is especially a problem for	programmers try-
     ing to write machine-independent C	routines.  ...

     [There follows an example with a careful note
      NOT to modify register variables after calling
      setjmp.  This is because their values will be
      indeterminate on a Pyramid.  It would not be too
      much trouble to conserve them, just ignore the
      register declaration in a routine that calls
      setjmp.]


henry@utzoo.uucp writes :
>However, an implementation that simply trashes the registers (rather,
>allows them to be trash) or, still worse, does likewise for automatic
>variables not declared "register" (i.e. by promoting them to registers
>and then not preserving them), is probably not going to sell well when
>word gets around.

You are correct, perhaps ANSI could have guaranteed that
automatics would be CONSERVED.  My point was that none should be
RESTORED.


ark@alice.UUCP (Andrew Koenig) writes:
>...John Reiser figured out a way around it -- a nifty version of
>longjmp that unwound the stack, restoring the correct registers
>at each iteration -- and that went into at least some VAX C
>implementations.

This allows register variables to be truly RESTORED without
the space and time overhead of saving all the registers in
the jmp_buf.  However, the calling convention that supported it
was rather luxurious (read : expensive) in its own right.

chris@mimsy.UUCP (Chris Torek) writes:
>But the contents of *all* automatic variables (register or not) *can* be
>guaranteed without too much trouble, and without requiring `volatile'
>qualifiers.

Granted, they can be guaranteed CONSERVED.
They are not RESTORED.

I wrote previously:
>I think this newer definition of setjmp/longjmp is much cleaner
>and provides only what is necessary and most useful.

chris@mimsy.UUCP (Chris Torek) writes:
>This is a separate (and much more sensible) line of argument with
>which I happen not to agree: opinion as to elegance.

I think that CONSERVING all automatic variables but RESTORING
none of them is more elegant than CONSERVING all non-register
automatic variables and sort-of-restoring or maybe-restoring
register variables.

I say that CONSERVING them all and RESTORING non of them is best.

geoff@warwick.UUCP (Geoff Rimmer) writes:
>I don't know what the ANSI standard says, but our manual page for
>setjmp() mentions in passing that
>
>	register variables have unpredictable values code after the
>	return from longjmp 
>
>So, it's probably just luck that the Sequent, Ultrix and Vax C
>compiler give what you'd expect from a non-register variable: 

It seems like restoring register variables to an unknown
state is a pretty common thing to do.  My argument is that
all these venders instead of saying "register variables
are in an unknown state" should NOT RESTORE THEM, and should write
them to the stack, and should say that they are CONSERVED.

It ocurrs to me that this would require the following extra work
on the part of their compilers: reserving space for register
variables on the stack and dumping even [registers that could
be guaranteed across procedure calls] to the stack.

haahr@phoenix.Princeton.EDU (Paul Gluckauf Haahr) writes :
>a setjmp/longjmp implementation that restores all variables (including
>those in registers) falls out of a caller saves function call protocol.
>no muss, no fuss, just restore the stack pointer and pc.  this will
>work in the presence of inter-procedural optimization, if setjmp() is
>marked as a function that trashes all registers.

Using a full caller-saves convention by itself will not even
allow you to CONSERVE register variables, much less RESTORE them.
(Although when Paul said "restore" he may have meant CONSERVE).
When the first return from setjmp takes place, all those nicely
saved register values are popped off the stack. NEVER TO BE PUSHED
AGAIN. :-)  If you want your registers to be RESTORED, you have
to save them in the jmp_buf.

haahr@phoenix.Princeton.EDU (Paul Gluckauf Haahr) writes :
>this is one of the several reasons i consider caller-saves a better
>approach to function call protocol.

Both pure-caller-save and pure-callee-save have their benefits, but
I personally believe that a combined caller-callee saves 
protocal works best.

tim@crackle.amd.com (Tim Olson) writes:

[In response to my saying that register windows made setjmp a big deal]

>Register-windowed machines are usually very easy to write
>setjmp()/longjmp() for, because the registers are treated as a
>"stack-cache".  Thus, all you have to save is the current stack-cache
>pointer in the jmpbuf, and restore it upon a longjmp().  This makes
>automatic variables act just as statics do: their value is that as of
>the time of the longjmp().  This is much cleaner than the botch of
>saving the register contents in the jmpbuf and restoring them.
>
>Of course, you still have to worry about optimizations such as register
>coalescing, etc.

You are correct. I was in error.

Sliding register window machines SHOULD be easy to write 
setjmp/longjmp for.  The simple solution of restoring the
Control Stack (or Register Stack) Pointer to its old value 
have the effect of CONSERVING register variables, but not
of RESTORING to their values at setjmp time.

I think this is a very sane way to do it.


--chris

-------------------------------------------------------------------------------
| Nothing the God of Bio-Mechanics wouldn't let you in heaven for ?           |
-------------------------------------------------------------------------------
| Chris Quenelle            | Smart Mailers -> cquenel@polyslo.CalPoly.EDU    |
| Computer Systems Lab      | Dumb Mailers  -> !ucbvax!voder!polyslo!cquenel  |
| Cal Poly State Univ.      |-------------------------------------------------|
| San Luis Obispo, CA 93407 | On a clear disk you can seek forever.           |
-------------------------------------------------------------------------------

cquenel@polyslo.CalPoly.EDU (93 more school days) (01/27/89)

In article <15626@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
>
>Now, clearly one can sprinkle `volatile' qualifiers into the
>declarations in timed_fiddle_with(), so that it will work under pANS
>C.  But (at least some of) the variables so declared are not in fact
>volatile---the value of `base', in particular, *never changes!*  The
>qualifier is being (mis)used strictly for its side effect of inhibiting
>optimisation, not because the variable that is so qualified behaves in
>a way that is not described by the virtual machine.

	This depends on how you look at it.
	If you think of "volatile" as meaning "this has to live in memory",
	then it makes sense to me.
	Or if you consider that the setjmp/longjmp mechanism
	in a very real sense DOES violate the "virtual machine",
	then volatile is a very appropriate description.

	Some people seem to think that ideally, setjmp/longjmp
	should take the program back in TIME.  I think that 
	*conceptually* longjmp should only take the program back
	to a previous execution point.  A C routine (IMHO) has only
	one existence.  If a routine runs through part A and then
	setjmps and then runs through part B and then longjmps.
	I would *expect* that it's state would be the state after
	the execution of part B.  That is the way my working defenition
	of the "C" virtual machine works.

	--chris

rbutterworth@watmath.waterloo.edu (Ray Butterworth) (01/27/89)

In article <9467@ihlpb.ATT.COM>, gregg@ihlpb.ATT.COM (Wonderly) writes:
> > this is one of the several reasons i consider caller-saves a better
> > approach to function call protocol.
> Except that this kills any efficency that one might try to gain through
> the use of normal scratch registers at the lowest level of function call.
> What is the expense of 
>     for (i=0; i < cnt; ++i)
>         cnt[i] = strlen (str[i]);
> when caller saves verses the case where called saves?  If caller uses a
> lot of registers, it will continually save/restore them if caller saves
> whereas when called saves, it can decide when all of that work should
> really be done.

I think you've got it backwards.  If the caller saves, and the
compiler has any smarts at all, it will go something like:

    /* save all active registers now */
    for (i=0; i < cnt; ++i)
        cnt[i] = strlen (str[i]);
    /* restore all active registers now */

i.e. if the current function uses N registers, and strlen uses n registers,
then with caller saving there are N saves and N restores,
and with callee saving there are cnt*n saves and cnt*n restores.

So which is better depends upon which is smaller, N or cnt*n.

For strlen(), n might be small, but in general I think that
N is usually a lot smaller than cnt*n.

prc@maxim.ERBE.SE (Robert Claeson) (01/27/89)

In article <9480@smoke.BRL.MIL>, gwyn@smoke.BRL.MIL (Doug Gwyn ) writes:

> Really, setjmp/longjmp is just a groady "goto" and should be
> avoided for the same reasons as the ordinary "goto"...

> it is even more violent than an ordinary "goto", which is why
> it has to be more loosely constrained.  I don't think I've
> even seen a case in which setjmp/longjmp was the best way to
> design code, let alone essential.  The nearest to a valid use
> I know of is to allow a SIGINT to abort processing and fly
> back to a main command loop (e.g. in the "ed" editor), but
> even there the longjmp can cause stdio data structures to be
> left in an inconsistent state.  Asynchronous concurrent
> processes really need to be designed around better tools than
> setjmp/longjmp.

Okay, all you portable code types, maybe you can help me with making
this more portable (ie, to not use setjmp/longjmp).

In a single-key (not character) keyboard read routine I've written,
I recognizes function keys and returns a single, generic value for them
instead of the usual character. I do this by setting an alarm(1) and
reading character by character until I've got a non-redundant string, which
in the case of a printable character (an 'a' for example) is that single
character.

If a complete function key sequence is found before that alarm() is
performed, the alarm is cleared and a value is returned.  But if I
keep reading from the keyboard without finding a  real key, a SIGALRM
handler is called, which just does a longjmp back to  a setjmp in the
main (non-static) function. Then the complete string, except for the
first character, is stored in a buffer that will  be read instead of
the keyboard on subsequent calls to the function. The first  character
read is returned. This is how I handles single Escape keystrokes.

I could  probably read the clock, do a tight  loop  that reads the
keyboard non-blocking and check the clock on each iteration, but  that
would probably take too much CPU, so I don't want to do that.

Now, make this portable and non  CPU-intensive. If you think the
algorithm needs to be changed, just tell me.
-- 
Robert Claeson, ERBE DATA AB, P.O. Box 77, S-175 22 Jarfalla, Sweden
"No problems." -- Alf
Tel: +46 758-202 50  EUnet:    rclaeson@ERBE.SE  uucp:   uunet!erbe.se!rclaeson
Fax: +46 758-197 20  Internet: rclaeson@ERBE.SE  BITNET: rclaeson@ERBE.SE

ch@maths.tcd.ie (Charles Bryant) (01/27/89)

In article <5771@phoenix.Princeton.EDU> haahr@princeton.edu (Paul Gluckauf Haahr) writes:
>a setjmp/longjmp implementation that restores all variables (including
>those in registers) falls out of a caller saves function call protocol.
>no muss, no fuss, just restore the stack pointer and pc.  this will
>work in the presence of inter-procedural optimization, if setjmp() is
>marked as a function that trashes all registers.
>

Could you explain this further please. Consider the example:

	foo()
	{
		register a,b,c;
		/* useful work with a,b,c so all are live */
		if (setjmp(jb)==1) { something using a, b, and c }
		a += b+c;
		fna();		/* only a is live */
		return a;
	}

	fna()
	{
		longjmp(jb,1);
	}

Then the stack will look like this:

call to setjmp: <addr in foo> a b c <addr foo will return to>
call to longjmp: <addr in fna> <addr in foo> a <addr foo will return to>

setjmp can't know what registers were saved (i.e. a, b, c) so it must save
the sp as the address where a is saved.
if longjmp restores the sp to this value, 'foo' will assign
a = <addr in fna>
b = <addr in foo>
c = a at time of longjmp

Is this reasoning correct? Does the compiler have to save ALL the registers for
every function call in 'foo' which potentially could call longjmp or is in
a different file?
-- 

		Charles Bryant.
Working at Datacode Electronics Ltd.

gwyn@smoke.BRL.MIL (Doug Gwyn ) (01/28/89)

In article <470@maxim.ERBE.SE> prc@maxim.ERBE.SE (Robert Claeson) writes:
>Now, make this portable and non  CPU-intensive. If you think the
>algorithm needs to be changed, just tell me.

I assume you're not using stdio to do this, since longjmping from
a signal handler is unsafe if a stdio routine was active at the
time of the alarm.  A SVID-conforming UNIX provides several ways
to do the sort of thing you're attempting, for example using VMIN
and VTIME which obviate the need for SIGALRM.  Or, you could just
set a global flag in your alarm handler and return, since a SVID-
conforming UNIX will abort a read() from a terminal when the alarm
occurs.  By testing for EINTR after the read() returns, and using
the global flag as additional verification, you can make the main
control logic do the right thing.

There is no fully portable way to do what you're attempting, and
using longjmp is not a solution because it is unreliable in this
context.

meissner@tiktok.dg.com (Michael Meissner) (01/31/89)

In article <470@maxim.ERBE.SE> prc@maxim.ERBE.SE (Robert Claeson) writes:
| Okay, all you portable code types, maybe you can help me with making
| this more portable (ie, to not use setjmp/longjmp).
| 
| In a single-key (not character) keyboard read routine I've written,
| I recognizes function keys and returns a single, generic value for them
| instead of the usual character. I do this by setting an alarm(1) and
| reading character by character until I've got a non-redundant string, which
| in the case of a printable character (an 'a' for example) is that single
| character.

Both of the modern UNIX varients have methods to do timed reads
from a character device without resorting to alarm.

In Berkeley-based UNIXes (UNICI?), you do a select on the file
descriptor(s) with the given timeout specified in the struct timeval
structure.  This will also work on pipes.  Note there are some
additional macros to use in setting up the bit array of file
descriptors for 4.3 based systems that were not available in 4.2, but
they are easy enough to provide.

In System V based UNIXes, you do an ioctl to set MIN/TIME to be the
minimum number of characters to read and the timeout in tenths of a
second.  This only works on terminals (and pty's if provided).

Some version of System V.3 (V.3.1?) is susposed to have rewritten the
terminal subsystem into streams (and pipes?).  If so, you can then use
poll, which provides the same basic functionality as select, except
that it is limited to streams devices.

Given that you set an appropriate timeout, non of the above solutions
are CPU intensive, and they don't have the gapping holes of
interrupting the main task at possibly the wrong time that alarm, and
signal open up.

| I could  probably read the clock, do a tight  loop  that reads the
| keyboard non-blocking and check the clock on each iteration, but  that
| would probably take too much CPU, so I don't want to do that.
| 
| Now, make this portable and non  CPU-intensive. If you think the
| algorithm needs to be changed, just tell me.

As an aside, I've used systems at times, where the latency between
characters could be really high, and such simple minded function key
mappings would break occansionally.  For example, when I was in our
Mass. plant, I would find times when the main network down to our
North Carolina plant would be out.  I eventually resorted to calling a
California site, and from there calling North Carolina.  I think I
went through about 5-10 hosts, with each host sometimes buffering the
I/O...
--
Michael Meissner, Data General.
Uucp:	...!mcnc!rti!xyzzy!meissner
Arpa:	meissner@dg-rtp.DG.COM   (or) meissner%dg-rtp.DG.COM@relay.cs.net

jf@cci632.UUCP (Jens Fiederer) (02/02/89)

In article <9480@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>
>I don't think I've even seen a case in which setjmp/longjmp was the best
>way to design code, let alone essential.

While I have never used setjmp/longjmp myself, I have never written a large
interpreter (for example, a full blown LISP) in C.  Writing a LISP interpreter
in SAIL, I found a non-local goto facility very useful to help in error
handling.  My feeling is that setjmp/longjmp have their legitimate uses, be
they ever so few.

Jens

paul@sun505.UUCP (Paul E. Black) (02/03/89)

In article <26109@cci632.UUCP> jf@ccird3.UUCP (Jens Fiederer) writes:
>
>While I have never used setjmp/longjmp myself, ... [m]y feeling is
>that setjmp/longjmp have their legitimate uses, be they ever so few.

I agree.  I helped write an IC design editor.  The structuring seemed
better to put a setjmp in the top loop of the "command interpreter"
(the code which reads the keyboard & mouse and decides which function
should be dispatched).  Then any command which needs to terminate can
easily do a longjmp.

In about 75 000 lines of code, there are less than 40 uses of setjmp and
longjmp.  Those are confined to just three usage areas.

Paul E. Black          | UUCP: ...{pyramid,amdahl,ames}!oliveb!cirrusl!paul
CIRRUS LOGIC, Inc.     | Internet: cirrusl!paul@olivetti.com
1463 Centre Pointe Dr. | Voice: (408) 945-8305 x 210
Milpitas, CA 95035 USA

prc@maxim.ERBE.SE (Robert Claeson) (02/05/89)

I wrote:

| In a single-key (not character) keyboard read routine I've written,
| I recognizes function keys and returns a single, generic value for them
| instead of the usual character. I do this by setting an alarm(1) and
| reading character by character until I've got a non-redundant string, which
| in the case of a printable character (an 'a' for example) is that single
| character.

In article <3112@xyzzy.UUCP>, meissner@tiktok.dg.com (Michael Meissner) writes:

.....
> In System V based UNIXes, you do an ioctl to set MIN/TIME to be the
> minimum number of characters to read and the timeout in tenths of a
> second.  This only works on terminals (and pty's if provided).
.....

Does this really work that good? A function key (control) character
sequence can be of arbitrary length. The up-arrow  key on my keyboard
sends three characters, whereas a typical F-key sends 5 characters.
So how do I know what to put into VMIN?

> As an aside, I've used systems at times, where the latency between
> characters could be really high, and such simple minded function key
> mappings would break occansionally.

What better function key mapping algorithms are there? I've played with
don't having any timeouts at all for these cases, but then I cannot use
the single escape key (or whatever key the function key sequences
starts with).
-- 
Robert Claeson, ERBE DATA AB, P.O. Box 77, S-175 22 Jarfalla, Sweden
"No problems." -- Alf
Tel: +46 758-202 50  EUnet:    rclaeson@ERBE.SE  uucp:   uunet!erbe.se!rclaeson
Fax: +46 758-197 20  Internet: rclaeson@ERBE.SE  BITNET: rclaeson@ERBE.SE

gwyn@smoke.BRL.MIL (Doug Gwyn ) (02/05/89)

In article <483@maxim.ERBE.SE> prc@maxim.ERBE.SE (Robert Claeson) writes:
>Does this really work that good? A function key (control) character
>sequence can be of arbitrary length. The up-arrow  key on my keyboard
>sends three characters, whereas a typical F-key sends 5 characters.

The character sequence should follow ANSI X3.64, meaning that it begins
with an ESC character and continues through an alpha.  That can be
parsed without any timeouts at all.

What probably is contributing to the confusion is that you bought
keyboards that have an ESC key the user can press that violates the
X3.64 standard.  Remove the key or tell your users how to recover
from it (treating a second consecutive ESC specially is one way).

les@chinet.chi.il.us (Leslie Mikesell) (02/06/89)

In article <483@maxim.ERBE.SE> prc@maxim.ERBE.SE (Robert Claeson) writes:

>> In System V based UNIXes, you do an ioctl to set MIN/TIME to be the
>> minimum number of characters to read and the timeout in tenths of a
>> second.  This only works on terminals (and pty's if provided).
>.....

>Does this really work that good? A function key (control) character
>sequence can be of arbitrary length. The up-arrow  key on my keyboard
>sends three characters, whereas a typical F-key sends 5 characters.
>So how do I know what to put into VMIN?

Put something fairly large in VMIN and something small in VTIME.  Then
make read() requests for many characters at a time.  The read() will
return when at least one character is typed and either VTIME has elapsed
between characters or VMIN characters have been entered.  Increasing
VMIN/VTIME beyond what you need to capture the function key strings
will increase efficiency (by reducing the read() system calls) at the
cost of response time if the operator can type faster than VTIME expires.
In any case, this approach should be much more efficient that wrapping
alarm()'s around single character read()s.

Les Mikesell

les@chinet.chi.il.us (Leslie Mikesell) (02/06/89)

In article <9597@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:

>What probably is contributing to the confusion is that you bought
>keyboards that have an ESC key the user can press that violates the
>X3.64 standard.  Remove the key or tell your users how to recover
>from it (treating a second consecutive ESC specially is one way).

It must be fun to run vi/emacs/ksh/etc. on a keyboard that has not
ESC key.


Les Mikesell

prc@maxim.ERBE.SE (Robert Claeson) (02/06/89)

In article <9597@smoke.BRL.MIL>, gwyn@smoke.BRL.MIL (Doug Gwyn ) writes:
> In article <483@maxim.ERBE.SE> prc@maxim.ERBE.SE (Robert Claeson) writes:
> >Does this really work that good? A function key (control) character
> >sequence can be of arbitrary length. The up-arrow  key on my keyboard
> >sends three characters, whereas a typical F-key sends 5 characters.
> 
> The character sequence should follow ANSI X3.64, meaning that it begins
> with an ESC character and continues through an alpha.  That can be
> parsed without any timeouts at all.

I have already written code to parse ANSI X3.64 sequences, but since
there's so many non-ANSI terminals out there, I'm not really sure I
want to lock myself into ANSI-compatible terminals (even though I'd
like to).

> What probably is contributing to the confusion is that you bought
> keyboards that have an ESC key the user can press that violates the
> X3.64 standard.  Remove the key or tell your users how to recover
> from it (treating a second consecutive ESC specially is one way).

I'd like to get rid of all escape keys in the world (that's why I
only buy VT220-style keyboards nowadays), but on the other hand, I
don't think an escape key violates the standard. As I recall, all
function key sequences should start with the CSI 8-bit character
or 7-bit character sequence (ESC [) and end with an alphanumeric
character.

Oh, come to think of it, maybe I should get rid of my table-driven
code and invent "terminal handlers" instead. You know, determine
the terminal type (with ESC Z or by reading the TERM variable) and
start an appropriate process that handles all i/o to the terminal,
and have my application talk to that process (via good 'ole pipes,
message queues or shared memory).
-- 
Robert Claeson, ERBE DATA AB, P.O. Box 77, S-175 22 Jarfalla, Sweden
"No problems." -- Alf
Tel: +46 758-202 50  EUnet:    rclaeson@ERBE.SE  uucp:   uunet!erbe.se!rclaeson
Fax: +46 758-197 20  Internet: rclaeson@ERBE.SE  BITNET: rclaeson@ERBE.SE

gwyn@smoke.BRL.MIL (Doug Gwyn ) (02/07/89)

In article <7644@chinet.chi.il.us> les@chinet.chi.il.us (Leslie Mikesell) writes:
>It must be fun to run vi/emacs/ksh/etc. on a keyboard that has not
>ESC key.

If you were following the discussion you should have heard how reliance
on ESC timing causes problems.  If you don't like removal of the ESC key
then follow my second suggestion instead.

wayne@dsndata.uucp (Wayne Schlitt) (02/07/89)

In article <9597@smoke.BRL.MIL> gwyn@smoke.BRL.MIL (Doug Gwyn ) writes:
>
>   What probably is contributing to the confusion is that you bought
>   keyboards that have an ESC key the user can press that violates the
>   X3.64 standard.  Remove the key or tell your users how to recover
>   from it (treating a second consecutive ESC specially is one way).


*gack*.

you cant be serious.

do you ever use vi or emacs?  both of them need to use a single ESC
key to work.  if the X3.64 standard says you can use the escape key,
then the X3.64 standard isnt going to be used much.  



-wayne

gwyn@smoke.BRL.MIL (Doug Gwyn ) (02/07/89)

In article <WAYNE.89Feb6150523@dsndata.uucp> wayne@dsndata.uucp (Wayne Schlitt) writes:
>you cant be serious.

Mostly I am.

>do you ever use vi or emacs?

When I'm forced to I do.

>both of them need to use a single ESC key to work.

No, as a matter of fact EMACS wants a "meta" key.
On terminals lacking a suitable "meta" key, ESC is often substituted.

This use of ESC never was essential, it's just a kludge that should
have been avoided in the first place.  (TECO was the first and worst
offender, but at least it predated X3.64 so it had an excuse.)

>if the X3.64 standard says you can[not] use the escape key,
>then the X3.64 standard isnt going to be used much.  

That's not exactly what it says.  It does require that escape sequences
be properly handled, and in many environments that can't be reliably
done if you let users bang away on a separate ESC key.

Note that I suggested a second alternative if you don't like the one
about removing the ESC key.

prc@maxim.ERBE.SE (Robert Claeson) (02/08/89)

In article <7644@chinet.chi.il.us>, les@chinet.chi.il.us (Leslie Mikesell) writes:

> It must be fun to run vi/emacs/ksh/etc. on a keyboard that has not
> ESC key.

Sort of. All keyboards  that I know of that lacks an ESC key has another
key as a substitute. For  example, on the VT220 keyboard, one can use
ctrl-3 or ctrl-[.
-- 
Robert Claeson, ERBE DATA AB, P.O. Box 77, S-175 22 Jarfalla, Sweden
"No problems." -- Alf
Tel: +46 758-202 50  EUnet:    rclaeson@ERBE.SE  uucp:   uunet!erbe.se!rclaeson
Fax: +46 758-197 20  Internet: rclaeson@ERBE.SE  BITNET: rclaeson@ERBE.SE

daveh@marob.MASA.COM (Dave Hammond) (02/08/89)

In article <9597@smoke.BRL.MIL> (Doug Gwyn (VLD/VMB) <gwyn>) writes
[concerning multi-byte keystroke sequences]:
>
>The character sequence should follow ANSI X3.64, meaning that it begins
>with an ESC character and continues through an alpha.  That can be
>parsed without any timeouts at all.

Considering the number of display manufacturers *not* using ANSI X3.64
sequences, this seems a bit narrow-minded.

>What probably is contributing to the confusion is that you bought
>keyboards that have an ESC key the user can press that violates the
>X3.64 standard.  Remove the key or tell your users how to recover
>from it (treating a second consecutive ESC specially is one way).

The very "feature" which drove me away from VT-220's was the lack of
a real ESC key.  Substituting Shift-F12 (or even programming an
unshifted F-key) just doesn't cut it.  I'm sure you can think of several
programs which would break without an ESC key.

Handling keyboards which may transmit their multi-byte sequence intro
key as a single byte key is not especially difficult.  You do have to
rely on some sort of timeout, but there are adequate calls available
on most OS's to either read with timeout or peek at the keyboard queue.
Timeouts of 1/4 to 1/2 sec. on a 9600 baud serial line are adequate to
distinguish between single bytes and failed sequences -- and barely
noticeable as a "delay" to the user.

--
Dave Hammond
...!uunet!masa.com!marob!daveh
daveh@marob.masa.com

rob@hathi.eng.ohio-state.edu (Rob Carriere) (02/09/89)

In article <501@maxim.ERBE.SE> prc@maxim.ERBE.SE (Robert Claeson) writes:
>In article <7644@chinet.chi.il.us>, les@chinet.chi.il.us (Leslie Mikesell) 
writes:
>> It must be fun to run vi/emacs/ksh/etc. on a keyboard that has not
>> ESC key.
>Sort of. All keyboards  that I know of that lacks an ESC key has another
>key as a substitute. For  example, on the VT220 keyboard, one can use
>ctrl-3 or ctrl-[.

Yeah.  You should hear what my finger reflexes have to say about that,
though. 

SR

gwyn@smoke.BRL.MIL (Doug Gwyn ) (02/09/89)

In article <501@maxim.ERBE.SE> prc@maxim.ERBE.SE (Robert Claeson) writes:
-In article <7644@chinet.chi.il.us>, les@chinet.chi.il.us (Leslie Mikesell) writes:
-> It must be fun to run vi/emacs/ksh/etc. on a keyboard that has not
-> ESC key.
-Sort of. All keyboards  that I know of that lacks an ESC key has another
-key as a substitute. For  example, on the VT220 keyboard, one can use
-ctrl-3 or ctrl-[.

I don't know about ctrl-3, but ctrl-[ almost certainly transmits the
ASCII ESC code, so it amounts to an ESC key.  That brings one right
back to the original problem, of distinguishing between legitimate
escape sequences and human-typed garbage.

gwyn@smoke.BRL.MIL (Doug Gwyn ) (02/09/89)

In article <546@marob.MASA.COM> daveh@marob.masa.com (Dave Hammond) writes:
>You do have to rely on some sort of timeout, but there are adequate
>calls available ...

Apparently you missed the posting that explained why timeout is NOT
adequate.