Search code examples
cpointersembeddedatomicmsp430

Atomically overwrite a pointer


I have found similar questions to the one I have (i.e. Is changing a pointer considered an atomic action in C?), but I have not been able find anything that gives me a definitive yes/no answer when it comes to writing a pointer.

My problem is thus. I have an array of struct pointers with a pointer field.

For example

struct my_struct {
    void *ptr;
};
struct my_struct *my_struct[10];

The pointer fields of each struct is read and written to by a thread.

For example

uint8_t index;
for(index = 0; index < 10; index++)
{
    if(my_struct[index]->ptr != NULL)
    {
        my_struct[index]->ptr = NULL;
    }
}

Periodically an interrupt occurs that will read (does not write and I can't use a lock because the handler is time critical) one or more of the pointers stored in my array of structs.

For example

void *ptr = my_struct[2]->ptr;

Ultimately I do not care about whether the pointer read by the interrupt handler is new or old; only that it hasn't been corrupted. Clearly, the following operation will not be atomic

my_struct[index]->ptr = NULL;

But, can I be sure that if an interrupt occurs and reads "my_struct[index]->ptr" it will not be corrupted? I suspect that this should be true since (1) a pointer should completely fit into a register (at least for my target, MSP430) and (2) writing a single register is most likely an atomic instruction. Is my reasoning correct?


Solution

  • The RAM for this chip is embedded on the chip. The compiler agrees with the processor on pointer size, I think you should be ok.
    There's no cache, so you wouldn't even have to worry about fences for synchronizing access.

    EDIT: Lots of other answers here, +1 to all of them. The OP's question is more of an MSP430/gcc compiler question than a C language question and in my haste, late last night before turning out the lights and going to bed, I gave the above answer. I have no previous experience with the MSP430, so I went online and did some spelunking, asked the OP to check a couple constants in their environment and jumped right to the conclusion that they probably don't have anything to worry about in the stated scenario. I am used to working with embedded C compilers that barely meet K&R standards, much less C99 or C11, but that experience actually predates C11, so I did not think to ask whether the _Atomic keyword was available (I should have!). So here's another go at it:

    If you can declare _Atomic(void*) ptr;, by all means, do that. It will ensure the necessary alignment and generate code that writes and reads the pointer value atomically. As @Lundin points out, this is the only sure thing in C when it comes to atomicity. In the absence of _Atomic, assert(sizeof(void*) == 2) also assert(&(my_struct[index]->ptr) % 2 == 0), the later will assure the pointer value is stored in an aligned address location. If/when these assertions do not hold, you are at risk of reading a partially written pointer value due to misalignment or size of the pointer exceeding the word size of the processor. Even these assertions are no guarantee, as they only hold true for code compiled with DEBUG defined. If you feel the need to always verify these constraints, use if(expression) instead.

    @CL.'s point regarding the volatile keyword should also be taken to heart, since the compiler is free to optimize and reorder accesses, it is possible that the interrupt routine might never see the real pointer value and unless that data was initialized to NULL prior any use in your code, that could be the cause of some very serious, difficult to diagnose bugs. This is an unlikely scenario on simple micros with no cache and no speculative execution pipelines, but it can't be ruled out either. So use the volatile keyword.