0

Is it necessary to protect reads of volatile variables in critical sections when using interrupts? Or are critical sections only required when concurrently writing variables?

Here is an example of a variable written within an ISR which is protected with a critical section:

void loop() {
    portENTER_CRITICAL(&mux);
    uint other = variable_written_from_isr + 1;
    portEXIT_CRITICAL(&mux);
}

What will happen if an interrupt changes variable_written_from_isr while it is being read? Would anything worse that inconsistencies happen?

Here is an opposite example where a variable is written from an interrupt and read from the loop (or a task):

void IRAM_ATTR handleInterrupt() {
    portENTER_CRITICAL_ISR(&mux);
    uint other = variable_written_from_loop + 1;
    portEXIT_CRITICAL_ISR(&mux);
}

Please note that I aware of stdatomic. I am mainly interested in understanding the behavior of concurrent access in the context of interrupts.

2
  • 1
    What will happen if an interrupt changes variable_written_from_isr while it is being read? ..... there will be no interrupt because portENTER_CRITICAL(&mux); disables interrupts
    – jsotola
    Apr 1, 2019 at 8:04
  • Thank you for pointing it out. I was expecting the interrupt execution to be delayed until the critical section is exited.
    – DurandA
    Apr 3, 2019 at 0:30

2 Answers 2

0

You should use critical sections around any operations that could potentially be affected by the execution of an ISR (or which could affect an ISR) that aren't atomic.

Simply put, an atomic instruction is one which can be represented by a single assembly language instruction. You can't interrupt during the execution of an assembly instruction, so anything that only takes one instruction can't be interrupted until it's complete.

Examples would be moving a value from one 8-bit variable (assuming an 8-bit architecture) into a register, storing an 8-bit value into memory, etc.

What instructions are likely to be atomic very much depends on the architecture that you are working on. The only real way to be sure is to compile the code into assembly language and examine what is produced. Of course, that means learning the assembly language of your chosen architecture.

However: if in doubt, use a critical section. Better to be safe than sorry. But keep those critical sections as short as possible to not disturb the interrupts more than is needed.

For example, instead of starting a critical section, reading some volatile value, doing some calculations with it, and writing it elsewhere, you should use the critical section just to read the volatile value into a temporary variable. After the critical section, you can then do your calculations and things with it, while the interrupt is then free to be called again.

0

A non-atomic read in your main code of a 2-or-more-byte variable that can be written in an interrupt routine does need to be protected. In this case, the variable itself can't be corrupted, but the value read from it by your main code might be - in the case where the main code is reading the shared variable one byte at a time, and gets interrupted between two of those byte-reads.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.