2

Refer to the following code:

void calledFunction(volatile uint8_t **inPtr);
volatile uint8_t buffer[] = {0,0,0,0,0,0}; 

volatile uint8_t *headPtr = buffer;

void foo(void)
{
    volatile uint8_t *tmpPtr = NULL;

    tmpPtr = headPtr;

    //This function modifies tmpPtr
    calledFunction(&tmpPtr);

    headPtr = tmpPtr;
    return;
}

This is a simplified version of code that I am attempting to make interrupt-safe, and I am not sure why this local is defined as a volatile. I know that there is no performance reason (i.e. to guarantee at least O(n) for this function) because this function should run as efficiently as possible.

This function can be called in both main execution and inside interrupts, but since tmpPtr is a nonstatic local variable, it should not be able to be modified by any other instance of foo().

I can't see any access pattern that would require the volatile keyword in this context.

In short, What is the purpose of the volatile keyword for tmpPtr in function foo()?

EDIT:Forgot a & in function argument

EDIT2: I have inherited this code and need to modify it. My main question is whether the volatile keyword has any special effective reason for being in this context.

EDIT3: Added the prototype for calledFunction()

EDIT4: Added important clarification in original code that headPtr and buffer both have volatile

StephenKercher
  • 143
  • 1
  • 7
  • 1
    It is not `volatile`, it is a *pointer* to volatile. And it is pointing to the global `buffer`. So if `foo` is called from interrupt context, you want to make sure it can actually access the `buffer`. – Eugene Sh. Jul 15 '20 at 13:45
  • Also, you are passing `tmpPtr` by **value** so the `calledFunction` does not (and cannot) modify it. – Adrian Mole Jul 15 '20 at 13:46
  • 1
    @AdrianMole It's a pointer. The function can modify the pointee. But yeah, the line after that is making me doubt about the intention – Eugene Sh. Jul 15 '20 at 13:47
  • @EugeneSh. Yes, but the code comment says "modifes tmpPtr" - which it cannot do. It can only modify `*tmpPtr`. – Adrian Mole Jul 15 '20 at 13:48
  • @AdrianMole Yeah, the next line might be hinting that you are right. – Eugene Sh. Jul 15 '20 at 13:49
  • The question seems ambiguous in that I am not sure if @krchrit wrote this code, or he inherited it from someone – TSG Jul 15 '20 at 14:04
  • I made a mistake and forgot to add an `&` in this example. The code I am modifying is written correctly. I have inherited this code and am modifying it to become interrupt-safe. – StephenKercher Jul 15 '20 at 14:08
  • Presumably, `calledFunction` expects a `uint8_t volatile **` (for whatever reason) and passing a `uint8_t **` would lead to a compiler warning/error. – Petr Skocik Jul 15 '20 at 14:18
  • @EugeneSh. based on your initial comment, I now believe the original code was written with the opposite assumption in mind. This is due to `callingFunction` having the prototype of `void callingFunction(volatile uint8_t **tmpPtr)` – StephenKercher Jul 15 '20 at 14:19

2 Answers2

2

The reason tmpPtr has volatile is due to tmpPtr needing to reference a volatile uint8_t, not because tmpPtr itself is volatile (it isn't).

As initially pointed out by @Eugene Sh., this question came up due to a misunderstanding in syntax when defining volatile pointers and variables. This question has a great explanation of syntax for pointers to volatile vs volatile pointers.

StephenKercher
  • 143
  • 1
  • 7
0

Volatile restricts the compiler from optimizations when accessing (reading or writing) the data this pointer points to.

This comes up in embedded or interrupts often because memory-mapped peripherals can't have what would normally be "extraneous" reads or writes optimized out.

E.g.,

int32_t variable = 0;
variable = 1;
variable = 2;
variable = 3;

An optimizing compiler would skip setting the value of variable to 0, 1, and 2 and just set it to 3. That's fine generally, but if instead of writing to a normal variable we are writing to a memory-mapped port, we actually want each of those writes to happen.

This can happen even outside the world of hardware interfaces. If a separate thread is in a loop looking for variable to be set to 2, an optimizing compiler would preclude this from happening.

The fact that it's local is not material. It's just that most use cases for volatile happen be implemented with translation-unit (or cross-translation-unit using extern) scope.

Two examples are memory-mapped register definitions (the struct is typically global, often in a header file, and commonly the instance of the pointer to the struct is global though it doesn't have to be), and flags like our thread example.

I don't advocate for either design, but you will come across it frequently in embedded development.

Scott S
  • 341
  • 4
  • 11
  • I appreciate the detailed response. I understand what `volatile` means in context to embedded development. My main question was asked because I had a misunderstanding of the syntax for declaring a variable as a pointer to a volatile vs a volatile pointer. I read the declaration of `tmpPtr` as a volatile pointer, not a pointer to a volatile(which it is). Treating `tmpPtr` as a volatile pointer made no sense to me since it was declared locally(and not static) – StephenKercher Jul 16 '20 at 17:55
  • The fact that it's local or non-static is still immaterial. You could contrive an example like foo() above but with a volatile pointer (uint8_t * volatile tmpPtr), which gets modified indirectly in calledFunction() (e.g., message queue to another thread) and a while loop in waits for the value of the pointer to change at the bottom of foo(). Check out https://stackoverflow.com/questions/9935190/why-is-a-point-to-volatile-pointer-like-volatile-int-p-useful – Scott S Jul 16 '20 at 20:05
  • A shop I worked at always had the qualifier to the right of the type to make things consistent, which has made my life much easier. E.g.: int32_t const * const variable; -- first const refers to int32_t, second const refers to pointer. The same can be done with volatile. E.g.: uint32_t volatile const * volatile const variablePointer. I promise this will make you deeply unpopular with your coworkers, though. – Scott S Jul 16 '20 at 20:14