5

I use some global structures in a multithreaded program, some of the members are modified by multiple threads simultaneously, some others are not.

I didn't define any of this members volatile, but anytime i use this members for both reading and writing purposes, i use atomic builtins such as __sync_fetch_and_add.

The question is, should i define this members or the whole struct volatile?

I think the compiler has to access the memory instead any register because of this builtins (lock prefix), should i be worried about other members that are not cause race conditions.

I checked out the assembly output of my compiler (gcc 4.6.2) and it seems my assumption is right.

Here is the test code.

int sum = 0;

for (i=0; i<2000000000; i++) {
    sum += i;
}

The assembly output (-O2 -S -masm=intel)

L2:
    add edx, eax
    inc eax
    cmp eax, 2000000000
    jne L2

So compiler is never accessed the memory (eax = i, edx = sum)

Here is the second test code.

volatile int sum = 0;

for (i=0; i<2000000000; i++) {
    sum += i;
}

The assembly output

L2:
    mov edx, DWORD PTR [esp+28]
    add edx, eax
    mov DWORD PTR [esp+28], edx
    inc eax
    cmp eax, 2000000000
    jne L2

Compiler accessed the memory for sum everytime as expected.

The final code, my way.

int sum = 0;

for (i=0; i<2000000000; i++) {
    __sync_fetch_and_add(&sum , i);
}

The assembly output.

L2:
    lock add    DWORD PTR [esp+28], eax
    inc eax
    cmp eax, 2000000000
    jne L2

Not even a temp register as before (edx), compiler accessed the memory everytime.

So, i don't define volatile any member that modified by multiple threads or modified by only one thread at a time. Am i safe?

Thanks in advance.

Kurt
  • 53
  • 1
  • 7
  • Volatile is best avoided in most cases -- it doesn't have anything to do with multi-threading. Use explicit barriers instead. – Cory Nelson Feb 03 '13 at 19:00
  • I think it does. If i use a global variable in a loop at thread1 and compiler uses a register instead of memory access as i show, thread2 can modify this variable in memory and the result is mess. – Kurt Feb 03 '13 at 19:07
  • 2
    It absolutely doesn't! [See this answer](http://stackoverflow.com/questions/6866206/volatile-and-createthread/6866927#6866927) for an explanation. Note, GCC's _sync functions operate as a barrier themselves. – Cory Nelson Feb 03 '13 at 19:46
  • I think you didnt understand my previous comment. Read this article written by Andrei Alexandrescu. http://www.drdobbs.com/cpp/volatile-the-multithreaded-programmers-b/184403766 so it says, Although both C and C++ Standards are conspicuously silent when it comes to threads, they do make a little concession to multithreading, in the form of the volatile keyword. – Kurt Feb 03 '13 at 19:56
  • C11 has lots of new threading features. It's definitely not silent on the issue! – Carl Norum Feb 03 '13 at 20:15
  • That's right, actually it's going to be better, there is a study group (SG1) works on concurrency and the next minor standart is coming (2014). But my question is on C. I give this article to convince Nelson that volatile has something to do with multithreading. Even though the code that i modify global variable is thread safe (by mutex or something), compiler can change the register instead of the memory, and another thread can read a different value from memory of this variable. Volatile is important in that case. But since __sync functions are always modify the memory, i am safe. – Kurt Feb 03 '13 at 20:33
  • Respect to Andrei, but that article was written during a time when very few understood standard `volatile`'s lack of usefulness for threading -- his first example (`Wakeup`) assuming non-standard VC++ behavior shows that he was likely as clueless as everyone else. – Cory Nelson Feb 04 '13 at 06:06

1 Answers1

4

Yes, you are safe. The documentation does not say they should be volatile, so they shouldn't.

The __sync* functions act as memory barriers as appropriate, so volatile would be superfluous. And it wouldn't allow using anything else beyond the __sync* functions anyway (only the __sync* functions generate the lock prefix).

Note: The __sync* functions are deprecated in gcc 4.7 in favour of C++11-style __atomic* types, but gcc 4.6 does not have those yet.

Jan Hudec
  • 73,652
  • 13
  • 125
  • 172