8 ms·
And it makes sense as long as you allow the concept of unsequenced operations at all (admittedly it’s somewhat rare; e.g. in Scheme such things are defined to s
by mananaysiempre 5mo ago
And it makes sense as long as you allow the concept of unsequenced operations at all (admittedly it’s somewhat rare; e.g. in Scheme such things are defined to still occur in sequence, but which specific sequence is unspecified and potentially different each time). The “volatile” annotation marks your variable as being an MMIO register or something of that nature, something that could change at any point for reasons outside of the compiler’s control. Naturally, this means all of the hazards of concurrent modification are potentially there.
That said, your “common parlance” definition of “data race” is not the definition used by the C standard, so your last sentence is at best misleading in a discussion of standard C.
> The execution of a program contains a data race if it contains two conflicting actions in different threads, at least one of which is not atomic, and neither happens before the other. Any such data race results in undefined behavior.
(Here “conflicting” and “happens before” are defined in the preceding text.)
- tsimionescu 5mo agoYour first paragraph makes it sound as if the compiler will actually generate two reads of the value of some register, which might lead to unexpected effects at runtime for certain special registers. However, this is not at all what UB means in C (or C++). The compiler is free to optimize away the entire block of code where this printf() sequence occurs, by the logic that it would be UB if the program were to ever reach it. For example, the following program: int y = rand(); if (y != 8) { volatile int x; printf("%d: %d", x, x) ; } else { printf("y is 8"); } Can be optimized to always print "y is 8" by a perfectly standard compliant compiler.
- shakna 5mo ago"volatile" tells the compiler it is _not_ safe to optimise away any read or write, so it can't just optimise that section away at all. > An object that has volatile-qualified type may be modified in ways unknown to the implementation or have other unknown side effects. Therefore any expression referring to such an object shall be evaluated strictly according to the rules of the abstract machine, as described in 5.1.2.3. Furthermore, at every sequence point the value last stored in the object shall agree with that prescribed by the abstract machine, except as modified by the unknown factors mentioned previously. A compliant compiler is only free to optimise away, where it can determine there are no side-effects. But volatile in 5.1.2.3 has: > Accessing a volatile object, modifying an object, modifying a file, or calling a function that does any of those operations are all side effects.
- saagarjha 5mo agoSure it can. That code path has unconditional UB and thus it is not valid.
- shakna 5mo agoOnly if there would be no side-effects. Which there are.
- saagarjha 5mo agoNo this is irrelevant for making this decision
- shakna 5mo agoI've mentioned elsewhere the standards, and compilers as well, disagreeing with you here. But feel free to run against the various compilers through godbolt. [0] They won't optimise the branch away. Access to a volatile, must be preserved, in the order that they exist. No optimisation, UB or otherwise, is allowed to impede that. Because an access is a side-effect. [0] https://godbolt.org/z/85cGhq3Ta https://godbolt.org/z/85cGhq3Ta
- saagarjha 5mo agoThat they won’t is as most a courtesy to you but they are not required to do this.
- shakna 5mo ago> Furthermore, at every sequence point the value last stored in the object shall agree with that prescribed by the abstract machine, except as modified by the unknown factors mentioned previously. I quoted the C standard, first. Not compiler behaviour. I showed where it requires the compiler not to optimise this. How about, instead of one-line throwaway disagreements, you point out where they are permitted to do this, instead?
- mananaysiempre 5mo ago> Your first paragraph makes it sound as if the compiler will actually generate two reads of the value of some register, which might lead to unexpected effects at runtime for certain special registers. I don’t see how. I was trying to explain why it’s reasonable for a volatile read to be a side effect, after which the C rule on unsequenced side effects applies, yielding UB as you say.