[comp.sources.games.bugs] What setbuf

epsilon@wet.UUCP (Eric P. Scott) (07/16/89)

Many, many screen-oriented applications posted to the net have a
serious flaw: they do terminal output a character at a time.
This is fine for single-user PCs running MSDOS.  This is NOT fine
for multi-user machines running UNIX.  The culprit here is
Standard I/O; stdout is not buffered when directed to a terminal.
(System V has line buffering, but it's not the appropriate
solution here.)  This means that a write(2) call is performed for
each character output.  This has substantial overhead on many
machines--not only does your program run slowly, response time
for everyone else is degraded.  The fix is very simple:
put
	setbuf(stdout, malloc(BUFSIZ));

early in main(), and (if you've done any output)

	fflush(stdout);

before asking for terminal input, sleeping, going heavily
compute-bound, changing tty modes, or using fork/exec/system-type
calls.  This causes stdio to store up to BUFSIZ (defined in
<stdio.h>) characters before demanding a context switch.  Since
BUFSIZ is typically 512 or 1024, this can be a substantial
improvement.

Make sure you have declared malloc to return the proper type for
your system (either char * or void *).

I urge source-group moderators to perform a cursory check before
redistributing submissions.  Note that the absence of setbuf/
setvbuf/setlinebuf calls does not automatically damn a program,
it just means that closer inspection is needed.  This issue comes
up often enough that it might warrant a mention in regular
monthly/quarterly postings.

					-=EPS=-

"We haven't found a cure for people who insist on writing
	printf("\n");
 but our efforts at rehabilitation have been largely successful."

nelson@sun.soe.clarkson.edu (Russ Nelson) (07/16/89)

In article <283@wet.UUCP> epsilon@wet.UUCP (Eric P. Scott) writes:

   Many, many screen-oriented applications posted to the net have a
   serious flaw: they do terminal output a character at a time.
   This is fine for single-user PCs running MSDOS. ...

No it isn't.  "Kernel"[1] calls in MS-LOSS have quite a big of overhead,
and the difference between buffered and unbuffered I/O *is* noticable.
So please, use setbuf in your MS-LOSS programs also.

    [1] To the extent that MS-LOSS can be said to have a kernel.

--
--russ (nelson@clutx [.bitnet | .clarkson.edu])
I'm not the NRA, and I vote twice.

guy@auspex.auspex.com (Guy Harris) (07/18/89)

>The fix is very simple: put
>	setbuf(stdout, malloc(BUFSIZ));
>
>early in main(),

In which case, if you run out of e.g. swap space (or address space,
unless you've got tons of it and haven't consumed it all due to some
other bug), you may drop core.  The fix is very simple; either do

	register char *bufp;

	...

	if ((bufp = malloc(BUFSIZ)) == NULL) {
		complain and exit;
	}
	setbuf(stdout, bufp);

or just do:

	static char stdoutbuf[BUFSIZ];

	...

	setbuf(stdout, stdoutbuf);

in "main()".  (I say "static" because if you return from "main()", it's
like exiting, and standard output will be flushed; better safe than
sorry.)  "malloc" is overkill in this situation....

chad@lakesys.UUCP (D. Chadwick Gibbons) (07/18/89)

In article <2245@auspex.auspex.com> guy@auspex.auspex.com (Guy Harris) writes:
|
|>The fix is very simple: put
|>	setbuf(stdout, malloc(BUFSIZ));
|
|In which case, if you run out of e.g. swap space (or address space,
|unless you've got tons of it and haven't consumed it all due to some
|other bug), you may drop core.

	Uh, no. Sorry. Read the man page for setbuf a little better.
malloc() returns NULL when it can't allocate the memory, right? If the
second argument to setbuf is NULL, buffering will be turned off. No
dumping core, just a straight through output mechanism-which might be
nice to have considering your program can't allocate memory and it is
early on in the game.
-- 
D. Chadwick Gibbons, chad@lakesys.lakesys.com, ...!uunet!marque!lakesys!chad

epsilon@wet.UUCP (Eric P. Scott) (07/19/89)

In article <2245@auspex.auspex.com> guy@auspex.auspex.com (Guy Harris) writes:
>>The fix is very simple: put
>>	setbuf(stdout, malloc(BUFSIZ));
>>
>>early in main(),
>
>In which case, if you run out of e.g. swap space (or address space,
>unless you've got tons of it and haven't consumed it all due to some
>other bug), you may drop core.

I got a mail message claiming the same thing--on which system
does it occur?  All the ones I know of simply have malloc()
return a NULL pointer, which is valid input to setbu()--it sets
the stream unbuffered.

>[suggested checking malloc's return value]

In any case, if the first thing you do in a program is malloc()
and it fails, you're probably in serious trouble anyway.  (I
normally practice a fairly paranoid programming style, this being
one of the exceptions.)

Also, NULL should always be cast to a pointer of the desired type
when used explicitly.  I prefer  if (!(x))  to test for this
case; the ! operator is true against a NULL pointer type, \even
if/ your architecture uses something other than 0 to represent
NULL, and false otherwise.

>[suggested using a static buffer]

I've run across stdio implementations that are documented to only
work with buffers obtained from malloc().  (I presume this means
they free() on fclose, or somesuch nonsense.  There should be a
"MYBUF" flag, but since that isn't part of the documented
interface, some vendors didn't include it.

SunOS users: your stdio is different from all UNIX flavors I know
about.  See the setbuf(3s) manual page.  BSD users will probably
want to use the BSD-specific buffering functions as well.  It's a
lot easier to change a setbuf() to something "better" than to find
all the places to fflush if it's not done automatically as a side
effect of reading stdin.  I.e. plan for fully buffered terminal
output.

					-=EPS=-

prc@erbe.se (Robert Claeson) (07/19/89)

In article <283@wet.UUCP> epsilon@wet.UUCP (Eric P. Scott) writes:

>The fix is very simple:
>put
>	setbuf(stdout, malloc(BUFSIZ));

>early in main(), and (if you've done any output)

>	fflush(stdout);

>before asking for terminal input, sleeping, going heavily
>compute-bound, changing tty modes, or using fork/exec/system-type
>calls.  This causes stdio to store up to BUFSIZ (defined in
><stdio.h>) characters before demanding a context switch.  Since
>BUFSIZ is typically 512 or 1024, this can be a substantial
>improvement.

Or, on at least System V-type systems, use the call:

	#define BUFFER_SIZE	32768

	setvbuf(stdout, malloc(BUFFER_SIZE), _IOFBF, BUFFER_SIZE);

which can buffer much more than the setbuf() construct above. The buffer
can be of arbitrary size.

i
n
e
w
s
-- 
          Robert Claeson      E-mail: rclaeson@erbe.se
	  ERBE DATA AB

rjc@edai.ed.ac.uk (Richard Caley) (07/20/89)

In article <857@lakesys.UUCP> chad@lakesys.UUCP (D. Chadwick Gibbons) writes:

>|>The fix is very simple: put
>|>	setbuf(stdout, malloc(BUFSIZ));

But why use malloc at all, seems odd to dynamically allocats something 
which you will need every run and which will always be the same size . . .

	static char buffer[BUFSIZ];

	setbuf(stdout,&buffer);