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;
}
curiousguy
  • 8,038
  • 2
  • 40
  • 58
  • 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? – Lundin Jun 27 '19 at 12:54
  • 1
    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. – Yunnosch Jun 27 '19 at 12:57
  • `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)`. – KamilCuk Jun 27 '19 at 13:05
  • @KamilCuk memcpy cannot be used for `volatile`... behaviour undefined. – Antti Haapala -- Слава Україні Jun 27 '19 at 13:09
  • 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. – anand Jun 27 '19 at 13:17
  • @Lundin I thought it would be assumed after reading the first line; I added the interrupt code now. – alx - recommends codidact Jun 27 '19 at 13:18
  • @KamilCuk Regarding `if(x)`: Why? The compiler can't make that assumption after a memory barrier, right? – alx - recommends codidact Jun 27 '19 at 13:19
  • @CacahueteFrito If they are updated by an interrupt you will need volatile for that reason, and it has nothing to do with memory barriers. – Lundin Jun 27 '19 at 13:20
  • @Lundin 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? – alx - recommends codidact Jun 27 '19 at 13:24
  • @anand 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. – alx - recommends codidact Jun 27 '19 at 13:26
  • @Lundin 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. – followed Monica to Codidact Jun 27 '19 at 13:35
  • @KamilCuk "_In your second code snippet compiler will completely remove if (x)_" Do you know of any **real** compiler that will? In which case exactly? – curiousguy Jun 28 '19 at 17:38
  • @Lundin "_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? – curiousguy Jun 28 '19 at 17:41
  • @curiousguy The need for volatile of variables shared with interrupts has to do with preventing accidental optimization, nothing else. – Lundin Jul 01 '19 at 06:45
  • 1
    @Lundin Yes but what exactly are the optimizations we are trying to avoid here, if the interrupt are prevented? – curiousguy Jul 01 '19 at 17:07
  • @curiousguy https://electronics.stackexchange.com/a/409570/6102 – Lundin Jul 02 '19 at 06:13
  • @Lundin 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? – alx - recommends codidact Jul 02 '19 at 11:53
  • @CacahueteFrito No. Read the link I just posted. – Lundin Jul 02 '19 at 12:10
  • @Lundin 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. – alx - recommends codidact Jul 02 '19 at 12:52
  • @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. – Lundin Jul 02 '19 at 12:56
  • 1
    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. – Lundin Jul 02 '19 at 12:59

1 Answers1

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.