Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

volatile and non-volatile bitfields

I'm writing a code for Cortex-M0 CPU and gcc. I've the following structure:

struct {
    volatile unsigned flag1: 1;
    unsigned flag2: 1;

    unsigned foo; // something else accessed in main loop
} flags;

flag1 is read and written from both GPIO interrupt handler and main loop. flag2 is only read and written in main loop.

The ISR looks like this:

void handleIRQ(void) {
    if (!flags.flag1) {
        flags.flag1 = 1;
        // enable some hw timer
    }
}

The main loop looks like this:

for (;;) {
    // disable IRQ
    if (flags.flag1) {
        // handle IRQ
        flags.flag1 = 0;
        // access (rw) flag2 many times
    }
    // wait for interrupt, enable IRQ
}

When accessing flag2 in main loop, will the compilier optimize access to it so it won't be fetched or stored to memory every time it is read or written to in code?

It's not clear to me because to set flag1 in ISR, it will need to load whole char, set a bit and store it back.

like image 987
woky Avatar asked Mar 20 '17 11:03

woky


1 Answers

It is my reading of the C11 standard that it is not proper to use a bitfield for this - even if both of them were declared as volatile. The following excerpt is from 3.14 Memory location:

  1. Memory location
    Either an object of scalar type, or a maximal sequence of adjacent bit-fields all having nonzero width
  2. NOTE 1 Two threads of execution can update and access separate memory locations without interfering with each other.

  3. NOTE 2 It is not safe to concurrently update two non-atomic bit-fields in the same structure if all members declared between them are also (non-zero-length) bit-fields, no matter what the sizes of those intervening bit-fields happen to be.

There is no exception given for volatile. Thus it wouldn't be safe to use the above bitfield if both threads of execution (i.e. the main and the ISR) if ISR will update one flag and the main will update another. The solution given is to add a member of size 0 in between to force them be placed in different memory locations. But then again, it would mean that both flags would consume at least one byte of memory, so it is again just simpler to use a non-bit-field unsigned char or bool for them:

struct {
    volatile bool flag1;
    bool flag2;

    unsigned foo; // something else accessed in main loop
} flags;

Now they will be placed in different memory locations and they can be updated without them interfering with each other.


However the volatile for flag1 is still strictly necessary because otherwise updates to flag1 would be side-effect free in the main thread, and the compiler could deduce that it can keep that field in a register only - or that nothing need to be updated at all.

However, one needs to note that under C11, even the guarantees of volatile might not be enough: 5.1.2.3p5:

When the processing of the abstract machine is interrupted by receipt of a signal, the values of objects that are neither lock-free atomic objects nor of type volatile sig_atomic_t are unspecified, as is the state of the floating-point environment. The value of any object modified by the handler that is neither a lock-free atomic object nor of type volatile sig_atomic_t becomes indeterminate when the handler exits, as does the state of the floating-point environment if it is modified by the handler and not restored to its original state.

Thus, if full compatibility is required, flag1 ought to be for example of type volatile _Atomic bool; it might even be possible to use an _Atomic bitfield. Both of these require a C11 compiler, however.

Then again, you can check the manuals of your compiler if they guarantee that an access to such volatile objects is also guaranteed to be atomic.