Live data from Hacker News

Ways to break your systems code using volatile (2010)

blog.regehr.org

41–50 of 77 posts

Re: Ways to break your systems code using volatile (2010)

#41

> "Side note: although at first glance this code looks like it fails to account for the case where TCNT1 overflows from 65535 to 0 during the timing run, it actually works properly for all durations between 0 and 65535 ticks." From example 1, ignoring device and setup-specifics what to do when TCNT1 overflows, it actually works properly for all ticks, both "first" and "second" are unsigned (therefore behaviour is def…

Can someone please tell me why this got downvoted? It's particularly annoying when correcting common misconceptions to get penalised for it... (and if what I said was wrong, I'd also like to know why!)

Re: Ways to break your systems code using volatile (2010)

#42
post #13

Err...volatile just tells the compiler not to cache the value in a register, that's it. If you don't understand volatile you really, really are not the kind of programmer who should even think about using it.

This is my understanding of volatile as well: volatile just forces read/write to memory. What a read/write to memory entails is a different story. What happens with no volatile is again another story. If my understanding is wrong, someone please enlighten me.

You are correct. The author is a delusional narcissistic idiot who believes in a C "memory model" or "virtual machine". It bullcrap.

C is just high level assembler, that makes certain optimizations based on certain assumptions. Lots of stuff can break at -O3 at higher.

One usually fairly reasonable assumption is that values will not change between reads and writes. Sometimes they do, hence volatile.

When you write actual assembly this all becomes irrelevant as you can choose where you store variables and how you access them and when you should consider them "stale". And when you understand code at this low level, C and it's volatile becomes rather plain and boring.

There is a LOT of this kind of blogspam on the internet where fools obtain a semi-correct understanding of some low level concepts, then write these long essays about this "dark magic" and making themselves out to be these grandiose elite greybeards hack0rs.

And guess where it rises to the front page to the adoration of semi-sentient wannabe dreamer engineers who coo and ga over how smart he is? You guessed it.

Re: Ways to break your systems code using volatile (2010)

#43

This should say 2010. I believe much of it is out of date, as C11 does have a memory model, and does provide both atomics and barriers. Many, if not most, uses of volatile should probably be replaced by atomics. https://en.cppreference.com/w/c/atomic

Many projects are stuck in C99, or even C89...

Re: Ways to break your systems code using volatile (2010)

#44

> "Side note: although at first glance this code looks like it fails to account for the case where TCNT1 overflows from 65535 to 0 during the timing run, it actually works properly for all durations between 0 and 65535 ticks." From example 1, ignoring device and setup-specifics what to do when TCNT1 overflows, it actually works properly for all ticks, both "first" and "second" are unsigned (therefore behaviour is def…

Can someone please tell me why this got downvoted? It's particularly annoying when correcting common misconceptions to get penalised for it... (and if what I said was wrong, I'd also like to know why!)

Probably because the article already says it works for all ticks...

Re: Ways to break your systems code using volatile (2010)

#45

Earlier quoted context omitted.

Can someone please tell me why this got downvoted? It's particularly annoying when correcting common misconceptions to get penalised for it... (and if what I said was wrong, I'd also like to know why!)

Probably because the article already says it works for all ticks...

Yes, upon rereading I see that, too. When I first read it, I understood in such a way that it only would work until (a timestamp of) 65535 ticks since device-startup had passed, but he was referring to durations of that length.

Thank you for clarifying.

Re: Ways to break your systems code using volatile (2010)

#46

> "Side note: although at first glance this code looks like it fails to account for the case where TCNT1 overflows from 65535 to 0 during the timing run, it actually works properly for all durations between 0 and 65535 ticks." From example 1, ignoring device and setup-specifics what to do when TCNT1 overflows, it actually works properly for all ticks, both "first" and "second" are unsigned (therefore behaviour is def…

But if the duration is > 65535 ticks, the calculated duration will be wrong, no? There is no mechanism to count how many times TCNT1 overflows, so it will be incorrect if the duration of what you are timing exceeds 65535 ticks.

Re: Ways to break your systems code using volatile (2010)

#47
post #7

Edit: Looks like the slides had an inaccuracy (see replies). Huh, looks like I learned something today :) I think a good way of summarizing volatile is this slide from my parallel architectures class [1]: > Class exercise: describe everything that might occur during the > execution of this statement > volatile int x = 10 > > 1. Write to memory > > Now describe everything that might occur during the execution of > thi…

I don't get this, most likely due to my ignorance, but I thought volatile doesn't necessarily force anything to RAM, it can just push it out so cache coherence handles the rest, between cores (and perhaps peripherals). MESI can do the work without actually hitting memory. if you want to force actually to ram then perhaps you'd need a memory barrier. This is not my area though. Wrong? Right?

What happens with this code?

    volatile int x;
    int          y;
    int          z;
    
    x = 10;
    x = 20;
    y = x;
    z = x;
Answer:

    the constant 10 is written to x
    the constant 20 is written to x
    the contents of x is read and written into y
    the contents of x is read and written into z
Now, what happens with this code?

    int x;
    int y;
    int z;
    
    x = 10;
    x = 20;
    y = x;
    z = x;
One answer is the same as the above. Another valid answer is:

    the constant 20 is written to x
    the constant 20 is written to y
    the constant 20 is written to z
Why? Because x is not used between the two assignments, so the first will never be seen. Also, x is not used between it's assignment and the assignment to y, so the compiler can do constant propagation.

All volatile does it tell the compiler "all writes must happen, and no caching of reads".

Re: Ways to break your systems code using volatile (2010)

#48
post #46

> "Side note: although at first glance this code looks like it fails to account for the case where TCNT1 overflows from 65535 to 0 during the timing run, it actually works properly for all durations between 0 and 65535 ticks." From example 1, ignoring device and setup-specifics what to do when TCNT1 overflows, it actually works properly for all ticks, both "first" and "second" are unsigned (therefore behaviour is def…

But if the duration is > 65535 ticks, the calculated duration will be wrong, no? There is no mechanism to count how many times TCNT1 overflows, so it will be incorrect if the duration of what you are timing exceeds 65535 ticks.

That is correct, yes. I had erroneously understood that the author meant "all durations between 0 and 65535 ticks" as "any duration between the device's 0th and 65535th tick", my bad... Also makes this entire thread obsolete, but FWIW, one shouldn't be attempting to measure a duration that can't even be contained in the variable's bit width. Some workarounds would be to add more bits, slow down the tickrate or add overflow counters.

Re: Ways to break your systems code using volatile (2010)

#49
post #28

Earlier quoted context omitted.

Yep. The slide is completely wrong. It is showing low-level architecture details that would be 100% identical between the two cases. Volatile changes nothing on that list. Volatile just makes sure the compiler bothers. Otherwise, a pair of writes to the same memory location could be optimized by eliminating the first write. Volatile makes the compiler do that. Of course, the CPU itself may then do this optimization,…

> It is showing low-level architecture details that would be 100% identical between the two cases. To be as charitable as I can possibly be, the only part that could theoretically make sense is that the compiler could emit non-temporal store instructions to bypass the cache. I know compilers currently don't do that for volatile, but I don't know why.

I think the reason is the details are too complicated to be captured by the volatile keyword.

For instance the processor I use has a controller that enforces consistency on IO memory operations. So volatile works 'fine'. I know that. The compiler is targeting a core not an implementation. So it has no idea.

Re: Ways to break your systems code using volatile (2010)

#50
post #47

Earlier quoted context omitted.

I don't get this, most likely due to my ignorance, but I thought volatile doesn't necessarily force anything to RAM, it can just push it out so cache coherence handles the rest, between cores (and perhaps peripherals). MESI can do the work without actually hitting memory. if you want to force actually to ram then perhaps you'd need a memory barrier. This is not my area though. Wrong? Right?

What happens with this code? volatile int x; int y; int z; x = 10; x = 20; y = x; z = x; Answer: the constant 10 is written to x the constant 20 is written to x the contents of x is read and written into y the contents of x is read and written into z Now, what happens with this code? int x; int y; int z; x = 10; x = 20; y = x; z = x; One answer is the same as the above. Another valid answer is: the constant 20 is wri…

Understood but we're talking about different things I think (though this is very much not my area).

You're saying volatile is acting as a kind of memory barrier instruction for the compiler - got it. But I'm saying I understand that at the CPU level, just considering x86 instructions, writes don't have to be forced to RAM, despite a common assumption that they are; they can remain in caches. See johntb86's reply confirming this.

Post reply on HN