Assume you're into embedded programming. Assume you have some kind of run time system or operating system and doing it all in ANSI C. This is no real-time language, like occam, which would disallow all constructs discussed here. However, with C, we'll certainly have to make the best of it. Then, the problem: how to let a process (we could also call it task or thread in this note), ...how to let a process not manipulate data which, any microsecond later - or worse - just now, may also, later be manipulated by an interrupt function. Just when the process increments a value, just while it's been moved from RAM to internal register. The value is 10. Then it happens: the interrupt arrives and the processor hw sets the process aside, to let the interrupt have the machine. The interrupt decides to decrement that same value to 9 and stores it before it returns. The RAM will contain 9 until the process shortly overwrites it with 11. Confusion in embedded programs may or may not be discovered. But we sleep better (or stay in the company longer) the less such possibilities we leave behind us. This problem was solved in the sixties by the Dutch computer scientist Dijkstra. However, in our system we can't use his solution, the semaphore. We dropped semaphores in our runtime system, which is message based. Besides, with semaphores, they may not be suitable for use in interrupt, because blocking there is a goof idea. Communicating between processes and interrupts defines
hot-spots process code, where access to common data, like buffers, must
run uninterrupted. Critical
regions in process code must be protected,
f.ex. by disabling interrupts over the region. Or better, disable the
actual interrupt, to remove race conditions. |
... Forward refs Local_ForExternOnly_ |
|
Above: screen "clip" from my folding editor Winf (see [11]) | ||
Now, say that
you have access
functions and interrupt functions in the same C file, an
ok idea. If the process calls an access function, which calls a
function which may also at any time be called from the interrupt, we're
lost. Reentrant common functions here is not a good idea.
It is not as difficult as one may think, to keep track of which function calls which. Introduce a naming scheme. The CREW rule (Concurrent Read, Exclusive Write) governs access to common data. It spells: if you have at least one writer, then all access must be exclusive, and both parties must ensure that all writes and (yes!) reads are exclusive. Or, always do it, why should not at least one write? Don't make your own rule. Ohms law can't be improved. If one part reads and one writes and you think you know the roles at any time, to let go of protection, still you should protect. If you rely on the atomicity of op-code, beware, it's a shaky foundation. Another compiler, or another optimization level may break your assumptions. Or you may forget to check. An interrupt, by its very nature, defines its own exclusiveness. Provided any higher level interrupts do not in any way touch the data. Here is my suggestion: |
At any stage,
rename all instances:
It is the last point that saves your project. Saves it from strange errors that don't easily reproduce. Saves you from service trips. However, observe that if the software you fixed is a lower level protocol driver and it continues to run after the glitch or race or hazard of this stupid integer, a higher OSI level will do a retransmission for you making you ignorant of the error. Ignorant that some green lights out there went on too early. However, lawyers are good at finding telephone numbers. Then, you try to explain. |
Other publications at http://www.teigfam.net/oyvind/pub/pub.html