[comp.lang.c] why do you need volatile?

reg%lti.UUCP@bu-it.bu.edu (Rick Genter x18) (06/03/88)

If anyone still (!) doesn't understand why volatile is necessary,
look at the March, 1988 issue of the Microsoft Systems Journal,
page 23, which contains a listing of a sample program that uses
multiple threads of execution within a single process.  There is
a boolean variable called bContinueCalc which gets set to FALSE 
when the calculation loop is supposed to abort.  The calculation
loop is

	for (A = 1.0, i = 0 ; i < iCalcRep ; i++) {
		if (! bContinueCalc)
			break;

		A = Savage (A);
	}

The other thread of execution deals with the user interface (the
Presentation Manager in OS/2).  If the ABORT button is clicked on,
the variable bContinueCalc is set to FALSE.  bContinueCalc is a
global variable declared as

	BOOL bContinueCalc = FALSE;

It should be declared

	volatile BOOL bContinueCalc = FALSE;

otherwise the C compiler is perfectly justified taking the test of
bContinueCalc out of the for loop, thus invalidating the whole use
of the variable.

					- reg

(P.S. Don't flame me about the naming conventions, OS/2, etc.  I'm
just citing an example; I don't claim to *like* any of what I cited.)
--
Rick Genter					...!buita!lti!reg
Language Technology, Inc.			reg%lti.uucp@bu-it.bu.edu
27 Congress St., Salem, MA 01970		(617) 741-1507

faustus@ic.Berkeley.EDU (Wayne A. Christopher) (06/03/88)

In article <15735@brl-adm.ARPA>, reg%lti.UUCP@bu-it.bu.edu (Rick Genter x18) writes:
> 	for (A = 1.0, i = 0 ; i < iCalcRep ; i++) {
> 		if (! bContinueCalc)
> 			break;
> 		A = Savage (A);
> 	}
> ... should be declared
> 
> 	volatile BOOL bContinueCalc = FALSE;
> 
> otherwise the C compiler is perfectly justified taking the test of
> bContinueCalc out of the for loop, thus invalidating the whole use
> of the variable.

I don't think this is right.  If bContinueCalc were an automatic variable
that never had its address taken, or there were no function call inside the
loop, then the test could be taken out, but not otherwise.  "volatile"
doesn't mean that more than one function can modify a variable, but that
a variable can become modified by an event that happens wholly outside of
the control of the program.  These are limited to (I think) signal handlers
and device registers.  If your program doesn't use either of these, you
don't have to declare anything volatile.

	Wayne

tim@amdcad.AMD.COM (Tim Olson) (06/03/88)

In article <3754@pasteur.Berkeley.Edu> faustus@ic.Berkeley.EDU (Wayne A. Christopher) writes:
| In article <15735@brl-adm.ARPA>, reg%lti.UUCP@bu-it.bu.edu (Rick Genter x18) writes:
| > 	for (A = 1.0, i = 0 ; i < iCalcRep ; i++) {
| > 		if (! bContinueCalc)
| > 			break;
| > 		A = Savage (A);
| > 	}
| > ... should be declared
| > 
| > 	volatile BOOL bContinueCalc = FALSE;
| > 
| > otherwise the C compiler is perfectly justified taking the test of
| > bContinueCalc out of the for loop, thus invalidating the whole use
| > of the variable.

Actually, because there is a function call in the loop, the test cannot
typically be removed, unless the compiler can trace the program flow and
ensure that bContinueCalc is not modified elsewhere.  However, if there
were no function call in the loop, then the compiler *is* justified in
moving the test out of the loop, since it is invariant.

Here is an example, from the MetaWare 29000 C compiler:

=== non-volatile, function call in loop ===
;int bool;
;
;int f()
;{
;	int i = 0;
;	
;	for (;;) {
;		if (bool)
;			break;
;		i = A(i);
;	}
;	return i;
;}
	const	gr96,0	; (0x0)
	const	lr4,_bool
	consth	lr4,_bool
L00012:
	load	0,0,gr121,lr4		<-- note that load of "bool",
	cpeq	gr121,gr121,0		<-- and comparison are in the loop
	jmpt	gr121,L00014
	nop
	.
	.

=== non-volatile, no function call in loop ===
;int bool;
;
;int f()
;{
;	int i = 0;
;	
;	for (;;) {
;		if (bool)
;			break;
;		++i;
;	}
;	return i;
;}
	const	gr121,_bool
	consth	gr121,_bool
	load	0,0,gr121,gr121		<-- "bool" is loop-invariant, so it
	const	gr96,0	; (0x0)		<-- is moved out, along with the
	cpeq	gr121,gr121,0		<-- comparison to zero!
L00012:
	jmpt	gr121,L00014
	nop
	jmpi	lr0
	nop
L00014:
	jmp	L00012
	add	gr96,gr96,1


=== volatile, no function call in loop ===
;volatile int bool;
;
;int f()
;{
;	int i = 0;
;	
;	for (;;) {
;		if (bool)
;			break;
;		++i;
;	}
;	return i;
;}
	const	gr96,0	; (0x0)
	const	gr120,_bool
	consth	gr120,_bool
L00012:
	load	0,0,gr121,gr120		<-- load is not hoisted because
	cpeq	gr121,gr121,0		<-- of volatile declaration.
	jmpt	gr121,L00014
	nop
	jmpi	lr0
	nop
L00014:
	jmp	L00012
	add	gr96,gr96,1


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

bill@proxftl.UUCP (T. William Wells) (06/18/88)

In article <3754@pasteur.Berkeley.Edu>, faustus@ic.Berkeley.EDU (Wayne A. Christopher) writes:
) In article <15735@brl-adm.ARPA>, reg%lti.UUCP@bu-it.bu.edu (Rick Genter x18) writes:
) >     for (A = 1.0, i = 0 ; i < iCalcRep ; i++) {
) >             if (! bContinueCalc)
) >                     break;
) >             A = Savage (A);
) >     }
) > ... should be declared
) >
) >     volatile BOOL bContinueCalc = FALSE;
) >
) > otherwise the C compiler is perfectly justified taking the test of
) > bContinueCalc out of the for loop, thus invalidating the whole use
) > of the variable.
)
) I don't think this is right.  If bContinueCalc were an automatic variable
) that never had its address taken, or there were no function call inside the
) loop, then the test could be taken out, but not otherwise.  "volatile"
) doesn't mean that more than one function can modify a variable, but that
) a variable can become modified by an event that happens wholly outside of
) the control of the program.

Not quite, a nonvolatile variable is presumed to be modified only
by execution in the standard C paradigm.  This implies single
thread execution, among other things.  Now, if bContinueCalc is
demonstrably not changed in Savage(), the compiler is justified
in moving the test outside the loop.  To prevent that, one should
declare it volatile, meaning that something outside the normal
flow of execution can change it.