3

When writing C programs that need to share a file scope variable between the application and an interrupt routine/a thread/a callback routine, it is well-known that the variable must be declared volatile, or else the compiler may do incorrect optimizations. This is an example of what I mean:

int flag;

void some_interrupt (void)
{
  flag = 1;
}



int main()
{
  flag = 0;
  ...

  /* <-- interrupt occurs here */

  x = flag; /* BUG: the compiler doesn't realize that "flag" was changed 
                    and sets x to 0 even though flag==1 */

}

To prevent the above bug, "flag" should have been declared as volatile.

My question is: how does this apply to C++ when creating a class containing a thread?

I have a class looking something like this:

class My_thread
{
  private:
    int flag;

    static void thread_func (void* some_arg) // thread callback function
    {
      My_thread* this_ptr= (My_thread*)some_arg;

    }
};

"some_arg" will contain a pointer to an instance of the class, so that each object of "My_thread" has its own thread. Through this pointer it will access member variables.

Does this mean that "this_ptr" must be declared as pointer-to-volatile data? Must "flag" be volatile as well? And if so, must I make all member functions that modify "flag" volatile?

I'm not interested in how a particular OS or compiler behaves, I am looking for a generic, completely portable solution.

EDIT: This question has nothing to do with thread-safety whatsoever!

The real code will have semaphores etc.

To clarify, I wish to avoid bugs caused by the compiler's unawareness of that a callback function may be called from sources outside the program itself, and therefore make incorrect conclusions about whether certain variables have been used or not. I know how to do this in C, as illustrated with the first example, but not in C++.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • 19
    Well-known, perhaps, but wrong. `volatile` won't make operations atomic and likely won't do anything to help with multi-threading. – Fred Larson Feb 24 '11 at 16:11
  • 2
    Too true, volatile wouldn't help here. If it does, it would be compiler-specific. Have you considered using the Boost implementation? http://www.boost.org/doc/libs/1_42_0/doc/html/thread.html – 0xC0000022L Feb 24 '11 at 16:13
  • 2
    `volatile` would *sort of* solve the problem, but it's still the wrong solution. You need a memory barrier, not `volatile`. (and you may also need to use the CPU's atomic instructions for setting/testing the flag depending on platform) – jalf Feb 24 '11 at 16:17
  • 1
    What do you mean when saying you might need to declare `this_ptr` as "pointer-to-volatile data"? You want pointer to `My_thread` object and therefore declare it as `My_thread*`. Making class members `volatile` will not make them thread-safe. You need to use events and/or mutexes to achieve that. This question is interesting: http://stackoverflow.com/questions/2484980/why-is-volatile-not-considered-useful-in-multithreaded-c-or-c-programming – Bojan Komazec Feb 24 '11 at 16:24
  • 2
    Since neither the current C nor C++ standard have standard threading libraries, you can't write generic and completely portable threading code. The closest you're going to get until C++0x is widely used is Boost::Thread. – David Thornley Feb 24 '11 at 18:14
  • This question has **nothing** to do with thread-safety or atomic instructions. Your comments suggest that you didn't even read my question. – Lundin Feb 25 '11 at 07:56
  • Edited the question to clarify what I am looking for. – Lundin Feb 25 '11 at 08:17
  • What people are (I think) trying to say is that volatile might be requried in order to ensure that the generated code actually writes data to memory rather than keep it in register or optimize it out - but it's not *sufficient* because on a multicore machine you need a memory barrier to ensure that different cores have the same view of memory and there is no portable way to do that in c++. On some compilers volatile will do this, and on x86 it will likely work anyway as the hardware tends to ensure it but it's not portable. – jcoder Feb 25 '11 at 09:13
  • @JohnB Memory barriers will of course be platform-dependent, just like mutecies/semaphores. While order-of-execution might be an issue as well, my question is about how I should do, in a portable way, to prevent the compiler from making naive assumptions about whether a callback function was called or not. – Lundin Feb 25 '11 at 10:08

4 Answers4

2

Well, that edit makes all the difference of the world. Semaphores introduce memory barriers. Those make volatile redundant. The compiler will always reload int flag after any operation on a semaphore.

Fred Larson already predicted this. volatile is insufficient in the absence of locks, and redudant in the presence of locks. That makes it useless for thread-safe programming.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • I don't quite see how memory barries have anything to do with this? They are there to prevent bugs caused by unexpected order-of-execution, correct? The bug I am worried about is when the compiler does not even realize that the callback function has been executed. – Lundin Feb 25 '11 at 10:14
  • 2
    @Lundin: No, memory barriers also force the compiler and CPU to store any dirty cached value (write barrier) and reload any unmodified cached value (read barrier). In combination, they make sure that `flag` is written to memory and read back from memory. – MSalters Feb 25 '11 at 12:00
  • Ok I see. But how do I know that memory barriers will actually do this for me? There is no standard for how they should be implemented. – Lundin Feb 25 '11 at 14:01
  • @Lundin: "Memory barriers" in my explanation is a descriptive term - they're the behind-the-scenes mechanism in higher-level multithreading operations. Arguably, you don't need a standard for them as long as there's a standard for those higher level operations. But see also [this previous question](http://stackoverflow.com/questions/3851398/memory-fences-barriers-in-c-does-boost-or-other-libraries-have-them) for more information – MSalters Feb 28 '11 at 09:33
  • +1 for memory barriers. We need to make more people learn this term. – Shahbaz Apr 17 '12 at 09:34
0

From the function pointer signature I guess you are using the posix thread implementation for threads. I assume you want to know how to start off a thread using this API. First consider using boost thread instead. If not an option, I usually go for something like the following to get somewhat of that cosy Java readability.

class Runnable {
    public:
        virtual void run() = 0;
};

class Thread : public Runnable {
    public:
        Thread();
        Thread(Runnable *r);

        void start();
        void join();        

        pthread_t getPthread() const;

    private:

        static void *start_routine(void *object);

        Runnable *runner;
        pthread_t thread;
};

And then something like this in the start_routine function:

void* Thread::start_routine(void *object) {
    Runnable *o = (Runnable *)object;

    o->run();

    pthread_exit(NULL);
    return NULL;
}

Now access to fields of classes extending the Runnable or Thread class need not be volatile since they are thread-local.

That said, sharing data between threads is more complex than using a volatile data member unfortunately if that is what you asked...

sheepez
  • 986
  • 1
  • 10
  • 26
vidstige
  • 12,492
  • 9
  • 66
  • 110
  • How will this help against bugs caused by the compiler's unawareness of that a thread callback function has been executed? And nope, I'm actually using Windows CreateThreads, but my question is of generic nature. – Lundin Feb 25 '11 at 08:03
  • I guess my point is that data members only accessed by _one_ thread need not be volatile, and when a data member is accessed by two or more threads volatile won't help on it's own. – vidstige Feb 25 '11 at 12:03
0

Some implementation of the fallback mechanism is given here for both Windows and Linux. Try this example:

typeReadFileCallback varCallback;

I was able to implement using that.

JasonMArcher
  • 14,195
  • 22
  • 56
  • 52
0

Read this article by Andrei Alexandrescu over at Dr. Dobbs, it might be relevant:

volatile - Multithreaded Programmer's Best Friend

From the intro to the article:

The volatile keyword was devised to prevent compiler optimizations that might render code incorrect in the presence of certain asynchronous events. For example, if you declare a primitive variable as volatile, the compiler is not permitted to cache it in a register -- a common optimization that would be disastrous if that variable were shared among multiple threads. So the general rule is, if you have variables of primitive type that must be shared among multiple threads, declare those variables volatile. But you can actually do a lot more with this keyword: you can use it to catch code that is not thread safe, and you can do so at compile time. This article shows how it is done; the solution involves a simple smart pointer that also makes it easy to serialize critical sections of code.

Robert S. Barnes
  • 39,711
  • 30
  • 131
  • 179