Martin
Martin

Reputation: 301

Using a variable marked volatile, instead of mutex protected

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

Answers (2)

Grady Player
Grady Player

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

ecatmur
ecatmur

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

Related Questions