volatile and multiple threads

  • Thread starter S James S Stapleton
  • Start date
S

S James S Stapleton

Is volatile necessary anymore? I have a two-thread piece of code I've been
testing to figure out what volatile does (fairly simple code, uses
pthreads). I have an update thread (variables passed as volatile) and a
print thread (one variable volatile, the other, not). There is no difference
in the behavior of the volatile and nonvolatile thread.

I'm compiling this with gcc, using the -O2 and -pthreads flags.

The sudocode is at the end. The result I'm getting is that the correct
memory addresses and values are being printed by the volatile and
non-volatile variable. If I understand things correctly, the non-volatile
variables should give the wrong addresses at least half the time. Is GCC
just smart enough to handle this, or am I completely misunderstanding
things. I can provide the code, but I'd rather not clutter up the list with
it, without a request.

Thanks,
-Jim Stapleton

* two pointers (a, b) are global (non volatile) variables. Each is of type
(int*)
* each is malloc'ed and the value in the allocated memory set to the value
of a counter variable (0).
* A global volatile turn variable is set to 0 (0 = print, 1 = update).
* the print and update functions are called.

* print function
** output that the print function was called
** call the internal print function with a non-volatile pointer to a
(int**), and a volatile pointer to b (volatile int**)
** output that the print function is closing
** exit

* internal print function
** wait until turn = 0
** loop until count = 5
*** print the memory addresses a & b point to, and the values stored therein
*** set turn to 1
*** wait until turn = 0
** exit

* update function
** output that the update function was called
** call the internal update function with a volatile pointer to a (volatile
int**), and a volatile pointer to b (volatile int**)
** output that the update function is closing
** exit

* internal update function
** wait until turn = 1
** loop until count = 5
*** increment count
*** assign integer pointers (int*) t1 and t2 to the memory values pointed to
by the arguments (eqiv to t1 = a, t2 = b)
*** malloc new memory to a and b (sizeof(int)), and set the value to the
same as count.
*** print out eqiv: "*t1 (t1) -> *a (a) *t2 (t2) -> *b (b)"
*** free t1 and t2
*** set turn = 0
*** wait until turn = 1
** exit
 
W

Walter Roberson

Is volatile necessary anymore? I have a two-thread piece of code

Threads are not part of the C programming language itself.
Your system's implementation of threads might (or might not)
have imposed constraints on the generated code such that
everything worked out okay in your particular program on
that particular implementation (on that particular hardware.)

Is volatile necessary anymore? Try writing a C program
on an embedded platform that has memory-mapped I/O registers,
such that reading a register fetches the "next" input value.
On such systems, you don't want the compiler optimizing away
references to the location because it figures the reference
is just going to generate the same value as last time; you
also don't want the compiler introducing new references to the
location figuring that it doesn't need to remember the value
because the value will still be there when it is needed next.

I do not know whether volatile is of any theoretical benefit in
variables shared between pthreads; for that you should consult
comp.programming.threads or the like.
 
S

S James S Stapleton

Is volatile necessary anymore? Try writing a C program
on an embedded platform that has memory-mapped I/O registers,
such that reading a register fetches the "next" input value.
On such systems, you don't want the compiler optimizing away
references to the location because it figures the reference
is just going to generate the same value as last time; you
also don't want the compiler introducing new references to the
location figuring that it doesn't need to remember the value
because the value will still be there when it is needed next.

OK, I kept seeing references to embedded systems when I was look at things
regarding this. I assume then that they have more agressively optimising
compilers? Would volatile thus be less useful in a non-embedded system?
I do not know whether volatile is of any theoretical benefit in
variables shared between pthreads; for that you should consult
comp.programming.threads or the like.

Thanks, I'll check that out.

-Jim Stapleton
 
J

Jens Thoms Toerring

OK, I kept seeing references to embedded systems when I was look at things
regarding this. I assume then that they have more agressively optimising
compilers?

It's not because of the optimization being more aggresive. If
you have a hardware register mapped to memory and you can achieve
a certain effect by writing to that address, then you must make
sure that the write really happens. If the memory address isn't
marked as volatile the comiler is allowed to make up a copy of
what gets written there in a register instead of really writing
it out since it can't know that this write has side-effects.
Would volatile thus be less useful in a non-embedded system?

It's not only embedded systems, also hardware drivers for opera-
ting systems will use volatile. But you can also see the diffe-
rence with a "normal" program, just try

#include <limits.h>
int main( void )
{
volatile int i;
for ( i = 0; i < INT_MAX; i++ )
/* empty */ ;
return 0;
}

If you remove the 'volatile' the compiler will rather likely opti-
mize out the whole loop, drastically reducing the execution time.

But volatile can also be useful for signal handlers to make sure
the part of a program accessing a flag that gets set in a signal
handler doesn't use an out of date copy in a register (but then
you usually also throw 'sig_atomic' into the mix to avoid partial
reads).
Regards, Jens
 
R

Roberto Waltman

OK, I kept seeing references to embedded systems when I was look at things
regarding this. I assume then that they have more agressively optimising
compilers? Would volatile thus be less useful in a non-embedded system?

To give a concrete example, lets assume the following program is built
in such a way that the variables 'inputs' and 'outputs' are mapped in
the obvious ways to hardware registers.

-------------------------------------------

#define MAKE_IT_WORK ???

#if MAKE_IT_WORK
#define MAGIC volatile
#else
#define MAGIC
#endif

extern unsigned char MAGIC inputs;
extern unsigned char MAGIC outputs;

#define SMOKE_DETECTOR 0x01
#define SPRINKLERS_ON 0x01

int main(void)
{
while (1)
{
if (inputs & SMOKE_DETECTOR)
{
outputs |= SPRINKLERS_ON;
break;
}
}
return 0;
}

-------------------------------------------

If 'inputs' is not declared as volatile, a compiler could relocate
reading its value before the loop, doing it only once (since nothing
changes its value inside the loop)
Similarly if 'outputs' is not volatile, the assignment to it could be
removed altogether, (since it is not used afterwards,) leaving the
equivalent of:

...
int main(void)
{
/* read the value of 'inputs' into a register */
while (1)
{
if ( [saved value of 'inputs'] & SMOKE_DETECTOR)
{
break;
}
}
return 0;
}
 
K

Kaz Kylheku

Is volatile necessary anymore?

Yes, in portable, standard-conforming C, volatile is a necessary
attribute on a variable that is written by an asynchronous signal handler.
The type volatile sig_atomic_t must be used.
I have a two-thread piece of code I've been

The C language doesn't incorporate a threading API. Threads are described
in various competing extensions to the language, such as POSIX and Win32.
Rules about concurrent access to shared data by multiple threads are
defined by those interfaces.

In POSIX, volatile is not required. Shared data is guarded by the use of
a synchronization object, whose use ensures that the memory is stable.

Volatile is a bad idea because (if implemented earnestly) it interferes
with compiler optimizations, So it would be insane for a threading
platform to require it.

When a thread holds a lock over some shared data, it may make many accesses
and stores to that data. Propagation of data changes from one thread to another
are only necessary on entry to and exit from the critical section. Volatile
goes beyond what is required, asking for more memory traffic than necessary.
The sudocode is at the end.

That's ``pseudocode''.

Sudo is a utility for executing commands with superuser privilege.

So ``sudocode'' is perhaps a script executed by sudo. :)
 
S

S James S Stapleton

Thanks, I guess I'm just not sure how my code differs from this in a multi
threaded context. Since another thread may modify the code. Anyway, I'm
continuing this on comp.programming.threading

Thanks for the help.
-Jim Stapleton
 
C

Chris Torek

Is volatile necessary anymore? I have a two-thread piece of code ...

The rules for using variables in threads depends on the thread
implementation, so there is no single answer to this question.
(Threads, as others noted, are not standardized in C. Different
thread implementations thus behave differently.)

As a general rule, though, in code that *implements* threads
(rather than simply *using* those provided by some existing
implementation), "volatile" is necessary but not sufficient.
That is, the thread locking code needs to mark certain things
volatile, but *also* needs to prod the compiler in some special
way(s) to get hardware guarantees that are not provided by
the volatile qualifier. (In some specific cases, the hardware
guarantees *are* already provided, and volatile is sufficient.
In some specific compilers, the optimization is weak, and even
the volatile keyword is not required.)
 
C

CBFalconer

S said:
Is volatile necessary anymore? I have a two-thread piece of code
I've been testing to figure out what volatile does (fairly simple
code, uses pthreads). I have an update thread (variables passed as
volatile) and a print thread (one variable volatile, the other,
not). There is no difference in the behavior of the volatile and
nonvolatile thread.
.... snip monstrosity ...

First, realize what the 'volatile' characteristic says about an
object. It has nothing to do with threads. It does have to do
with how the object can be modified. For example, the volatile
object could be an integer, which is non-zero when an input port
has unread data, and zero after that data has been read. The
actions which set or reset that flag can be external to the
program. If anything reads the port value, the flag is reset. If
you read the 'ready' status, the flag is unaltered. It can only be
set when the external unit has fresh data. It can only be reset by
reading that data.

Remember that the above is only one illustration of a use for
'volatile'.
 
G

GPS

Kaz said:
Yes, in portable, standard-conforming C, volatile is a necessary
attribute on a variable that is written by an asynchronous signal handler.
The type volatile sig_atomic_t must be used.


The C language doesn't incorporate a threading API. Threads are described
in various competing extensions to the language, such as POSIX and Win32.
Rules about concurrent access to shared data by multiple threads are
defined by those interfaces.

In POSIX, volatile is not required. Shared data is guarded by the use of
a synchronization object, whose use ensures that the memory is stable.

Volatile is a bad idea because (if implemented earnestly) it interferes
with compiler optimizations, So it would be insane for a threading
platform to require it.

<offtopic>
Some lockless algorithms use volatile. Microsoft's documentation
suggests it for their XBox.

http://msdn.microsoft.com/en-us/library/bb310595(VS.85).aspx

Some POSIX-based systems use volatile, and the atomic macros for
Linux/BSD sometimes use volatile.

Another thing to keep in mind is that most systems these days do atomic
writes of pointers, and integers, as long as alignment is correct.

The GNU libc for instance depends on this.
http://www.gnu.org/software/libtool/manual/libc/Atomic-Types.html#Atomic-Types
When a thread holds a lock over some shared data, it may make many accesses
and stores to that data. Propagation of data changes from one thread to another
are only necessary on entry to and exit from the critical section. Volatile
goes beyond what is required, asking for more memory traffic than necessary.

volatile is not ideal, but it's sometimes required.

Another danger with lock-free algorithms is reordering of writes. So it
requires a lot of care, but sometimes lock-free is the best solution
when lock contention would otherwise reduce performance.

The sooner we can move from the current popular thread models to
Flow-based programming, or the Plan 9 model, or something like those the
better. The current thread models just don't scale with the current
languages to 500 or 1000 cores.
</offtopic>
 
K

Kenneth Brody

S said:
Is volatile necessary anymore?
[... mega-snip ...]

Yes.

Consider a memory-mapped I/O device:

#define MyIODevice ((volatile char *)0x12345678)
...
void ResetDevice()
{
*MyIODevice = 0x12;
*MyIODevice = 0x34;
}

Without volatile, the compiler can discard the first assignment.

--
+-------------------------+--------------------+-----------------------+
| Kenneth J. Brody | www.hvcomputer.com | #include |
| kenbrody/at\spamcop.net | www.fptech.com | <std_disclaimer.h> |
+-------------------------+--------------------+-----------------------+
Don't e-mail me at: <mailto:[email protected]>
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,954
Messages
2,570,116
Members
46,704
Latest member
BernadineF

Latest Threads

Top