Where all to use volatile keyword in C
Asked Answered
F

5

3

I know volatile keyword prevents compiler from optimizing a variable and read it from memory whenever it is read. Apart from memory mapped registers, what are all the situations where we need to use volatile? Given a conforming compiler, do I have to declare test_var as volatile in both scenarios?

1.

In file1.c

int test_var=100;


void func1()
{
    test_var++;
}

In file2.c

extern int test_var;

void func2()
{
    if(test_var==100)
    {
      ....
    }
}

2.

In file1.c

int test_var=100;

void func1()
{

}

In file2.c

extern int test_var;

void func2()
{
    if(test_var==100)
    {
      ....
    }
}
Frighten answered 29/5, 2017 at 13:4 Comment(6)
That is probably required in multithreaded applications as well.Giron
Note that with your variable declared as being of the extern storage class, the compiler is restricted in its ability to optimize out reads and writes to that variable.Hatchway
@Jean-FrançoisFabre and that would be a classic bug, see my answer.Howenstein
@FelixPalmen very good answer btwGiron
barrgroup.com/Embedded-Systems/How-To/C-Volatile-KeywordProchoras
@Hatchway "the compiler is restricted in its ability to optimize out reads and writes to that variable" How so?Dade
H
6

memory mapped I/O is the only generic use of volatile in C. *)

With POSIX signals, a volatile can also be used together with the type sig_atomic_t like this:

volatile sig_atomic_t signal_occured = 0;

Neither of your scenarios should require volatile at all. If all you're interested in is a guarantee that the value is updated between different compilation units, see tofro's comment, extern already guarantees that. Particularly, volatile is not the correct tool for thread synchronization in C. It would only introduce bugs, because, as you state it, it does require actual read and write accesses to the variable, but it does not enforce their proper ordering with respect to threads (it's missing memory barriers, google for details).

Note that this is different from some other languages where volatile is designed to work between threads.

In an embedded system, volatile might be good enough for communicating between an ISR (interrupt service routine) and the main program, when combined with a data type that's read/written atomically, just like sig_atomic_t for POSIX signals. Consult the documentation of your compiler for that.


*) The C standard mentions this, along with the use-case of "asynchronously interrupting functions", only in a footnote, because memory-mapped I/O is outside the scope of the language. The language just defines the semantics of volatile in a way that make it suitable for memory-mapped I/O.

Howenstein answered 29/5, 2017 at 13:13 Comment(4)
Inasmuch as you seem to be distinguishing between standard C and POSIX (or other) C extensions, your opening sentence is a bit odd. As far as I am aware, standard C does not provide memory-mapped I/O, so shouldn't you say "there is no generic use of volatile in C"?Manana
@JohnBollinger well, footnote 134 in section 6.7.3 explains this intended use of volatile ("A volatile declaration may be used to describe an object corresponding to a memory-mapped input/output port"), but it also mentions "asynchronously interrupting functions" (the use-case with POSIX signals), so you're probably right ;)Howenstein
@JohnBollinger as the "reason to be" for volatile most probably was memory-mapped I/O, I'll leave it and just add a footnote.Howenstein
This answer is misleading. There is explicit usage of volatile in the C standard, namely for signal handlers and longjmp, see my answer.Lavinalavine
D
4

In neither of your examples is volatile necessary.

volatile is necessary:

  1. anywhere a variable may be changed outside of the control of a single thread of execution,
  2. anywhere the variable access is required to occur even when it semantically has no effect.

Case 1 includes:

  • memory mapped I/O registers,
  • memory used for DMA transfers,
  • memory shared between interrupt and/or thread contexts,
  • memory shared between independent processors (such as dual port RAM)

Case 2 includes:

  • loop counters used for empty delay loops, where the entire loop may otherwise be optimised away completely and take no time,
  • Variables written to but never read for observation in a debugger.

The above examples may not be exhaustive, but it is the semantics of volatile that are key; the language only has to perform an explicit access as indicated by the source code.

Diapedesis answered 29/5, 2017 at 18:2 Comment(11)
The examples "memory shared between interrupt and/or thread contexts" and "memory shared between independent processors (such as dual port RAM)" are dangerous in C. In short: There's something necessary, but typically more than just volatile. volatile is, in general, useless for these cases.Howenstein
@FelixPalmen : I am not sure that "useless" is entirely true - not a complete solution for sure. To assume the problem is solved with volatile alone is dangerous; however it remains a circumstance in which volatile plays a role.Diapedesis
@FelixPalmen Dangerous does not always mean useless though.Blucher
@Diapedesis as soon as you use a construct that ensures proper synchronization between threads, volatile is superfluous. ISRs on embedded platforms might be an exception if your environment/compiler gives you some guarantees, but apart from that -- if you spot a volatile along the lines of multiple threads accessing the same data, you spot a bug.Howenstein
A more extensive article than is reasonable on SO can be found at embedded.com/design/programming-languages-and-tools/4442490/1/…, and the basics of volatile in embedded systems at embedded.com/electronics-blogs/beginner-s-corner/4023801/…Diapedesis
@FelixPalmen : It is probably not convenient in SO comments for you to explain the "superfluous" comment - I'd need clarification. Please feel free to edit my answer if you feel there is something important to add (or perhaps remove) - I'd appreciate it. Superfluous is not of course dangerous however. Matters of synchronisation and atomicity are important aspects not addressed by volatile, but those are not perhaps the subject of the question.Diapedesis
@Diapedesis your first link already tells everything necessary: "In most multi-threaded setting you will use a variant of enter_critical and exit_critical region [...] Thus having volatile objects inside this region serves no purpose." (page 4). So yes, volatile for thread synchronization is dangerous in general (sometimes hiding bugs), and most of the time useless because the correct tool does all that's needed.Howenstein
@FelixPalmen : I am not sure I advocated "'volatile' for thread synchronization". The part you mentioned is followed by a quote from Linus Torvalds ""volatile" doesn't help anything at all with memory ordering and friends, so it's insane to think it "solves" anything on its own.”. The critical point being "on its own". He is wrong of course it does solve some things on its own, just not "memory ordering and friends" - context is everything!Diapedesis
@FelixPalmen : To be clear I have never used a processor for which this is a problem, so your input is valuable. On Cortex-M it is recommended for portability across ARM platforms to use DMB, but is redundant on Cortex-M itself.Diapedesis
"as soon as you use a construct that ensures proper synchronization between threads, volatile is superfluous" Yes but sometimes no additional construct is needed; f.ex. for a flag that says "stop processing and exit as soon as possible", a volatile boolean is enough.Dade
@Diapedesis In most cases, doing anything to enforce correct memory visibility between cores or diff CPU will make volatile redundant and only a pessimization, and a confusing one (as ppl will suppose that making a few variables volatile is helpful while often you would need to make almost all shared objects volatile to even have correct semantics)Dade
L
1

Besides extensions such as memory mapped devices, in standard C volatile has two use cases: interaction with signal handlers and modification of objects across usage of setjmp/longjmp. Both are case were there is unusual flow of control that an optimizer may not be aware of.

Lavinalavine answered 29/5, 2017 at 14:20 Comment(1)
memory mapped devices are no "extensions", they are just something that might be present "outside", a property of the environment, and they are mentioned in the standard.Howenstein
B
1

In C microcontroller applications using interrupts, the volatile keyword is essential in making sure that a value set in an interrupt is saved properly in the interrupt and later has the correct value in the main processing loop. Failure to use volatile is perhaps the single biggest reason that timer-based interrupts or ADC (analog-digital conversion) based interrupts for example will have a corrupted value when flow of control resumes after the processor state is returned post-interrupt. A canonical template from Atmel and GCC:

volatile uint8_t flag = 0;

ISR(TIMER_whatever_interrupt)
{
    flag = 1;
}

while(1) // main loop
{
    if (flag == 1)
    {
        <do something>
        flag = 0;
    }
}

Without the volatile it's guaranteed to not work as expected.

Broughton answered 4/6, 2017 at 21:38 Comment(0)
D
0

Apart from memory mapped registers, what are all the situations where we need to use volatile?

If

  1. execution is purely sequential (no threads and no signals delivered asynchronously);
  2. you don't use longjmp;
  3. you don't need to be able to debug a program compiled with optimizations;
  4. you don't use constructs with vaguely specified semantics like floating point operations;
  5. you don't do useless computations (computations where the result is ignored) as in a benchmark loop;
  6. you don't do timings of any pure computations, that is anything that isn't I/O based (I/O based such as timings of accesses of network requests, external database accesses)

then you probably have no need for volatile.

Dade answered 14/11, 2019 at 15:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.