Reputation: 301
Consider the following example code snippet:
void thread_function(void *);
volatile int running = 0;
pthread_t myThread;
int main () {
running =1;
pthread_create(myThread, NULL, (void *)thread_function, NULL);
/* do some stuff */
/* exit condition met */
running = 0;
pthread_join(&myThread);
/* clean up */
return 0;
}
void thread_function(void *)
{
while(running) {
/* do stuff */
}
}
I have read lots of posts regarding the volatile keyword and how it should not be used in pretty much all but memory mapped IO but, in this particular circumstance, I cannot understand how it would not perform as expected? Yes, I may get one extra iteration of the while loop (if I'm unlucky enough to get a context switch while I'm setting the 'running' variable to zero), but that doesn't matter.
Is there a good reason why I should mutex protect this variable? Is there something I've overlooked?
Upvotes: 2
Views: 559
Reputation: 14549
if running
is volatile
... and changed on another core, you will get the updated version of running
... it may not be the next instruction, but a memory barrier will eventually happen and you will get the valid value.
So it isn't safe if you need to guarantee the order of execution, but it will work in a modern multithreaded OS for non-critical operations, like locking a UI element, or sending some network api call.
I have tried using OS X to even make the volatile
even matter, and I can never get the change to not be seen:
#import <pthread.h>
bool flag;
void * secondThread(void *);
int main(int argc, const char * argv[])
{
flag = true;
@autoreleasepool
{
pthread_t thread;
int status = pthread_create(&thread, NULL, secondThread, NULL);
if (status)
{
printf("problem with pthread_create %i",errno);
}
sleep(4);
flag = false;
}
return 0;
}
void * secondThread(void * arg)
{
while (flag)
{
puts("sleeping\n");
sleep(1);
}
return NULL;
}
running with -Ofast
I get:
sleeping
sleeping
sleeping
sleeping
my conclusion (and maybe a bad one) is that I include volatile
for semantic correctness more than I do to actually change the way the program executes.
If you are running an embedded system or RTOS I suspect it may be more important.
Upvotes: 0
Reputation: 157314
As written, your program is safe, because pthread_create
and pthread_join
are memory barriers. However, as soon as you fill in /* do some stuff */
you will find that writes to running
are reordered relative to other data shared between the threads; in this case, this can result in myThread
terminating earlier than expected.
Use a condition variable, or atomic_bool
. Safe to use volatile bool to force another thread to wait? (C++)
Upvotes: 1