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)
    {
      ....
    }
}
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
Girish Onte
  • 107
  • 2
  • 8

5 Answers5

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.

  • 1
    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"? – John Bollinger May 29 '17 at 13:51
  • 1
    @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 ;) –  May 29 '17 at 13:57
  • @JohnBollinger as the "reason to be" for `volatile` most probably was memory-mapped I/O, I'll leave it and just add a footnote. –  May 29 '17 at 14:00
  • This answer is misleading. There is explicit usage of `volatile` in the C standard, namely for signal handlers and `longjmp`, see my answer. – Jens Gustedt May 29 '17 at 14:21
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.

Clifford
  • 88,407
  • 13
  • 85
  • 165
  • 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. –  May 30 '17 at 07:33
  • 1
    @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. – Clifford May 30 '17 at 15:53
  • @FelixPalmen Dangerous does not always mean useless though. – Bumsik Kim May 30 '17 at 15:58
  • @Clifford 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. –  May 30 '17 at 16:00
  • 1
    A more extensive article than is reasonable on SO can be found at http://www.embedded.com/design/programming-languages-and-tools/4442490/1/C-keywords--Don-t-flame-out-over-volatile, and the basics of volatile in embedded systems at http://www.embedded.com/electronics-blogs/beginner-s-corner/4023801/Introduction-to-the-Volatile-Keyword – Clifford May 30 '17 at 16:03
  • @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. – Clifford May 30 '17 at 16:16
  • @Clifford 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. –  May 31 '17 at 07:35
  • @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! – Clifford May 31 '17 at 09:53
  • @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. – Clifford May 31 '17 at 09:54
  • "_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. – curiousguy Nov 14 '19 at 15:28
  • @Clifford 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) – curiousguy Nov 14 '19 at 15:30
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.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • 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. –  May 29 '17 at 14:33
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.

TomServo
  • 7,248
  • 5
  • 30
  • 47
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.

curiousguy
  • 8,038
  • 2
  • 40
  • 58