Does the C++ volatile keyword introduce a memory f

2020-01-25 15:32发布

I understand that volatile informs the compiler that the value may be changed, but in order to accomplish this functionality, does the compiler need to introduce a memory fence to make it work?

From my understanding, the sequence of operations on volatile objects cannot be reordered and must be preserved. This seems to imply some memory fences are necessary and that there isn't really a way around this. Am I correct in saying this?


There is an interesting discussion at this related question

Jonathan Wakely writes:

... Accesses to distinct volatile variables cannot be reordered by the compiler as long as they occur in separate full expressions ... right that volatile is useless for thread-safety, but not for the reasons he gives. It's not because the compiler might reorder accesses to volatile objects, but because the CPU might reorder them. Atomic operations and memory barriers prevent the compiler and the CPU from reordering

To which David Schwartz replies in the comments:

... There's no difference, from the point of view of the C++ standard, between the compiler doing something and the compiler emitting instructions that cause the hardware to do something. If the CPU may reorder accesses to volatiles, then the standard doesn't require that their order be preserved. ...

... The C++ standard doesn't make any distinction about what does the reordering. And you can't argue that the CPU can reorder them with no observable effect so that's okay -- the C++ standard defines their order as observable. A compiler is compliant with the C++ standard on a platform if it generates code that makes the platform do what the standard requires. If the standard requires accesses to volatiles not be reordered, then a platform the reorders them isn't compliant. ...

My point is that if the C++ standard prohibits the compiler from reordering accesses to distinct volatiles, on the theory that the order of such accesses is part of the program's observable behavior, then it also requires the compiler to emit code that prohibits the CPU from doing so. The standard does not differentiate between what the compiler does and what the compiler's generate code makes the CPU do.

Which does yield two questions: Is either of them "right"? What do actual implementations really do?

13条回答
Anthone
2楼-- · 2020-01-25 16:33

This is largely from memory, and based on pre-C++11, without threads. But having participated in discussions on threading in the committe, I can say that there was never an intent by the committee that volatile could be used for synchronization between threads. Microsoft proposed it, but the proposal didn't carry.

The key specification of volatile is that access to a volatile represents an "observable behavior", just like IO. In the same way the compiler cannot reorder or remove specific IO, it cannot reorder or remove accesses to a volatile object (or more correctly, accesses through an lvalue expression with volatile qualified type). The original intent of volatile was, in fact, to support memory mapped IO. The "problem" with this, however, is that it is implementation defined what constitutes a "volatile access". And many compilers implement it as if the definition was "an instruction which reads or writes to memory has been executed". Which is a legal, albeit useless definition, if the implementation specifies it. (I've yet to find the actual specification for any compiler.)

Arguably (and it's an argument I accept), this violates the intent of the standard, since unless the hardware recognizes the addresses as memory mapped IO, and inhibits any reordering, etc., you can't even use volatile for memory mapped IO, at least on Sparc or Intel architectures. Never the less, none of the comilers I've looked at (Sun CC, g++ and MSC) do output any fence or membar instructions. (About the time Microsoft proposed extending the rules for volatile, I think some of their compilers implemented their proposal, and did emit fence instructions for volatile accesses. I've not verified what recent compilers do, but it wouldn't surprise me if it depended on some compiler option. The version I checkd—I think it was VS6.0—didn't emit fences, however.)

查看更多
登录 后发表回答