[net.emacs] CCA Emacs Redisplay Bug Fix

massar@godot.UUCP (J.P. Massar) (02/27/85)

At times, especially when the load average is high, and especially
when you have the Time Display option set, Emacs would screw up
redisplaying the screen after processing a lot of typed characters.

For instance, if you typed 'abcdefghij' quickly

Emacs might print 'abc'

then pause and process the rest of the characters you typed without displaying 
them, then display the rest of the line which might look like

abcefghj

i.e., some characters might be 'missing'.  ('missing' because by typing
C-L the proper line

abcdefghij

will be displayed).

The problem is basically that during the times Emacs processes input 
without displaying it, it can get confused as to where the cursor really is.

Here is an essentially simple fix which solves the problem with respect
to Time Display.  Included in this is a fix which makes Emacs only check for
mail once a minute (as opposed to what it does now, which is to check
every time you type a character! (this checking does a 'stat' system
call, which presumably generates a signficant amount of I/O and 
processing)).

The fix is entirely within e_main.c.  I cannot provide diffs because of
other changes I have made.  Instead, I provide source for the two
routines that need changing:  edit and timed.
        
My first impressions are that with these fixes Emacs handles much better
under heavy loads.  

These fixes do not solve the problem entirely.  There are undoubtably
other places in the code where Emacs does similar things and likewise
gets confused about where the cursor is.  One of these may be when it
does Auto Save's.  Basically any time Emacs displays something in the
mode line or in the Echo area there is the possibly that this bug
could be present, although it is not necessarily present.

Now that the bug is understood, if anyone can produce it in circumstances
other than time redisplay it can probably be tracked down and fixed.
I don't promise to fix these cases, but I'd be interested in hearing
about the cases where it still happens.

***************************************************************************

/***** Replace the code for edit in e_main with the following *****/

***************************************************************************


#define DELTA_DT 5                      /* seconds between time redisplays */
#define DELTA_MCHECK 60                 /* seconds between mail checks */

static long clock,mailctime,dtctime;


/* EMACS main editing loop */

edit(earg)
{
	register i;
	char retval;

	for (;;) {
		if (mlhexf && !infile && (i = binsrch("Main Loop Hook", (char *)comtab, NCOMS, sizeof(struct key))) >= 0 && i < NCOMS) {
			xcnum = i;
			(void)(*comtab[i].func)(1, -1, 0, 0, 0);
		}
		disp = echocom = flushed = longline = numarg = piperr = quitf = signonly = autargf = 0;
		if (Savemd && ++nschar >= Saveint)
			asave();
		updis();
		if (bgspellf && !access(sptemp, 0)) {
			echo2("Background Spell has finished.");
			rdisputc(Ctrl('G'));
			fflush(stdout);
			sleep(1);
			goback();
			bgspellf = 0;
		}
                
                time(&clock);

#if	vms
		ckmsgs();		/* Check for broadcast messages */
#endif

#if	!vms

                /* check for mail every so often if not processing typeahead */
                
		if (Mailnote && clock > mailctime + DELTA_MCHECK && empty(0)) {
                        mailctime = clock;
			ckmail(1, 0);
                }
#endif

                /* redisplay the time if we're not processing typeahead */
                /* and some number of seconds have passed */

		if (Timemd && clock > dtctime + DELTA_DT && empty(0)) {
                        dtctime = clock;
			dtime(); 
                }
                        
#if	BSD42
		if (streqn(TERM, "sun", 3) && empty(0) && !findloc(curline, curcol))
			cc = blincur();
		else
#endif
			cc = getchar();
		if (prtermf) {
			prtermf = 0;
			continue;	/* Dummy character */
		}
		if (krappf)
			krappf--;
		if (vcolflg)
			vcolflg--;
		if (Autargm && (isdigit(cc) || cc == '-')) {
			autargf = 1;
			retval = autoarg();
		} else {
			if (Abbrevmd && (index(Abbexpch, cc) || *Addabbex && index(Addabbex, cc)))
				expabb();
			retval = keyex(1);
		}
		Sedit++;
		if (!earg || exitf || quitf && infile && !recovery)
			return(retval);
	}
}


***************************************************************************

/***** Replace the code for edit in e_main with the following *****/

***************************************************************************

/* Display the time in the lower right hand corner of the screen */

dtime()
{
	register char *tp;
	register len;
	char dtimebuf[SBUFSIZ];
	int x;
	int y;
	
		tp = ctime(&clock);
		tp[16] = 0;
		x = mapline;
		y = mapcol;
		if (*echo2buf && *echo2buf != ' ') {
			sprintf(dtimebuf, "                                                        %s", tp);
			len = min(strlen(echo2buf), 56);
			(void) strcpy(echo2buf + len, dtimebuf + len);
			echo2(echo2buf);
		} else
			echo2("                                                        %s", tp);
		newpos(x, y);
}

-- 
-- JP Massar, Thinking Machines Corporation, Cambridge, MA
-- ihnp4!godot!massar
-- massar@cca-unix

root@bu-cs.UUCP (Barry Shein) (02/28/85)

In a similar vein, I noticed that running CCA EMACS on our student
VAX750 (4.2bsd) seemed to be very uneven and creating a fair
amount of load. I began by studying the main loop that reads in
keystrokes and noticed the following:

	characters are read in 1 at a time with a read(0,&c,1)
	type statement

	there is polling for typeahead existence via empty(0)
	calls which invokes ioctl(0,FIONREAD,&n)

This causes a lot of calls to the system per keystroke. Read the
code and consider what occurs when all your users are using
terminals with multi-character function keys (eg. VT100s, VT200s)

Solution (maybe better put, approach): [this applies to 4.2bsd]

replace the read(0,&c,1) call with a call to a routine rdchar(0)
which buffers up as many input chars as it can (a little _filbuf/getc
routine really.) Set the character count held in the buffer
into a global. Now empty(0) becomes simply a test against this
character count which isn't as accurate but the penalty for being
wrong is much less than the penalty for so many sys calls
NOW for the thing that makes it work:

	use a select() call in rdchar to delay 1/10 of a second:
	(PSUEDO-CODE HERE)

	again:
		if(n_inbuf-- > 0) return(buf[nxtchar++])
		else {
			t->tv_sec = 0 ;
			t->tv_usec = NTENTHS * 100000 ;
			select(0,0,0,0,t) ;	/* cast these */
			if((n_inbuf = read(0,MAXBUF,buf)) <= 0) ...err
			nxtchar = 0 ;
			goto again ;
		}

	This will tend to pick up function keys in one shot,
	sometimes more than one if the person is hammering an
	arrow key or some such.

	empty becomes:

	return(!n_inbuf) ;

Just for generality's sake, I made the number of 1/10s of seconds
available as an EMACS var to the user (test for zero before the select())
Seems to help although these things are very hard to measure. That
system was definitely getting too many syscalls as judged by vmstat,
now seems better.

On a SYSV system one can just use the MINchars, MINtime features
of ~CANON for similar effect.

Yes, I still get the effect of pause while a lot of typing coming
in and then just display entire update.

I have found that 2 or 3 1/10s of a second is tolerable.

It is possible I am crocked here I suppose...anyone got a good
way to quantify the supposed advantage? I am just going by
common sense and observation which is very dangerous in
performance issues.

P.S. changes are in e_main.c and e_io.c

		-Barry Shein, Boston University

chris@umcp-cs.UUCP (Chris Torek) (02/28/85)

Why the select() with timeout?  You shouldn't have to poll the keyboard...
in Gosling Emacs, the "interesting" events are

	keyboard input
	PTY input (for subprocesses)
	child death

The first two are done with a select call, the third with SIGCHLD; my
hacks to do keystroke echoing just split the single select call into
a select-with-timeout followed by a select-forever....
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 4251)
UUCP:	{seismo,allegra,brl-bmd}!umcp-cs!chris
CSNet:	chris@umcp-cs		ARPA:	chris@maryland