Is volatile needed when variable is only read during interrupt
Asked Answered
T

1

15

The C standard states that the volatile keyword should be used in the definition of a variable when there's a chance that the variable's value could change outside the normal flow of execution of the program.

If a global variable is changed (written) during normal execution flow and only read outside this normal flow (in an interrupt). Does this variable need to be volatile ? And why ?

Throttle answered 21/3, 2019 at 10:19 Comment(11)
Short answer: yes. What makes you think it should not need to be declared volatile?Humpy
@Humpy because the variable didn't "change" outside the normal flow. It is modified only in normal flowThrottle
It doesn't work without volatile? Volatile tell to compiler not optimize the reads. Is used for register for example that can change by events from outside worldRuderal
If the interrupt can interrupt execution at any time, then the variable needs to be either protected by some synchronization mechanism; or be volatile and atomic. In Standard C the interrupt handler should only set a volatile sig_atomic_t flag; specific platforms might provide further allowed behaviour for interruptsMaurinemaurise
Related: #26307571Victim
@Maurinemaurise I agree with atomic part, for the volatile it is still unclear to me.Throttle
@Throttle if it's not volatile then it's undefined behaviour due to a data race; also the compiler might perform optimizations around the writing of the variable that would impact the values readMaurinemaurise
@M.M: You are missing here an important point, sig_atomic_t is NOT required in "Freestanding Environments", they are only required in "Hosted Environments". When we talk about embedded development and interrupts, we usually are in freestanding environments and not on hosted ones.Petasus
@Petasus freestanding environments which support interrupts typically do have other ways for the interrupt handler to communicate (otherwise they would be useless)Maurinemaurise
@Maurinemaurise after 4 years I think I finally got it. I would like to add other options, please correct me if I'm wrong. using simply _Atomic will take care of atomicity and of needed memory barrier (no need for volatile). I could also manage the atomicity manually by any means and add memory barrier and it would be sufficient.Throttle
It seems the question also makes a statement that only things that happen outside ISRs is "normal execution flow", whereas in reality, "normal execution flow" should probably refer to any function that gets called by another function within the call tree that starts with main() or any other top-level function (one of them being the ISR itself). So, from the point of view of the ISR, the variable DOES get modified outside the normal execution flow.Chipman
F
12

If a global variable is changed (written) during normal execution flow and only read outside this normal flow (in an interrupt). Does this variable need to be volatile?

Yes absolutely.

And why?

To ensure proper execution of the interrupt handler (rather than the normal flow).


Let me elaborate.

Imagine you have a variable like this:

int x;

You are modifying this variable in the normal flow of the program as this:

void modify(int *x){...some code here...}

And in the interrupt service routine, you are reading the variable.

Remember that an interrupt can occur asynchronously(at any time). Also remember that the compiler will first convert your code into a series of machine instructions which could look something like this:

load val from mem to register
perform modification operations
store value from registers to memory
perform other operations where the variable is used

Now, the compiler may optimise this program to reduce the number of memory read-writes as such:

load value
perform modifications
use value
...
modify register content
...
use
...
keep using value from the registers without ever storing in the memory.

In such a case, if an interrupt occurs, (note that the interrupt context is usually different from the normal context and thus has different set of registers in a lot of architectures (say arm...)), it will attempt to read the value of the variable from the memory. But the contents of the memory have never been changed because of compiler optimisation.

So, the ISR could read an old value (worse still-we can't definitely say how old a value) which would result in unintended behaviour.

So, the variable should be declared as volatile to prevent the compiler from meddling with the program.

Foresheet answered 21/3, 2019 at 11:18 Comment(3)
Declaring the variable as volatile will not prevent the ISR from using the old value from the main code. The only way to prevent this is to disable interrupts either side of the read-modify-write. The ISR will inevitably use an "old" value if it fires on the instruction before the value is accessed by the main line code. If this is a problem then you have a major problem with your design.Jedjedd
But if the main code has not accessed the variable yet, then the variable vaue has not changed in which case the old value is what is supposed to be used by the ISR.Foresheet
That is what I saidJedjedd

© 2022 - 2024 — McMap. All rights reserved.