-2

In multi tasking environment. If a task has the expression y = x + x; is there a possibility of an interrupt(task switching) occurring between the two reads of x.

Anuj Priyadarshi
  • 347
  • 4
  • 16
  • short answer is that it's unlikely but possible – M.M Jul 15 '16 at 11:19
  • If `x` is an object that is modified concurrently by another thread, and/or asynchronously by a signal handler, and `a` is not atomic or either lock-free atomic or of type `volatile sig_atomic_t` in case of the signal-handler, the behavior is *undefined*. – EOF Jul 15 '16 at 11:44
  • On some architectures, it isn't even guaranteed that a single `x` can be fetched with a single atomic instruction (`long long`on a <64-bit architecture, for example). So generally, not even `y = 2 * x` would be thread-safe without locking the variable. – tofro Jul 15 '16 at 12:46
  • The above three comments could usefully be elaborated into answers - answers should not be posted as comments. A legitimate use of comments would be to have explained why the question deserved downvotes - it seems a legitimate question to me - albeit with a somewhat vague title. – Clifford Jul 16 '16 at 18:52

2 Answers2

1

In most cases a compiler generate code would read x only once since the optimisation is trivial. In fact it might even translate the operation into a right-shift, equivalent to x << 1. However you cannot guarantee that, and if x were declared volatile it would necessarily make two reads that will then be uninterruptible between reading of the low-order and high-order words (or vice versa),and in that case the assignment of y is also interruptable.

Another issue is that if x were not an atomic data type (i.e. could not be read in a single instruction) such as a 32 bit type on a 16 bit target for example, then even if it were a single read, it may be interruptable.

In either case it is only normally a problem if the x itself (or y in the second case) is shared between contexts (in which case it should also be declared volatile so two reads would then be necessary), or if for some reason the timing of the assignment were somehow critical and needed to be entirely deterministic (unlikely).

If x is shared and therefore volatile but is an atomic type, the simple solution in this example is to write the expression y = x << 1 or y = x * 2 to ensure that x is read only once. Where x is not atomic - you might need to lock the scheduler or disable interrupts (i.e. use a critical section), or more selectively protect the access with a mutex for example. For more complex expressions where the expression cannot be reduced to a single reference to the shared variable, simply assigning the variable to a non-shared local temporary variable ensures that it is read just once. The issue of atomicity still stands.

Clifford
  • 88,407
  • 13
  • 85
  • 165
-1

Here a specific answer for x86 like architectures, used with a traditional scheduler (https://en.wikipedia.org/wiki/Completely_Fair_Scheduler). Have a look at this asm code, generated for x86 by gcc with optimization set to -OS0.

https://godbolt.org/g/AfKTkF

int main(void) {
  int y;
  int x = 5;
  y = x + x;
}

turns to

main:
        pushq   %rbp
        movq    %rsp, %rbp
        movl    $5, -4(%rbp)
        movl    -4(%rbp), %eax  #read x
        addl    %eax, %eax      #execute x + x
        movl    %eax, -8(%rbp)
        movl    $0, %eax
        popq    %rbp
        ret

As you can see x is only read once, thus implying that even if there WAS a task switch, there would be no second read on x.


Edit: EOL states in the comments that x indeed can be read two times, modifying the code above yields following:

volatile x = 5;

forces the compiler to insert two movl operations (instead of one):

movl    -8(%rbp), %edx
movl    -8(%rbp), %eax

This does not make my first guess wrong though. The compiler would optimize the read access of the variable (if it is a simple type) and remove the second read. The volatile keyword forces the compiler to access the latest value in the registers. The first code won't force that read - thus giving a other result in case of thread switches and modification of x.

Haini
  • 922
  • 2
  • 12
  • 28
  • 4
    This only answers for the case of one specific hardware and software combination – M.M Jul 15 '16 at 11:18
  • @M.M Given the fact that there is no hardware / software combination stated in the question I assumed a popular and widely used one. And given the fact that a context switch (http://www.linfo.org/context_switch.html) is always pretty much the same. I can edit the question to state clearly that this is not generally valid. Would this remove the downvote from me? – Haini Jul 15 '16 at 11:23
  • Addressing the other downvotes: Could you please Elaborate? I can see that I only cover a special case, it still answers the question from the OP. – Haini Jul 15 '16 at 11:52
  • 1
    The C standard does *not* guarantee that `x` will only be read once in this expression. In fact, as far as the abstract machine is concerned, `x` is read twice (you can test this by `volatile`-qualifying `x`). If `x` is modified concurrently, the behavior is **undefined**. – EOF Jul 15 '16 at 12:02
  • @EOF: That is very true. We shouldn't loose focus though. OP asks if it is possible that a interrupt happens between those two reads. My first suggestion was that - given the code we have - no interrupt can happen as there is going to be only one movl instruction. What happens with x's value is not in our scope. Declaring x volatile will force two read instructions, thus an interrupt is possible between those two. Or am I completely stuck on this one? – Haini Jul 15 '16 at 15:32
  • @Haini: What a single compiler does for a single platform in a single contrived test is irrelevant, you cannot rely on this. The only reliable thing is to know which guarantees the language gives you. Caching the value of `x` for reuse in the expression is not among the language's guarantees. – EOF Jul 15 '16 at 16:18
  • @EOF: I understand your point now, thanks. I was somehow confused by it. So.. Do you suggest that I incorporate this into my answer or should I remove the answer all along as neither the question nor the answer are well received currently? Thanks for your time and explanation! – Haini Jul 15 '16 at 16:22
  • @Haini: I suppose you *could* write an interesting answer to this question. I'd start with explaining what guarantees C gives for this situation (very few), and possibly discuss *why* the compiler is even allowed to reuse the value of `x`, even though the abstract machine would need to load it twice (this gets into the as-if rule and undefined behavior on concurrent access to non-atomic, non-readonly objects). – EOF Jul 15 '16 at 16:27
  • While x86 is "*popular and widely used*" in desktop systems with a GPOS, it is less so in applications that might use an RTOS perhaps. I would not down vote on that basis however - it if fairly clear that this is a specific example of a specific compiler with specific compiler options and a specific instruction set. How *useful* that is the OP can decide. – Clifford Jul 16 '16 at 18:08
  • ... Referring to your answer as a "guess" though might weaken your argument. We should not be "guessing". Looking at the code the compiler generated is a dangerous way of determining the "rules". A different compiler, different compiler options, or a different target might produce a different result. – Clifford Jul 16 '16 at 18:58