I have a simple FIFO ring buffer queue that I am using in my embedded C program (using TI C28x C/C++ compiler which is quite similar to GCC for C89 without the extensions). Data is pushed and popped with the queue from interrupts, so the queues need to be volatile.
I've implemented the queue code itself without using volatile so that the queue user has the choice of whether a queue is volatile or not (I want to use this across several projects with differing uses), by declaring the handle to be to a volatile queue object in the usage, instead of defining the queue object itself as volatile in the implementation.
i.e. in que.c:
struct QUE_Obj { /* Object & members are not defined as volatile. */
void * data;
uint16_t capacity;
uint16_t head;
uint16_t tail;
uint16_t size;
bool full;
bool empty;
}
/* Implementation uses all non-volatile types. */
QUE_Handle QUE_init(void * data, uint_least8_t size, uint16_t capacity) {
/* ... */
QUE_Handle q = (QUE_Handle)malloc(sizeof(struct QUE_Obj));
/* ... */
return q;
}
/* ... */
in queue.h:
typedef QUE_Obj * QUE_Handle;
QUE_Handle QUE_init(void * data, uint_least8_t size, uint16_t capacity)
in main.c:
/* Data buffer and queue handle declared to be for volatile data. */
static volatile uint16_t buffer[BUFFER_LENGTH] = {0};
volatile QUE_Handle que = QUE_init((void *)buffer); /* Buffer passed without volatile. */
My question then is, on that last line in C when I cast the buffer to void * does this remove any usefulness of the volatility?
Should I instead define the members of the QUE_Obj to always be volatile and adjust the types used in the implementation to be volatile regardless of the queue's use?
Asked another way, the push() and pop() functions are called from interrupt service routines but their implementation does not "know" about the volatility, will they be optimized away?
You should not access volatile data through a non volatile pointer. Consider the following simple example:
volatile int volatileVar;
int* nonVolatilePtr = &volatileVar;
Whenever your code accesses volatileVar through nonVolatilePtr the compiler does not know that the data you are accessing is volatile and might go for optimizing accesses to it (which is clearly an undesired behaviour). I see very little uses of non-volatile pointers to volatile data.
If you expect your queue to be possibly accessed by interrupt service routines (or similar) I would make the whole data structure volatile.