Electronic – Is disabling time sensitive interrupts in C a bad idea

cinterruptsmicrocontroller

I sometimes see code that disables interrupts, for example to perform a non-atomic read/write to a global variable used in an ISR. On AVR with gcc, this may look like:

ExpensiveOperation();
cli();
// Perform a non-atomic read/write
sei(); // Assume that it is acceptable to enable global interrupts here.

The cli and sei macros expand to asm volatile statements with memory barriers. The volatile keyword ensures that the cli/sei instructions are not optimized out, while the memory barriers ensure that if the non-atomic read/write is to a volatile variable, it will occur between the cli and sei instructions. However, this page suggests that nothing is preventing the compiler from putting ExpensiveOperation after the cli instruction.

Interrupts often require precise timing. If disabling interrupts in C risks having expensive operations run with interrupts disabled, should timing critical interrupts only be disabled in inline assembly (or should your program be rewritten to use only atomic reads/writes)?

Best Answer

Depending upon what kind of side-effects ExpensiveOperation() has, it may be illegitimate for a compiler to move it beyond a call to a function that the compiler can't "see" into if, from the compiler's point of view, there would be a possibility of such a function making use of such side-effects. It would be helpful if there were a standard function, typically implemented by an intrinsic, which would in actuality do nothing, but which a compiler would be required to treat as though it might do anything. Compilers which treated such a function as an intrinsic could avoid having to actually generate a useless "call" instruction [or any other code] for it beyond the fact required to ensure that any global variables which are cached in registers would get flushed before the "call".

Unfortunately, while calling an outside function would probably force a compiler to process ExpensiveOperation() in its entirety first, I don't know of anything that would 100% eliminate the possibility of a compiler identifying portions of that function whose execution could be deferred. Still, adding a sequenced side effect to ExpensiveOperation() and calling an outside function that would require any such side effect would need to have been completed may be the best one can hope for.