volatile vs memory barrier for interrupts
Asked Answered
H

1

6

Let x and y be variables that are shared between main code and interrupt code.

My idea of volatile is that it is only and always needed for hardware variables and interrupt variables that are also used in main code.

Every usage of x and y in the main code is guaranteed to be atomic by disabling interrupts.

Do x and y really need to be volatile, or is it enough to put a memory barrier before using them to force reloading the variables from RAM?

A)

volatile bool x;
volatile int y[100];

int main(void)
{

        while (true) {
                disable_interrupts();
                if (x)
                        work(y);
                x = false;
                enable_interrupts();
        }
}

B)

bool x;
int y[100];

int main(void)
{

        while (true) {
                memory_barrier();
                disable_interrupts();
                if (x)
                        work(y);
                x = false;
                enable_interrupts();
        }
}

The objectives are:

  • To let the compiler optimize work().

  • Be able to use standard library functions such as memcpy() (those aren't made to be used with volatile variables).

Edit: add interrupt example

interrupts.c:

extern volatile? int x;
extern volatile? int y;

void interrupt(void)
{

        x = true;
        REGY1 = y[7];
        y[23] = REGY2;
}
Hauteur answered 27/6, 2019 at 12:49 Comment(21)
How exactly do you see this code getting harmfully re-organized though, with neither volatile nor memory barrier? Even if work gets inlined? And who exactly updates the variables?Campos
As far as I can tell you are only showing one context in which the variables potentially are accessed. It only gets interesting with more than one. Please show an example of another context potentially accessing the variables.Hamrnand
My idea of volatile nee, only when the compiler optimizes them out. You don't need to volatile all variables, only those you don't want to apply optimizations to. those aren't made to be used with volatile variables - accessing volatile object via non-volatile handle is UB anyway. Anyway, compiler should not optimize the memcpy, as it will see it accesses a volatile object. Let's mark it: should. is it enough to put a memory barrier - no it is not. In your second code snippet compiler will completely remove if (x) statement as it is if (false).Calvados
@Calvados memcpy cannot be used for volatile... behaviour undefined.Glucoprotein
At basic level, disabling and enabling interrupt is for synchronisation just like binary mutex but volatile is for performing operations on memory location without intermediate locations like cpu registers which typically is required for device drivers. Depending on application volatile will still be needed.Befool
@Campos I thought it would be assumed after reading the first line; I added the interrupt code now.Hauteur
@Calvados Regarding if(x): Why? The compiler can't make that assumption after a memory barrier, right?Hauteur
@CacahueteFrito If they are updated by an interrupt you will need volatile for that reason, and it has nothing to do with memory barriers.Campos
@Campos I also had that idea. But I can't think of a logic reason for that; it's just that I've been told so. Could you give a rational explanation of why, and why the barrier wouldn't work?Hauteur
@Befool Sure, in this case, REGY1 and REGY2 would be volatile; hardware always needs it. My question was specifically for variables that don't represent any hardware.Hauteur
@Campos both volatile and memory barriers do the same thing, tell the compiler that something could have been changed in memory, forcing a paricular ordering of variable accesses. They just do it on different set of accesses.Wardlaw
@Calvados "In your second code snippet compiler will completely remove if (x)" Do you know of any real compiler that will? In which case exactly?Mullet
@Campos "If they are updated by an interrupt you will need volatile for that reason" If the variables are never examined in a section of code that can receive and async interrupt, do you need volatile?Mullet
@Mullet The need for volatile of variables shared with interrupts has to do with preventing accidental optimization, nothing else.Campos
@Campos Yes but what exactly are the optimizations we are trying to avoid here, if the interrupt are prevented?Mullet
@Mullet electronics.stackexchange.com/a/409570/6102Campos
@Campos But the memory barrier does prevent the compiler from caching the variable between loops, doesn't it? And that's actually the only unwanted optimization. Anything else can be optimized and will be fine, even better than with volatile, right?Hauteur
@CacahueteFrito No. Read the link I just posted.Campos
@Campos I read it, but I didn't see anything that proves memory barriers + isr disabling not being enough. In fact, the accepted answer there mentions that at its very end.Hauteur
@CacahueteFrito Because memory barriers and interrupt disable has nothing to do with code generation. Memory barriers prevent instruction re-ordering and prefetch caching. Volatile prevents incorrect optimizations. Interrupt disable prevents interrupts from occuring temporarily. These are three entirely different things. Some pedantic compilers might treat volatile access as a memory barrier, but that's another story.Campos
Note that desktop system compilers tend to never make assumptions about interrupts/callbacks not being called, unlike embedded system compilers. So for your average PC compiler, you need not volatile qualify variables shared with a callback, because as a compiler extension, the compiler ensures that it realizes that those variables may be updated at any point. But this is by no means guaranteed by any standard.Campos
W
5

Memory barriers instead of volatile are fine. Linux kernel developers prefer it that way

There are a few things to watch out for.

  • Move the barrier after disabling interrupts. Interrupts tend to happen at the worst times.
  • You need a second memory barrier before enabling interrupts, for variables that are written in the main program, and read in the interupt handler.
  • Disabling interrupts is not enough in a multiprocessor/multicore system, it doesn't prevent another core from running.
  • Needless to say, interrupts should not be disabled for extended periods of time, as it can prevent some hardware drivers from functioning.
Wardlaw answered 27/6, 2019 at 13:26 Comment(2)
I knew about that of Linux for various threads. I didn't know that it is also valid for interrupts :)Hauteur
It's valid for anything that can change the contents of variables. It does not matter if it's an interrupt, another processor, or a DMA access.Wardlaw

© 2022 - 2024 — McMap. All rights reserved.