0

Is it safe to share an aligned integer variable, not bigger than the processor natural word, with volatile qualifier, between the main program and an ISR in C? Is it guaranteed that no torn reads or writes may happen?

mrn
  • 959
  • 5
  • 15
  • Possible duplicate of [C 'Volatile' keyword in ISR and multithreaded program?](http://stackoverflow.com/questions/12738672/c-volatile-keyword-in-isr-and-multithreaded-program) – A.N Dec 20 '16 at 08:02
  • Depends on the system... I have used a system (Z80) where you had to disable interrupts to guarantee safe writing of a 2-byte value in the presence of ISRs – M.M Dec 20 '16 at 08:02
  • @A.N. It's not a duplicate. My question is about atomicity and torn writes/reads which is not covered by the question you pointed. – mrn Dec 20 '16 at 08:06
  • 1
    @M.M While Z80 could handle two-byte values, its "natural word" was still a single byte. – Some programmer dude Dec 20 '16 at 08:06
  • @mrn What the linked duplicate tells you is that `volatile` is not for synchronization/atomicity. – Some programmer dude Dec 20 '16 at 08:07
  • 1
    @M.M did you really developed firmware for Z80 using [tag:c]? You lost the beautiful occasion to write tons of `XOR A` and `INC HL` ;) – LPs Dec 20 '16 at 08:08
  • As far as the read write is atomic, you can be sure about concurrent access. `volatile` tells to the compiler to not optimize that variable. – LPs Dec 20 '16 at 08:13
  • @LPs well I wouldn't say "firmware" . Used some dreadful compiler with thousands of bugs and had to hand-write assembly to patch up the gaps. 0/10 would not do again. – M.M Dec 20 '16 at 08:14
  • Single reads and writes might be OK but what about combined read-modify-write? If you do a `Var++` in your main thread and in your ISR you might get different value than expected. – Gerhardh Dec 20 '16 at 09:29
  • 1
    There can't possibly be such a guarantee -- who or what would provide it? The only source of a generic C guarantee would be the C standard, and the C standard doesn't say anything about ISRs. – David Schwartz Dec 20 '16 at 19:33
  • @DavidSchwartz: But the C standard does provide the information if certain standard integer types are accessed atomically. At least if `stdatomic` is provided. – too honest for this site Dec 21 '16 at 23:29
  • @Olaf That doesn't mean that an ISR will see those updates, so the variable may still not be safe to share. There's no way to know whether atomicity is the only issue or not. – David Schwartz Dec 22 '16 at 07:34
  • @DavidSchwartz That's a matter of caches, etc. If you have only one CPU (typical for bare-metal embedded) or coherent caches, the ISR is guaranteed to see it! – too honest for this site Dec 22 '16 at 12:48
  • 1
    @Olaf No, that's not how guarantees work. You can't synthesize a guarantee by listing the ways you *think* things can go wrong and say you have a guarantee if none of those ways are present. You have a guarantee when someone says (through a standard or through documentation) "X is guaranteed to do Y" and that party is responsible for ensuring that it works or they've failed to meet the guarantee they gave you. If there's a guarantee, there's a standard or document that provides it. If you can't point to it, it's not guaranteed. – David Schwartz Dec 22 '16 at 15:08
  • @DavidSchwartz: So how do **you** think an ISR _on the same CPU_ will not see an atomic write to a memory location in the same logical (== physical) address space? The guarantee is given by the fact how CPUs and memory works! But you can very well read the datasheets to find how that cannot happen. (Note: I did not say the outer world will see it; that would be a matter of the read/write policy, i.e. caches and bus-interfaces!) – too honest for this site Dec 22 '16 at 16:00
  • How CPUs and memory work can *change*. There is no guarantee that it will stay the same. He's not saying now or on some particular piece of hardware. You can't read the datasheets of products that don't exist yet. You only have guarantees if standards or such documents provide them. Since this is a C question, the guarantee would have to come from the C standard. That says nothing about ISRs, so no guarantee. It's really that simple. That I can't imagine how it might fail in the future does not make a guarantee. – David Schwartz Dec 24 '16 at 02:59
  • @DavidSchwartz : I see your point (after much consideration), but it is perhaps somewhat pedantic. For the question to be answerable in the manner you suggest, it would have to specify a specific target and toolchain rather then just the language, but that would narrow its scope significantly, while with appropriate cautions, it is perhaps possible to provide an answer that is true in the vast majority of practical cases - and therefore more generally useful. Your point would perhaps be better made (and more helpful) by posting an answer. – Clifford Dec 27 '16 at 14:56
  • I honestly don't agree. It's extremely important to understand that there simply is no guarantee in this case. Platform-specific knowledge is absolutely essential to answer questions like this. The generic answer is no, there's no guarantee. I've seen so much code fail in horrible ways over decades because people do not learn this valuable lesson. – David Schwartz Dec 28 '16 at 05:40

5 Answers5

3

The volatile keyword does not imply atomicity - that simply ensures that a variable is explicitly read and not assumed not to have changed. For safe shared access without any other protection mechanism the variable must be both atomic and declared volatile.

The compiler may document types that are atomic for any particular target, and may define sig_atomic_t for this purpose.

In general it is perhaps reasonable to assume that your compiler will not do anything perverse and split an aligned word read where the instruction set allows an atomic read. Caution should be applied however when porting code between platforms - such low level code should be regarded as target specific and non-portable.

Clifford
  • 88,407
  • 13
  • 85
  • 165
  • There is no guarantee that all you need is explicit read/write and atomicity. Future hardware platforms may have other requirements that we are not aware of today. There simply isn't a guarantee, period. – David Schwartz Dec 24 '16 at 23:46
  • @DavidSchwartz : When writing systems level code it is probably fair to assume that you are aware of platform dependencies that are independent of the language. Moreover writing systems level code you are writing the a specific system *now* not some possible unknown future system. – Clifford Dec 25 '16 at 14:36
  • Exactly. That's why your answer (at least the last sentence of the first paragraph) is incorrect. There may be any number of other issues depending on the specifics of your platform. There's absolutely no useful, generic answer to whether `volatile` does or doesn't satisfy any or all of your requirements without knowing the specific system's requirements. Whether or not `volatile` helps you meet your requirements depends on what those requirements are and those vary drastically with the intended target -- there are no generic guarantees independent of platform knowledge. – David Schwartz Dec 26 '16 at 00:15
  • @DavidSchwartz : I clearly did not claim that `volatile` satisfied *all" requirements - you are entirely misrepresenting what I wrote. Atomicity is an independent attribute unrelared to `volatile`. The correct use of `volatile` [remains important in embedded systems](http://www.embedded.com/electronics-blogs/beginner-s-corner/4023801/Introduction-to-the-Volatile-Keyword). – Clifford Dec 26 '16 at 09:03
  • @DavidSchwartz : I stated two attributes required of a shared variable - and you are claiming that it is not true - you'll have to help me out there. There may be *additional* requirements in some cases, but it remains true *at least* that the variable must be read lest it was changed in another context, and it must not change while being read; in what context would that be *incorrect*. – Clifford Dec 26 '16 at 09:08
  • True, but that doesn't matter. Say there's three things that you need and there's one thing that provides all three of them and it's the *only* thing that provides the third thing. Then that thing is the one and only suitable option. So unless you know *everything* you need, you can't tell what feature/option is the one to use. – David Schwartz Dec 26 '16 at 19:41
  • @DavidSchwartz : Post a better answer. I'd be very interested in your argument, but comments are not a good medium for presenting it. On the vast majority of single-core, single -threaded processors the answer applies. If you are developing for something more esoteric with additional considerations, I guess you had better know what you are doing, and I'd worry if you were posting this sort of question - I don't have experience of such a system, so cannot comment. In the end in production coding, I could not care less about the hypothetical, it just has to work on the target in question. – Clifford Dec 27 '16 at 14:12
  • This question is not about any particular target. It asks if there's is a guarantee. It's a C question. The last sentence of your first paragraph is simply *wrong*. There's no way you can know what other protection mechanism might be required or what might be sufficient without knowing the platform. (Other than that sentence, there's nothing wrong with your answer except perhaps by omission.) – David Schwartz Dec 28 '16 at 05:42
  • @DavidSchwartz : It would help me understand your objection if you could provide a concrete example. I can see that in some architectures atomicity may conceivably not simply be a case of selecting a specific data type, but the requirement remains the avoidance of modification during access. That is a general computing imperative not specific to C. What else specifically must be achieved? I cannot improve the question until I can understand that, and currently you are all about what is wrong and not providing any solution. Suggestions for edits welcome - be constructive. – Clifford Dec 28 '16 at 10:01
  • My point is that you cannot form a guarantee from a lack of ways to imagine how something could fail. You are asking me to imagine how it could fail. You can't get a guarantee from a lack of imagination. The standards don't guarantee it, so no guaranteed. Period. You have an actual guarantee if you know ho to blame if it doesn't work. If, for example, `volatile` doesn't do what the C standard says it will do, your C implementation is to blame. Who do you blame the CPU requires something special to work with ISRs and `volatile` and atomic don't do it? Where is the promise that it will work? – David Schwartz Dec 28 '16 at 20:17
  • There was some code that didn't set a UDP socket non-blocking but just did a `select` and then a `recv`. The author thought it was guaranteed not to block because nobody could imagine any way it could block. Except it did when a datagram was received with a bad checksum and an administrator turned on checksum checking in-between the call to `select` and the call to `recv`. Now there's no way I could have imagined this particular failure until it happened. But I constantly advise people not to assume a socket won't block unless it's set non-blocking because there is no actual guarantee. – David Schwartz Dec 28 '16 at 20:20
  • @DavidSchwartz : That is not an example of this situation however. I suggest that it is not an issue of language standard; rather computer architecture; the atomic part is, as my answer goes on to state architecture dependent and plausibly compiler dependent. Yes it is tagged C, but it is also tagged embedded and the question considers machine dependencies. In that context I see nothing *wrong*. a single voice in all the internet telling me so does not make it so. Your opinion is noted; I suggest we agree to differ - perfectly amicably. – Clifford Dec 29 '16 at 14:53
1

Regarding the volatile keyword, it is just there to protect against possible incorrect optimizations by the compiler. It doesn't help with thread safety.

Whether or not it is thread-safe to use a shared variable of a given size is up to the compiler. There are no guarantees that access is atomic. For example the compiler might load the variable into a register before further processing, then write it back to memory afterwards. Mostly depends on CPU instruction set. If you want to be sure, you will have to check the disassembled code or write the code in assembler.

Otherwise, you can make a "poor man's mutex" with a bool. This only works for the specific case of microcontroller ISRs that cannot be interrupted by other interrupts. Since you know the ISR can't be interrupted, you can do this:

static volatile bool busy;
static volatile uint16_t shared;

void isr (void)
{
  if(!busy)
  {
    shared = something;
  }
}


void main (void)
{
  ...

  busy = true;
  do_something(shared);
  busy = false;

  ...
}

With this approach, it doesn't matter if busy or shared are atomic or not. No matter where the interrupt triggers, shared will not get destroyed mid-access.

Lundin
  • 195,001
  • 40
  • 254
  • 396
1

There is no guarantee that any generic integer variable will be written and read atomically. If you need such a guarantee you should use the sig_atomic_t type. It is the only type with such a guarantee.

From the C99 standard 7.14:

2 The type defined is

sig_atomic_t

which is the (possibly volatile-qualified) integer type of an object that can be accessed as an atomic entity, even in the presence of asynchronous interrupts.

Community
  • 1
  • 1
D Krueger
  • 2,446
  • 15
  • 12
  • `sig_atomic_t` is a boolean type. And C99 is not standard C. Standard C provides atomics optionally. – too honest for this site Dec 21 '16 at 22:33
  • @Olaf Well the standard writers surely wasted time by including `SIG_ATOMIC_MIN` and `SIG_ATOMIC_MAX` if you're right about`sig_atomic_t` being a boolean type. But they also saved time by not changing the definition of `sig_atomic_t` between C99 and C11. So there's that. – D Krueger Dec 21 '16 at 23:09
  • (Sorry, I was wrong about `sig_atomic_t` being a boolean type). Anyway, `sig_atomic_t` is part of `signal.h` and not required for a freestanding implementation&environment and most don't provide it. OP seems to use such an environment. `stdatomic.h` OTOH **is** provided for some implementations, e.g. by gcc on ARM platforms. So that's certainly a better idea to use. Also that header provides information if the standard types are accessed atomically (`ATOMIC_INT_LOCK_FREE` etc.) which can e.g. be tested with a static assertion (which makes your first statement questionable). – too honest for this site Dec 21 '16 at 23:19
  • @Olaf Regarding the atomics defined in stdatomic.h - they are defined in the standard in terms of threads and visibility. In a freestanding implementation of C, without any threads (and an ISR is not a separate thread), you are not able to provide standard-confomring atomics. Although it's true, that it is easy to imagine what such atomics should be in a freestanding environment without threads. – mrn Dec 23 '16 at 08:02
  • @mrn Tell this the gcc developers, because they **do** sopport `stdatomic.h` and the keywords even for e.g. ARM. And gcc **is** a freestanding implemention. Once you see how they implement them, you might want to re-read the standard. Atomics in C (and C++) do **not** depend on a specific implementation of threads! They are exactly meant to provide primitives which typically map to specific CPU instructions (barriers, exclusive load/store, `CAS` etc.). The mentioning of threads is just because they also have to work with atomics. As much as many other places in C11. – too honest for this site Dec 23 '16 at 13:38
  • @Olaf It's true - C11 atomics do not depend on a specific implementation of threads. But the semantics of atomics is defined based on the term thread. Formally, you need to either define a thread in a specific implementation and name atomics as standard-conforming or you need to define the semantics of atomics from scratch. In practice, C11 atomics' semantics is understandable without a definition of a thread. But it's not true that "mentioning of threads is just because they also have to work with atomics". A thread is a fundamental term used to define the semantics of atomics. – mrn Dec 23 '16 at 23:41
  • Formally, if you use C11 atomics in a freestanding implementation of C, you still have to look into the implementation decumentation to see how a thread is defined to be sure your program is correct. – mrn Dec 23 '16 at 23:46
  • @mrn Please provide a reference where the `_Atomic` type-specifier/qualifier requires an OS implementation of threads. The standard uses the term here in the context of "concurrent execution paths", not some library. Actually thread libraries use these atomics to implement their synchronisation functions, not vice versa as you seem to imply. The most basic (and on bare-metal still very common) example is a single (linear) main program and interrupts handlers. That's exactly what OP has and where atomics are the safest and easiest implementation of communication. And yes, an ISR **is** a thread – too honest for this site Dec 24 '16 at 04:34
  • @Olaf I didn't say atomics require OS implementation of threads. I pointed out what was incorrect in your comment (mentioning of threads is just because they also have to work with atomics) and noted that thread is not defined in the standard. Formally, to know what _Atomic means on particular platform you need a definition of thread for that platform. And regarding an ISR being a thread - Pete Becker's answer here http://stackoverflow.com/questions/41190204/how-to-understand-atomics-in-a-freestanding-c-or-c-implementation convinces me. – mrn Dec 25 '16 at 00:42
  • As I wrote, the standard uses "thread" in this context to refer to concurrent execution. There is no demand for a specific implementation, as atomics are low-level mechanisms. Simply put: a CPU which does not support certain machine instructions will not be able to support atomics. That's why they are optional. Nevertheless, any good compiler will support atomics if the CPU does. This is true e.g. for x86 and ARMv7 . That's why e.g. gcc provides built-ins for atomic operations. – too honest for this site Dec 25 '16 at 02:58
  • @Olaf What you write is a reasonable practical approach however it is not formally correct logic. You cannot make guarantees based on the intentions of intoducing atomics into the standard. The same way you can make similar guarantees for using volatiles in ISRs as C99 rationale says they are intended for memory-mapped I/O and objects shared among multiple processes. – mrn Dec 25 '16 at 12:02
0

Check your compiler. ISR's are non-standard. Also, C has no real notion of "the processor natural word" other than perhaps int.

MSalters
  • 173,980
  • 10
  • 155
  • 350
0

The answer is "sometimes". If the either ISR or the main process alters the variable, then you are OK. However if both manipulate the variable, something like

main
Var = Var + 10

ISR
Var = Var + 10

Then who knows? You would think the final result would be var + 20, but if in assembly the the sequence is

Main - Get Var
ISR -            Get Var
                 Add 10
                 Store Var
                 Return
       Add 10
       Store Var

Then the final result will be 10, not 20

As the previous poster said you will need to have some protection code to prevent this.

malaugh
  • 167
  • 6