22

Consider the following piece of code:

unsigned global;
while(global);

global is modified in a function which is invoked by an IRQ. However, g++ removes the "is-not-zero" test and translates the while loop into an endless loop.

Disabling the compiler optimization solves the problem, but does C++ offer a language construct for it?

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
0xbadf00d
  • 17,405
  • 15
  • 67
  • 107
  • @Styne666: the title is the question – Necrolis Feb 29 '12 at 08:43
  • See also http://stackoverflow.com/q/7083482/594137 – Samuel Harmer Feb 29 '12 at 08:54
  • @Styne666 - it's no good practice to write code which needs a specific compiler configurstion to work. Thus, disabling the optimization is no option for production code. – 0xbadf00d Feb 29 '12 at 09:01
  • @Styne666: You can remove your downvote. Question was clear, although not well formulated. And disabling optimizations is not a real answer in this regard. – Sebastian Mach Feb 29 '12 at 09:01
  • @Styne666: I agree that this question is not very precise and that he has given a wrong answer already to himself, assuming that globally or partially disabling optimization is the only solution. But this is not about disabling optimization, it's about making his busy wait loop work - which `volatile` is for. – Gunther Piez Feb 29 '12 at 09:11
  • 2
    @Styne666: Original title contained "loop", not "loops". Don't be stubborn. What counts is how the question is _now_, never mind who changed it, and _now_ your original motivation does not hold true anymore. If the OP decides to not like the tweak, he is entitled to edit it again, and the SO-rules say that if an edit happened, you can re-vote (info: you are not allowed to re-cast votes unless an edit happened). – Sebastian Mach Feb 29 '12 at 09:21
  • I found the question quite clear. Maybe it was not perfectly worded, but maybe English is not the OP's native language either (and besides, I've seen much worse on SO). The OP wants to spin in a loop until some interrupt changes a value. It doesn't work when optimizations are turned on (but does in a non-optimized build). Obviously, the OP would like it to work in either case. What's not clear about this, or where is there room for interpretation? – Damon Feb 29 '12 at 09:24
  • @Styne666: I see you've deleted your posts, but your vote remains. What I read in this behaviour is "I was wrong, but my vote remains as penalty because you dared critisizing me". Please correct me if I am wrong, I'll admit any mistakes on my side. Be honest to yourself. – Sebastian Mach Feb 29 '12 at 11:57
  • I removed comments as this has become a conversation unrelated to the question asked. The vote remains because the OP has not edited their question (in response to answers and comments) to clarify intent and meaning of the question. Without edits provided by the OP (or an answer marked as correct) no one can possibly know what the correct answer is. – Samuel Harmer Feb 29 '12 at 12:03

3 Answers3

17

Declare the variable as volatile:

volatile unsigned global;

This is the keyword that tells the compiler that global can be modified in different threads and all optimizations should be turned off for it.

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
  • 1
    Maybe cange "all optimizations" to "certain optimizations", as there's still many optimizations that can be applied. E.g., in `global = 5 + 6;`, your statement may imply that `5 + 6` are not reduced. – Sebastian Mach Feb 29 '12 at 08:46
  • 2
    Adding the volatile-qualifier doesn't change the created opcode. It still translates to an endless loop ... – 0xbadf00d Feb 29 '12 at 08:57
  • @SaschaHoll yes it does. You can set `global` to `0` on a different thread, and the loop will end. – Luchian Grigore Feb 29 '12 at 09:00
  • 3
    @luchian no the loop will not necessarily stop because that is undefined (datarace) – Johannes Schaub - litb Feb 29 '12 at 09:06
  • @Luchian Grigore - I agree with you. It should prevent the compiler from optimizing the loop, but it doesn't. The created opcode is the same as before. Tested wirh i586-elf-g++ 4.6 – 0xbadf00d Feb 29 '12 at 09:07
  • 2
    @JohannesSchaub-litb how would you suggest he does it then? – Luchian Grigore Feb 29 '12 at 09:08
  • @luch like you suggest. that the approach you suggest is correct doesnt mean that all your comments are correct though. – Johannes Schaub - litb Feb 29 '12 at 09:39
  • @LuchianGrigore It depends on his platform. There is no such thing as a "portable IRQ". On typical platforms, using [GCC atomic operations](http://gcc.gnu.org/onlinedocs/gcc-4.1.1/gcc/Atomic-Builtins.html) is the best solution. – David Schwartz Feb 29 '12 at 09:41
  • @JohannesSchaub-litb If `global` is written in only one thread, is it still a datarace ? – J.N. Feb 29 '12 at 09:55
  • @jn i dont know (not familiar enough with threading issues) but i think it is not. but as the other guy says the c++ spec has nothing to say about interrupt handlers anyway. – Johannes Schaub - litb Feb 29 '12 at 09:59
  • @J.N.: There's almost never a case where the IRQ handler always runs in the same thread as application code (on general-purpose, multi-tasking computers, anyway, maybe on embedded controllers). If this is kernel code or some such, then it's all platform-specific. – David Schwartz Feb 29 '12 at 10:17
  • @DavidSchwartz : my questions was more like, if there is one single thread (be it an interrupt or a normal one) that writes a variable, while all others are reading, is it still a data race ? (I don't think so if the variable is smaller or equal to the machine word size and aligned). – J.N. Feb 29 '12 at 10:37
  • 3
    @J.N.: Yes, it's still a data race. Nothing in the C++ standard makes the machine's word size special. C++ can certainly support CPUs that cannot do atomic writes or reads at their own word size. – David Schwartz Feb 29 '12 at 10:42
  • @DavidSchwartz, I got your point. I think that the standard should not bother with such CPUs ;). Could you name one for my culture ? Finally, I guess that any CPU as at least an atomic read and write at some size, which we could reasonably approximate to 8 bits and that would be enough to store a bool. Am I trying to push it too hard ? Many thanks for the interesting discussion. – J.N. Feb 29 '12 at 15:08
  • @J.N.: We have the standard we have. If you don't like it, your choice is not to program in C++ or to accept it. If you pretend you have guarantees you don't have, your software breaks when your users upgrade their CPUs, operating systems, libraries, and so on. That is, at least in my book, entirely unacceptable. It makes no difference what current platforms do when you're talking about what you can rely on. – David Schwartz Feb 29 '12 at 22:05
10

Since you're using GCC and you say that making the variable volatile does not work, you can trick the optimizer into thinking that the loop changes the variable by lying to the compiler:

while(global)
  asm volatile("" : "+g"(global));

This is an inline assembly statement which says that it modifies the variable (it's passed as an input-output operand). But it's empty, so obviously it doesn't do anything at runtime. Still, the optimizer thinks it modifies the variable - the programmers said so, and the compiler, barring operand substitution (which means simply replacing one text with another), doesn't really care about the body of inline assembly and won't do any funny things to it.

And because the body is empty and the constraint used it the most generic one available, it should work reliably on all platforms where GCC supports inline assembly.

3

You could use GCC attributes on the function declaration to disable optimisation on a per function basis:

void myfunc() __attribute__((optimize(0)));

See the GCC Function Attributes page for more information.

Silas Parker
  • 8,017
  • 1
  • 28
  • 43
  • If the variable has to be in a specific section of memory use the `section` [Variable Attribute](http://gcc.gnu.org/onlinedocs/gcc/Variable-Attributes.html) to place it correctly. You may also need to mark it as `volatile`. – Silas Parker Mar 05 '12 at 11:14