The next issue we should tackle is the list of functions an ISR is allowed to call.
Let me digress just a little at this point. Historically, the reason that ISRs were so difficult to write (and still are in most other operating systems) is that the ISR runs in a special environment.
One particular thing that complicates writing ISRs is that the ISR isn't actually a proper thread as far as the kernel is concerned. It's this weird hardware thread, if you want to call it that. This means that the ISR isn't allowed to do any thread-level things, like messaging, synchronization, kernel calls, disk I/O, etc.
But doesn't that make it much harder to write ISR routines? Yes it does. The solution, therefore, is to do as little work as possible in the ISR, and do the rest of the work at thread-level, where you have access to all the services.
Your goals in the ISR should be:
This architecture hinges on the fact that QNX Neutrino has very fast context-switch times. You know that you can get into your ISR quickly to do the time-critical work. You also know that when the ISR returns an event to trigger thread-level work, that thread will start quickly as well. It's this don't do anything in the ISR philosophy that makes QNX Neutrino ISRs so simple!
So, what calls can you use in the ISR? Here's a summary (for the official list, see the Full Safety Information appendix in the QNX Neutrino C Library Reference):
Basically, the rule of thumb is, Don't use anything that's going to take a huge amount of stack space or time, and don't use anything that issues kernel calls. The stack space requirement stems from the fact that ISRs have very limited stacks.
The list of interrupt-safe functions makes sense—you might want to move some memory around, in which case the mem*() and str*() functions are a good choice. You'll most likely want to read data registers from the hardware (in order to save transitory data variables and/or clear the source of the interrupt), so you'll want to use the in*() and out*() functions.
What about the bewildering choice of Interrupt*() functions? Let's examine them in pairs:
Keep in mind that InterruptMask() and InterruptUnmask() are counting—you must unmask the same number of times that you've masked in order for the interrupt source to be able to interrupt you again.
By the way, note that the InterruptAttachEvent() performs the InterruptMask() for you (in the kernel)—therefore you must call InterruptUnmask() from your interrupt-handling thread.
The one thing that bears repeating is that on an SMP system, it is possible to have both the interrupt service routine and another thread running at the same time.