Originally Posted by
bithub
The volatile keyword is only used on variables which can change outside of the scope of the application. In this case, the variable terminated is only shared among threads, thus volatile is not needed. The optimization you outlined would never take place with a global variable.
I did a search and this seems to be a controversial topic. My conclusions from usenet posts:
- The C standard does not deal with threads so gives no guidance about the use of volatile in this situation.
- Behaviour is compiler specific.
In that vain, I did some testing on my two installed compilers, MSVC.NET and GCC (Dev C++/MinGW). The results are reproduced below.
Code
The volatile keyword was included or taken out as noted.
Code:
#include <windows.h>
volatile int i;
DWORD CALLBACK ThreadA(LPVOID lparam)
{
int j = 0;
while (i == 1)
{
j++;
}
return 0;
}
DWORD CALLBACK ThreadB(LPVOID lparam)
{
int j = 0;
while (i == 2)
{
j++;
}
return 0;
}
DWORD CALLBACK ThreadC(LPVOID lparam)
{
int j = 0;
while (j++ < 10000) ;
i = 2;
return 0;
}
int main(void)
{
HANDLE hThread = CreateThread(NULL, 0, ThreadA, 0, 0, NULL);
CloseHandle(hThread);
hThread = CreateThread(NULL, 0, ThreadC, 0, 0, NULL);
CloseHandle(hThread);
ThreadB(NULL);
return 0;
}
VC using /O1, no volatile
This does the comparison once and goes into an infinite loop if it matches.
Code:
_ThreadA@4 PROC NEAR ; COMDAT
; 8 : int j = 0;
; 9 :
; 10 : while (i == 1)
cmp DWORD PTR _i, 1
jne SHORT $L74000
$L73999:
jmp SHORT $L73999
$L74000:
; 11 : {
; 12 : j++;
; 13 : }
; 14 :
; 15 : return 0;
xor eax, eax
; 16 : }
ret 4
VC using /O2, no volatile
This does the comparison once and goes into an infinite loop if it matches.
Code:
_ThreadA@4 PROC NEAR ; COMDAT
; 8 : int j = 0;
; 9 :
; 10 : while (i == 1)
cmp DWORD PTR _i, 1
jne SHORT $L74081
$L74048:
jmp SHORT $L74048
$L74081:
; 11 : {
; 12 : j++;
; 13 : }
; 14 :
; 15 : return 0;
xor eax, eax
; 16 : }
ret 4
VC using /O1, with volatile
This does the comparison every loop iteration
Code:
_ThreadA@4 PROC NEAR ; COMDAT
; 7 : {
$L74032:
; 8 : int j = 0;
; 9 :
; 10 : while (i == 1)
cmp DWORD PTR _i, 1
je SHORT $L74032
; 11 : {
; 12 : j++;
; 13 : }
; 14 :
; 15 : return 0;
xor eax, eax
; 16 : }
ret 4
VC using /O2, with volatile
This does the comparison every loop iteration
Code:
_ThreadA@4 PROC NEAR ; COMDAT
; 8 : int j = 0;
; 9 :
; 10 : while (i == 1)
mov ecx, DWORD PTR _i
mov eax, 1
cmp ecx, eax
jne SHORT $L74081
$L74048:
cmp DWORD PTR _i, eax
je SHORT $L74048
$L74081:
; 11 : {
; 12 : j++;
; 13 : }
; 14 :
; 15 : return 0;
xor eax, eax
; 16 : }
ret 4
GCC using /O2, no volatile
This does the comparison once and goes into an infinite loop if it matches.
Code:
_ThreadB@4:
pushl %ebp
movl _i, %eax // Next 4 lines: if (i == 2) goto L12;
movl %esp, %ebp
cmpl $2, %eax
je L12
L14:
xorl %eax, %eax // Next 3 lines: return 0;
popl %ebp
ret $4
L12:
je L12
jmp L14 // This is never reached
GCC using /O2, with volatile
This does the comparison every loop iteration
Code:
_ThreadB@4:
pushl %ebp
movl _i, %eax // Next 4 lines: if (i == 2) goto L12;
movl %esp, %ebp
cmpl $2, %eax
je L12
L14:
xorl %eax, %eax // Next 3 lines: return 0;
popl %ebp
ret $4
L12:
movl _i, %eax // Next 3 lines: if (i == 2) goto L12;
cmpl $2, %eax
je L12
jmp L14 // Next 1 lines: goto L14;
Conclusion
Although there is no guarantee that volatile will help when using a global variable accross threads, this testing clearly suggests that it may be required when using MSVC or MinGW and it is probably a good idea to use it.