On small embedded systems that don’t use an operating system, there is often a mix of a main loop and system interrupts for controlling the system. One problem with interrupts is spelled out in their name: they interrupt normal code. This can change variables being used by other code, while they are being used. This can be useful if you are waiting for an event. For example:
Assume someInterruptFunction is called by an interrupt routine and the while loop is in the program’s main function. One important note here is that hasInterruptOccured is marked volatile. That tells the compiler the value could change at any time. Otherwise, the optimizer will assume that since the while loop never changes hasInterruptOccured then it never needs to check it again. Being volatile means the optimizer will make no such assumptions.
The above is a useful example of variable being changed by interrupts, but it can be problematic.
Consider this code below. A function is called periodically to make calculations from x, but x is changed from an interrupt source.
Now consider what happens if x can change at any time. The equation might actually result in the following:
Where only two of the set of xn are equal. We never know when an interrupt might occur, and should plan that is can occur anywhere. One solution to the above is to use an intermediate to hold the value of x.
This would seem to solve all our problems, but it might not. Consider this code on an 8-bit processor:
You might think that xHold will capture the value of x, but it may not. That is because on an 8-bit system, storing the value of a 16-bit integer takes 2x 8-bit operations, any one of which might be interrupted. This means xHold could change in horrible ways.
Consider x=256 or 0x0100, and an interrupt will change the value to 255 (0x00FF). Now if the assembly code starts the copy with the most-significant byte, then gets interrupted, the result in xHold will be 511 (0x01FF). That’s because it read the most-significant byte when that was set to 0x01, and after the interrupt read the least-significant byte which was now 0xFF.
The typical solution to avoiding such issues is simply to disable interrupts when reading volatile variables.
Now there is no way for x to change when reading.
There is a second way to accomplish the same thing without disabling interrupts.
What’s going on here is that we check to see if x matches what we just read. If it doesn’t, x changed either during the storing, or the reading check. In either case, we simply try again. This works great for things such as system timers, and doesn’t require disabling every interrupt in the system just to get a value. It is my preferred way to fetch a single volatile value, and I usually call this the poor man’s semaphore as the same type of scenarios happens with threaded tasks. With threads you can simply use a semaphore or mutex to wrap the reading area, but you could use the same do-while trick.
Now for how to mess this up. In many projects I use a micro-controller timer as a system clock that counts in milliseconds or tens or hundreds of milliseconds per tick. The interrupt for the timer is very simple:
Then I make a function to get the system time.
The other day I found I had made a mistake in implementing this function. Here is what I had instead:
The difference is very subtle, but messed up all the work done to ensure the value didn’t change. If the above code were to run on an 8-bit micro-controller, the function could return bad times. I got lucky. In my case, the system was a 32-bit micro-controller with 1 ms ticks and the system time was a 64-bit value. The timer only modifies the upper 32-bit once every 49 days. So the chances for reading an incorrect timer value is very low. Still, a stupid mistake.