Should ALL global variables be volatile-qualified?

北城以北 提交于 2019-11-30 09:10:01

No, the volatile keyword is not necessary here. Since global_value is visible outside the function bar, the compiler must not assume that it remains the same if another function is called.

[Update 2011-07-28] I found a nice citation that proves it all. It's in ISO C99, 5.1.2.3p2, which I am too lazy to copy here in its entirety. It says:

At certain specified points in the execution sequence called sequence points, all side effects of previous evaluations shall be complete and no side effects of subsequent evaluations shall have taken place.

Sequence points include:

  • The call to a function, after the arguments have been evaluated (6.5.2.2).
  • The end of a full expression: [...] the expression in an expression statement (6.8.3); [...]

There you have your proof.

The only uses of volatile involve longjmp, signal handlers, memory-mapped device drivers, and writing your own low-level multi-threaded synchronization primitives. For this last use however, volatile is not sufficient and may not even be necessary. You'll definitely also need asm (or compiler-specific or C1x atomics) for synchronization.

volatile is not useful for any other purposes, including the code you asked about.

As Roland says, I'm not sure what part of the standard to cite to say, "if a program modifies something, that means the object is modified in the abstract machine. If a program uses a value, that means it uses whatever value the object has in the abstract machine".

volatile controls the number and order of reads and writes to memory, but even without volatile, an implementation that caches values as an optimization must respect the behavior of the abstract machine. That's what the "as-if" rule says, so optimizations that don't obey that aren't "easy to imagine" for me ;-) Your proposed emitted code is as clearly wrong to me as saying, "a write might go to memory without updating or dirtying the L1 cache, so future reads will still see the old value in the cache". Not on a single core, it won't, because a cache that behaved like that would be broken.

If you call strcpy, and then examine the contents of the destination buffer, the compiler isn't allowed to "optimize" by using a prior value of that byte, stored in a register. strcpy doesn't take a volatile char *. Similarly, global_value does not need to be volatile.

I suppose the confusion may be that in multi-threaded code, "and then", which is to say whether the read occurs "after" the write and hence "sees" the new value, is defined by synchronization primitives. In some implementations, volatile has something to do with synchronization due to implementation-specific guarantees.

In single-threaded code, and in the C and C++ standards, "and then" is defined by sequence points, of which there are plenty in the code given.

No. Global variables should not always be declared volatile.

You only really need it to be volatile if it could be changed by other threads and may suffer from memory reordering issues or compiler instruction reordering. And even then you won't need it if you have appropriate mutexing. Typically though, you probably have a bad design if you need to mutex global variables.

EDIT: making it volatile does not mean that the global variable would be thread safe though!

Other typical uses might be where the memory is accessed in an unusual way - for example if you have some DMA mapped memory on an embedded micro.

Volatile isn't needed in this example. If, for instance, some_function() outputs something, the asm listing seems changes observable behaviour of the c++ machine and violates the standard.

I guess it is a compiler bug Here is GCC assembler output:

.cfi_def_cfa_register 5
subl    $24, %esp
.loc 1 67 0
movl    global_value, %eax
addl    $1, %eax
movl    %eax, global_value
movl    global_value, %eax
movl    %eax, (%esp)
call    _Z13some_functioni
.loc 1 68 0
call    _Z3foov
.loc 1 69 0
movl    global_value, %eax
addl    $1, %eax
movl    %eax, global_value
movl    global_value, %eax
movl    %eax, (%esp)
call    _Z13some_functioni
.loc 1 70 0
leave
.cfi_restore 5

global_value is reloaded, as expected, between function calls

Also volatiles are for thread-safety too, simply v-qualifier is not sufficient for thread safety in all cases (you sometimes need additional care about atomicity and memory barriers, but interthread communication variables should be volatile...

[EDITED]: ... if they are repeatedly read and may be changed by another thread between reads. This, however, is not the case if any syncronization lock (mutex, etc) is used, since lock guarantees the variables can not be changed by any concurrent activity) (thanks to R..)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!