5 ms·
> [Using "volatile" should make stores and loads happen where they are written in the code...] Really? I was under the impression that "volatile" basically did
by Unklejoe 10y ago
> [Using "volatile" should make stores and loads happen where they are written in the code...]
Really? I was under the impression that "volatile" basically did nothing other than prevent the compiler from optimizing out a variable that appears to not ever be written to, but is actually written to via an ISR or something.
Also, how does the compiler usually handle global variables? Since they can be modified by some code that was linked in, does it assume them to be volatile?
Of course, this issue is slightly different than the atomic-ness of certain data types. We're talking more about situations where you have a series of atomic operations which you expect to happen in the order of which they're written.
- exDM69 10y ago> Really? I was under the impression that "volatile" basically did nothing other than prevent the compiler from optimizing out a variable that appears to not ever be written to, but is actually written to via an ISR or something. I think it also stops loads and stores to be reordered with other volatile loads and stores, and yes, it indeed is typically useful for memory mapped i/o, interrupt handlers and such. But most of the time it's not the right thing to do. > Also, how does the compiler usually handle global variables? Since they can be modified by some code that was linked in, does it assume them to be volatile? No, they're not considered volatile but may have stricter guarantees with reordering than local variables (esp. when combined with calls to foreign functions). If multithreaded code needs to be correct and portable, using locks or atomics is the way to go.
- vardump 10y ago> I think it also stops loads and stores to be reordered with other volatile loads and stores, and yes, it indeed is typically useful for memory mapped i/o, interrupt handlers and such. But most of the time it's not the right thing to do. It only prevents reordering by the compiler. CPU can still reorder them as much as it pleases, volatile doesn't affect it at all. Of course you're pretty safe on x86. Not so on other platforms. Typically you find this kind of issues when porting x86 code to some other platform. X86 writes are always in order with other writes. Loads are always in order to other loads. Just need to remember loads are sometimes ordered before stores. Again, volatile does not prevent CPU from reordering stores and loads.
- Dylan16807 10y agoIf the compiler isn't preventing CPU reordering, then it has been impossible pre-2011 to use volatile for its original purpose, communicating with memory-mapped device registers. That sounds like a broken compiler to me.
- vardump 10y agoYeah, it'd not be very nice when DMA start bit in some DMA control register is poked before DMA address is still unset! Those architectures that do this typically provide specialized always serialized instructions for communicating with memory mapped devices. If not, you need to have proper fences to ensure right order. X86 stores are always in order (except for some specific instructions, but they're no concern in this context), so it doesn't need this.
- makomk 10y agoMemory-mapped device registers are generally mapped in such a way that accesses bypass cache and go straight to memory. This has a fairly large performance penalty, of course.
- vvanders 10y agoYup. Don't. Use. Volatile. It's meant for reading from hardware registers and nothing else. There's nothing to prevent the re-ordering of reads/code around your volatile block. This is doubly nasty in that Windows will insert a memory fence which makes everything look like you'd want it. Soon as you port to another platform, boom all sorts of concurrency issues. Seriously, use atomics. That's what they're built for.
- vardump 10y ago> This is doubly nasty in that Windows will insert a memory fence Windows is not inserting any memory fences anywhere in your code. MSVC cl compiler will also only do so when you specifically ask it to do so by using an intrinsic.
- vvanders 10y ago> Visual Studio interprets the volatile keyword differently depending on the target architecture. ... For architectures other than ARM, if no /volatile compiler option is specified, the compiler performs as if /volatile:ms were specified; therefore, for architectures other than ARM we strongly recommend that you specify /volatile:iso, and use explicit synchronization primitives and compiler intrinsics when you are dealing with memory that is shared across threads. > /volatile:ms > Selects Microsoft extended volatile semantics, which add memory ordering guarantees beyond the ISO-standard C++ language. Acquire/release semantics are guaranteed on volatile accesses. However, this option also forces the compiler to generate hardware memory barriers, which might add significant overhead on ARM and other weak memory-ordering architectures. If the compiler targets any platform except ARM, this is default interpretation of volatile. From https://msdn.microsoft.com/en-us/library/12a04hfd.aspx https://msdn.microsoft.com/en-us/library/12a04hfd.aspx Really, just don't use volatile.
- vardump 10y agoI think I really need to read disassembler output of any code I compile with cl. Ugh. I haven't seen it doing that on any of my code using volatile, mostly for CAS and fetch-and-add (lock xadd).