Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Threads programming: do I need to use "volatile"?

55 views
Skip to first unread message

Arndt Schoenewald

unread,
Dec 18, 1996, 3:00:00 AM12/18/96
to

Hello MT users,

here is an interesting question that just came to my mind: do I
have to use the "volatile" keyword to tag variables which are
accessed by more than one thread?

Say I there's a tread waiting for a flag to eventually have its
value chaged by another thread. The code for the waiter would look
something like this:

pthread_mutex_lock(&mutex);

while (flag == 0)
pthread_cond_wait(&cond, &mutex);

printf("flag has been set\n");

Now, do I need to declare `flag' as volatile to keep the optimizer
from changing the code to

pthread_mutex_lock(&mutex);

if (flag == 0)
{
for (;;)
pthread_cond_wait(&cond, &mutex);
}

printf("flag has been set\n");

I have compiled and run the test code (included below) on Solaris
2.5 x86 using SunSoft C++ 4.0.1 with optimizations up to -O4 and
with gcc 2.7.2 and optimizations up to -O3, and the results suggest
that "volatile" is not necessary, but this may just be due to the
simplicity of my test case.

In this case, it doesn't seem worth arguing whether "volatile" is
needed or not; if in doubt, we just add it and forget about it.
But I maintain some big C++ programs with classes supporting
reentrant access and I am now worried whether I need to spread
the "volatile" tag all over my data members.

Is there anybody who can give an authoritative answer? Hey Bart,
this is something you forgot to mention in your book ;-)

Cheers,
Arndt

=== snip: complete test example follows ===========================

/* gcc -D_REENTRANT -O3 thrwait.c -o thrwait -lpthread */
/* CC -mt -O4 thrwait.c -o thrwait */

#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

pthread_mutex_t mutex; /* mutex and cond-var guarding flag */
pthread_cond_t cond;
int flag = 0; /* flag to be set by set_flag thread */

void err_exit(int rc, const char *msg)
{
if (rc)
{
errno = rc;
perror(msg);
}
else
fprintf(stderr, "%s\n", msg);

exit(rc ? 1 : 0);
}

void *set_flag(void *time)
{
int rc;
sleep((int) time);

if ((rc = pthread_mutex_lock(&mutex)))
err_exit(rc, "set_flag: pthread_mutex_lock");

flag = 1;
if ((rc = pthread_cond_signal(&cond)))
err_exit(rc, "set_flag: pthread_cond_signal");

if ((rc = pthread_mutex_unlock(&mutex)))
err_exit(rc, "set_flag: pthread_mutex_unlock");

return 0;
}

int main(int argc, char **argv)
{
pthread_attr_t attr;
pthread_t id;
int rc;

int wait_time = (argc == 2) ? atoi(argv[1]) : 5;

/*
* start set_flag() as a detached thread
*/
pthread_attr_init(&attr);
if ((rc = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)))
err_exit(rc, "main: pthread_attr_setdetachstate");

if ((rc = pthread_create(&id, &attr, set_flag, (void *) wait_time)))
err_exit(rc, "main: pthread_create");

/*
* wait for flag to be set
*/
if ((rc = pthread_mutex_lock(&mutex)))
err_exit(rc, "main: pthread_mutex_lock");

while (flag == 0)
{
if ((rc = pthread_cond_wait(&cond, &mutex)) && rc != EINTR)
err_exit(rc, "main: pthread_cond_wait");
}

printf("main: flag has been set by set_flag thread\n");
return 0;
}

--
Arndt Schoenewald (ar...@ais-gmbh.de)
Ostenhellweg 31, 44135 Dortmund, Germany
Tel: +49 231 556075
Fax: +49 231 556049

Bart Smaalders

unread,
Dec 19, 1996, 3:00:00 AM12/19/96
to Arndt Schoenewald

Actually, you don't need to use the volatile keyword because
you're calling the pthread routines around the access, and the
compiler has no way of knowing what those routines do. The flag
variable has to have at least static scope for the code to work, and
so the compiler cannot assume that flag isn't modified as the
result of a call to pthread_mutex_lock.

Where volatile is essential is when:

* you're modifying a flag from an signal handler and the flag is
checked in a loop w/o function calls.

eg

static volatile int terminate;
alarm_handler()
{
terminate = 1;
}

calculate_pi()
{
while (!terminate) {
/* do a bunch of math ops */
}
return(answer_so_far);
}

* you're dealing with device registers; eg spinning on a memory
location waiting for the register to indicate completion, for
example:

#include <sys/cg6fbc.h>
volatile struct fbc cg6; /* assumed to reference mmaped device */

... while (fbc.l_fbc_status & L_FBC_BUSY)
;


* you're writing your own locking primitives w/o function calls
and you have multiple threads active (but watch out; Dekker's
algorithm doesn't work on MPs with store buffering....)

So, unless your project fits in one of the above cases, you
can avoid volatile.

- Bart

--
Bart Smaalders Solaris Clustering SunSoft
ba...@cyber.eng.sun.com (415) 786-5335 MS UMPK17-301
http://playground.sun.com/~barts 2550 Garcia Ave
Mt View, CA 94043-1100

0 new messages