Search code examples
ccastingatomic

read array of 4 bytes atomically using read of 32-bit


I am managing an array of 4 flags of uint8_t each.

uint8_t flags[4].

these flags can change by different threads at any moment.

i want to be able to get a snapshot of these flags by using one atomic operation. lets say i do the following :

uint32_t temp_val = read_atomic32((uint32_t *) &flags[0])

now i cast back to an array of flags:

uint8_t *my_flags = &temp_val;
printf("flag zero is %d\n, my_flags[0]);

i am suspecting few problems here:

  1. maybe flags[4] is not aligned for uint32_t reads and might give a non-aligned crash or prehaps the atomic functionality will not be garunteed because of nonaligned access.

  2. what about endians? should a problem occur? even though i am using uint32_t i am later on casting it back to an array of uint8_t and i don't think the memory layout will be changed because of this action - still, maybe i am missing something here. i am assuming here that if i read a memory location for 4 bytes, these pattern will be the same until cast back no matter if your machine is little endian or big endian.

  3. is there a better way to manage 4 independent flags of uint8_t but still be able to read them as whole using one action in a portable safe way?


Solution

  • Place your flags in a union:

    union {
      uint8_t  c[4];
      uint32_t t;
    } flags;
    
    flags.c[0] = ...;
    flags.t = 0xffffffffU;
    

    This takes care of the alignment issue and you can use the type punning to access the flags via t without a cast. Not quite kosher according to strict ISO C rules, but usually does what you want.

    Endianness becomes an issue the moment you assign literal values (as in my example) or expect certain values when accessing flags.t. Just reading a value and then writing one just read (unmodified--no bit twiddling!) should be okay.