http://www.eetimes.com/discussion/break-points/4027634/Disabling-InterruptsDisabling InterruptsJack Ganssle3/22/2010 11:48 AM EDTI wish we lived in an atomic world.
No, I am not yearning for an Iranian bomb. Rather, I am referring to the fact that unavoidable non-atomic accesses to shared resources causes much grief and poor code. I read a lot of code, and find much that handles non-atomic accesses in this manner: long global_var; // Handle a non-atomic access to "global_var" This construct suffers from a number of problems, not the least of which is that it's not generally reuseable. If the function is called from some place with interrupts off, it returns with them on, disrupting the system's context. Usual solutions involve saving and restoring the interrupt state. But that, too, is fraught with peril. Optimizers are very aggressive today, and in some cases can reorder statement execution to the point where interrupts aren't disabled at the right point. The result: all that atomic-work may not work. I've asked several compiler vendors for their take, since they understand the optimizations the compilers do better than anyone. The most interesting and complete response came from Greg Davis of Green Hills Software, and he has graciously allowed me to reprint it here: "What we recommend for Green Hills customers is to use intrinsic functions for disabling and restoring interrupts. What this looks like is: #include // Code that handles global_var in a non-atomic way // Restore interrupts to state expressed by
"key" These Green Hills intrinsics for __DIR() and __RIR() generate different assembly code depending on the architecture and CPU that you are compiling for, but their interface is the same. The compiler considers the system-instructions that these intrinsics generate to be non-swappable, so the code that manipulates global_var will not be swapped across them. With GNU, people tend to prefer to use inline assembly. These assembly statements are typically embedded in inline functions or macros with GNU statement expressions. An implementation of something like this on an ARM7TDMI might look like: static inline unsigned int static inline void restore_interrupts(unsigned int
state) int global_var; // Code that handles global_var in a non-atomic way restore_interrupts(key); At least to my understanding, the combination of the declaring the assembly to be volatile and putting the "memory" in the clobbers list should ensure that memory accesses in the critical section stay in the critical section. Both of the above approaches involve compiler-specific extensions. The best approach I'm aware of that isn't compiler specific is to move the code into another file so it just looks like a function call to the compiler: extern unsigned int
disable_interrupts_reentrant(void); // Code that handles global_var in a non-atomic way restore_interrupts(key); and then to define the functions in a separate assembly file. The exact assembly syntax may vary between implementations, but it may look something like this on a traditional UNIX-style assembler. .text .globl restore_interrupts Since compilers need to assume that external functions read and write all global variables, there's no chance for the code that handles global_var to fall outside of the critical section." Thanks, Greg, for the insight. I hope this information is useful to folks. Jack G. Ganssle is a lecturer and
consultant on embedded development issues. He conducts
seminars on embedded systems and helps companies with their
embedded challenges. Contact him at j...@ganssle.com. His
website iswww.ganssle.com. |