This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

How to make an interruptable code sequence in C?

I am looking for opinions on how to create a code sequence that is written in "C" that must be performed as an uninterruptable sequence. I want to disable interrupts (globally) execute a code sequence and then re-enable interrupts. I am looking for your inputs as I don't see how to guarantee this from what I know about the "C" standard. I think the compiler is allowed to optimize the sequence so that the actual linear code could be placed outside my expected enable interrupt and disable interrupt sequence (start/end points). The compiler knows that the enable/disable of the interrupt is volatile and must be performed but it doesn't know that there is an architectural dependency to the code order I want. In other words it could be that part of my sequence gets optimized outside of where the interrupt is not globally disabled. As the opcode creation behavior is still correct but it is not from a system behavior point of view.

Outside of writing it in assembly has anyone experienced this and how did you end up handling it. Thanks in advance for your inputs.

  • Outside of writing it in assembly has anyone experienced this and how did you end up handling it.

    Depending on the compiler you use, it may already offer facilities to make parts of the code or whole functions uninterruptible.

    If you use an operating system, it may offer such facilities, too.

    If you are really worried about wild compiler optimizations resulting in things being done completely out of the intended sequence, there's always the option of checking the generated assembly code for conformity to the expectations and adjusting the optimization level if the instructions are not in the correct order. Usually, the compiler manual will state which optimizations are permitted at which optimization level.

    There are also other options, like using a "wrapper" function written in assembly which takes care of disabling interrupts, calls the C function that is supposed to be uninterruptible, and then restores the interrupt state.

  • I think the compiler is allowed to optimize the sequence so that the actual linear code could be placed outside my expected enable interrupt and disable interrupt sequence (start/end points).

    I may have misunderstood your question, but:

    If you have code that looks like this:

    Disable();
    Do something;
    Do something else;
    Do a bit more;
    Enable();

    then you can be sure that the middle three lines are executed after the first line and before the last line.

  • Alas the code sequence did look exactly like this. Using a very aggressive optimization and looking at the list file I did see part of the code did get placed outside my disable enable sequence. Hence my question.

  • This is waaaaaaaay outside the scope of the 'C' Standard - therefore you are going to have to rely upon implementation-specific features.

  • "Alas the code sequence did look exactly like this."

    I would be very interested to see the source code and the compiler's result.

    I've never seen any compiler do anything different to what Jack illustrated.

  • Alas the code sequence did look exactly like this. Using a very aggressive optimization and looking at the list file I did see part of the code did get placed outside my disable enable sequence.

    There are a number of optimisations such as 'common block subroutines' that will result in chunks of your translated code appearing in physically disparate places in the resulting assembly listing, this doesn't mean that those chunks are not executed after the disable() and before the enable(). It just means that the some of those instruction sequences also form part of the code elsewhere in your program. When they are executed as part of the code block you are concerned about they will be executed in the correct order.

    Are you sure that you are seeing a real reordering or is it just the fact that bits of the code are all over the place that's giving that impression? Have you traced the flow through by hand or in the debugger to confirm?

  • To emphasize the point:

    SwitchOffPower();
    ConnectGrannyAcrossLiveAndNeutral();
    DisconnectGranny();
    SwitchOnPower();

    I would be disappointed if my compiler reordered that one. Well, probably.

  • "Alas the code sequence did look exactly like this."

    I would be very interested to see the source code and the compiler's result.

    Me too.

    This is waaaaaaaay outside the scope of the 'C' Standard - therefore you are going to have to rely upon implementation-specific features.

    Not necessarily. The standard mentions the so called sequence points. It is my understanding that those mandate a certain order of execution in a compiled program.
    The general problem does exist, however. Interrupts and multiple CPU's do need more support in the language standard. I expect a new version of the standard in the future to include this.

  • The compiler wasn't Keil, but I was looking for opinions. Perhaps Keil considers the enabling/disabling interrupts as a sequence point.

    The disable and enable functions were inlined automatically to be:

    BCLR IEN ; global interrupt disable
    BSET IEN ; global interrupt enable
    

    The effect of using high optimization enabled the calls to be inlined thus eliminating the sequence points. Thus enabling/disabling interrupts in effect became simple reading/writing of memory locations from the compiler point of view with no dependency on the other variables (other than it was a volatile access). Enabling or disabling interrupts is not a FILE operation so no sequence point occurrence. The optimizer constraint was to make sure it was updated before hitting a sequence point. Unfortunately this resulted in undesirable behavior on my part. A volatile far pointer was being updated. But only part of the update was protected from interrupts and as luck would have it an interrupt occurred that used this pointer. A semaphore (mutex) was added in regards to this pointer to solve the issue (albeit we should have used one from the beginning).

    Sorry, in my case I may have killed your Granny

  • The effect of using high optimization enabled the calls to be inlined thus eliminating the sequence points.

    That would be a compiler bug, if I am not mistaken. It's not much consolation, though...

  • The compiler wasn't Keil, but I was looking for opinions.

    Does the compiler by any chance have an optimization stage that works on the generated assembly code without regarding the C code? "Instruction reordering" or something similar?

  • A compiler normally has stages working on the instruction level. But these stages must know about side effects. And these stages should still have access to meta-data from the source-level processing.

    But one problem with interrupts is that depending on where in the interrupt chain you perform your disable may affect if the disable is instant, or if there are one or more machine cycles after the disable instruction where you may still be hit by an interrupt.

    When the processor has a machine instruction to turn off interrupts, it may have a pipeline where one or more following instructions may already have been affected by the previous interrupt enable state.

    And when the disable is instead a write to an external interrupt controller, the internal busses in the processor may delay the write to the interrupt controller, allowing the processor to start new instructions.

    Because of this, you really have to read the datasheets for the processor and may have to add one or more NOP instructions between the disable, and the critical code section. It is very hard to find information about the lag between disable and until you enter the safe zone. And it isn't easy to test either since it is almost impossible to trig an interrupt at the exact clock cycle that maximizes this delay in relation to the disable instruction.

  • I guess it would depend on whether the compiler could determine any possible side-effects due to ConnectGrannyAcrossLiveAndNeutral() that might be affected by not having previously completed SwitchOffPower()...

    =:0

  • The compiler can't reorder operations unless it has 100% control. Whenever it can't see the individual instructions of a called function, it has to assume that the function is un-touchable. If the function is inlineable, the compiler may look at individual instructions from the function and mix them - but still only when not changing order of secondary effects.

    That is why the volatile keyword can be so important in multithreaded applications or when sharing variables with interrupt handlers. The compiler assumes that "normal" memory accesses can be reordered. A volatile variable that turns off the power will instantly tell the compiler that something magic happens that the compiler must not rearrange.

  • > That is why the volatile keyword can be so important in multithreaded
    > applications or when sharing variables with interrupt handlers. The
    > compiler assumes that "normal" memory accesses can be reordered. A
    > volatile variable that turns off the power will instantly tell the
    > compiler that something magic happens that the compiler must not
    > rearrange.

    volatile does not guarantee this for all situations. A nice
    summary of everything that can go wrong with volatiles is
    here: www.cs.utah.edu/.../emsoft08-preprint.pdf

    Enjoy!

    Marcus
    http://www.doulos.com/arm/