I am writing code for a 32-bit single-processor microcontroller using gcc.
I need to use timestamped objects from a linked list. Another piece of code that may be asynchronous (possibly in ISR) adds them to the list.
The critical section is implemented by disabling interrupts and using the barrier() function.
I am confused when gcc optimization can break my code by caching pointers to list items (the next last item to delete, a list of headers or a free list). I do not want the previous cycle time to be cached inside the while loop. Will the memory barrier protect me from the compiler by deciding to load the pointer once at the beginning of the function and not reload it again? All of these list pointers can be changed in the critical section of the manufacturer code (not shown). I am trying to figure out if pqueue_first should be a volatile pointer, for example.
Presumably, if there was no loop (which is suitable for adding to the list), am I fine if all the code in the function is in the critical section?
Please do not just point me to some general article about volatile or critical sections, because I read a bunch of them, but I had problems with how to apply it to this particular code. I understand that volatile ensures that the compiler will reload the variable each time it is referenced. But I do not understand the likely scope of optimization and its interaction with memory barriers.
typedef struct { EV_EventQueueEntry_t *pqueue_alloc; // allocation (never changes) EV_EventQueueEntry_t *pqueue_head; // head of active queue (ISR can change it) EV_EventQueueEntry_t *pqueue_free; // head of free list (ISR can change it) EV_EventQueueEntry_t *pqueue_first; // soonest item in queue (ISR can change it) EV_EventQueueEntry_t *pqueue_first_prev; // back pointer from soonest item (ISR can change it) EV_UInt_t max_event_count; } EV_EventQueue_t; void RunLoop(EV_EventQueue_t *pev) { while(not timeout) { // Enter critical section disable_interrupts(); barrier(); // item with most recent timestamp // this can be changed by ISR add to queue operation EV_EventQueueEntry_t *pfirst = pev->pqueue_first; if(pfirst!=NULL && EV_PortIsFutureTime(pfirst->event.timestamp, EV_PortGetTime())) { // Copy out message EV_Event_t e = pfirst->event; // Remove event from queue if(pev->pqueue_first_prev != NULL) pev->pqueue_first_prev->pnext = pfirst->pnext; else pev->pqueue_head = pfirst->pnext; // Put event back on free list pfirst->pnext = pev->pqueue_free; pev->pqueue_free = pfirst; pfirst->event.message.type = EV_MESSAGE_NULL; // Find next soonest message to process after this one pev->pqueue_first = ...; pev->pqueue_first_prev = ...; // back pointer // Exit critical section barrier(); enable_interrupts(); // Dispatch message ... } else { // Exit critical section barrier(); enable_interrupts(); // waste some time ... } } }
source share