It seems that a well-timed interrupt just before executing the CPU “sleep” instruction could wind up sleeping the system when it shouldn’t be sleeping. We saw a key click happen while preparing for a system SLEEP, the interrupt happened, the message was queued to the foreground task, then SLEEP was executed. This was because stepping through code with the emulator widened the timing window, but it seems it could also happen in the field, maybe with something more dangerous than a keystroke event. How is this race condition handled?
I suppose it depends a bit on the form of sleep you are going into, but if the sleep instruction is in the idle hook, then when the key click happens the foreground task should preempt the idle task to do its operation, and when it finishes then the CPU can go to sleep.
If you can’t use preemption, then you need to disable the interrupts while you check and prepare for sleeping, and end with re-enabling the interrupts and going to sleep as an effectively atomic operation where any pending interrupt occurs during the sleep waking the processor back up.
The solution for this CPU was to put the idle hook routine in RAM, which is basically one instruction (sleep or halt) followed by a return. There is another routine that modules can call to say whether or not they are ready to sleep. If all modules “can sleep” then the routine re-writes the 1st instruction of the idle hook with SLEEP. If any modules can’t sleep then the routine re-writes the 1st instruction with HALT. So, if the idle hook is just about to sleep and then an interrupt happens and sleep isn’t possible, the instruction is changed and sleep doesn’t happen after all.
This is not a solution for all CPU’s. AVR and PIC couldn’t do this. CPU’s with high-performance pipelines or instruction caches might not be able to do it. Richard Damon’s interlocked “sleep + enable interrupt” instruction would be a much simpler solution. It’s just that my CPU (S1C33) doesn’t have such an instruction, although fortunately it can execute from RAM.
I don’t know of any with that specific instruction, but on some cpus, the enable interrupt instruction will not reenable interrupts until the next instruction get put in the pipeline, so that works.
If you are not using preemption, then the wait for the next interrupt to wake you from the sleep (like a timer tick) may be unavoidable. If you are going into a deep sleep where the timer isn’t going to be working you are going to need something like the interlock trick I was describing or the code modification trick described or live with a small window with the chance of sleeping when you have something to do.