When should one use polling method and when should one use interrupt based method ? Are there scenarios in which both can be used ?
Sometimes you actually need to use both. For example if the events are sporadic but come in a high speed burst; you may need to first respond to an interrupt, and then before re-enabling the interrupt poll to see if another event has already occurred avoiding some of the overhead of the interrupt context switching. I believe Linux Network Interface operates in this mode.
It is much better to go with Interrupt based design
compared to polling based
because polling based is flawed in the sense that it expects the data to be returned on every poll. Now, you might say that I will get around this case where a single poll has returned me an error but why the heck waste all the CPU cycles polling for something when it could as well return an error ?? And to expect a poll might fail is practical product scenario.
Interrupt based designs
make even more sense when there is a lot of layers of functions involved in a single poll. To me its a common practice: Would you keep asking (polling) your friend again & again everyday whether he has the information you need OR would you just tell him that interrupt
me when you have the information I need. I think we do the right thing in day to day lives but fail to realize.
But interrupt based architectures
when implemented require solid understanding of the publish-subscribe design principle
. And, when done in app domains, they require the part of the code sending interrupts to be really well written. This good as it squeezes the complexity to one place as well.
Additional to above, following are the other advantages that polling based architecture provides you free of cost:
Whenever you are designing sw
& you have this choice, you should always choose an interrupt
based design over polling
based, because an interrupt
based design can fill up for polling
based situation using listeners but a polling based design can never fulfill the requirement needing interrupt
based design.
Following is a brief comparison matrix:
-INTERRUPT- -LOOP-
Speed fast slow
Eficiency good poor
CPU waste low high
multitasking yes no
complexity high low
debugging +/- easy easy
critical in time excellent poor
code bloat low impact high impact
Polling should be avoided where possible, as it typically eats a lot of CPU cycles unnecessarily (unless either (a) you are only going to poll for a short time or (b) you can afford to sleep for a reasonable time in your polling loop). Wasting CPU cycles is bad not only from a performance perspective, but it also drives up power consumption, which may well be an issue for battery-powered embedded applications.
You don't want to have your host waiting in the busy loop for a long time, and also polling can become inefficient when frequent checks are made for data that is not there frequently. So there for, if t he host and the device are both fast, then polling if pretty fast.
Interrupts are preferred when low latency is required. If you poll for some condition N times per second, then on average you will discover that condition in time one half of 1/N after it has actually happened.
Polling is sometimes preferred when absolute deterministic timing is required. By their very nature, interrupts can occur at unpredictable times and greatly complicate timing analysis, whereas with polled systems, it is relatively easy to make provable statements about deadline satisfaction.
When deciding upon polling or interrupt you have to fully understand the nature of the event you are trying to follow and your response to it.
Interrupts require no processing when nothing is happening, but require all your attention when something is happening. If the event is external and has noisy edges or fast pulses then this can cause major headaches with interrupts, you have to be careful around the setup of interrupts.
In this example the interrupt routine is responding to a laser beam having become clear and is setting itself up for an event where it becomes blocked:
BEAM_INTR_EN = TRUE; /*re-enable the beam interrupts*/
/*Set the beam interrupt for the next clear to blocked event*/
BEAM_INTR_EDGE = CLEAR_TO_BLOCKED;
BEAM_INTR_FLAG = FALSE; /*Clear the interrupt*/
There are 2 weak points of this code: 1) If the laser beam has become blocked again before the interrupt flag is cleared (BEAM_INTR_FLAG = FALSE;). The interrupt will have been missed and the code will be out of sync with the laser beam state.
2) When setting up interrupts in either in the background routine or for a higher priority than the priority this code is on, care must be taken when enabling the interrupt. If the interrupt flag was already set (incorrectly) prior to it being enabled, the interrupt routine would be called incorrectly as soon as it was enabled and maybe for the wrong edge.
The easiest way to fix 1) is to double check after you set up the interrupt, if it has occurred then force an interrupt. To fix 2) move the enabling of the interrupts to after the the double check:
/*Set the beam interrupt for the next clear to blocked event*/
BEAM_INTR_EDGE = CLEAR_TO_BLOCKED;
BEAM_INTR_FLAG = FALSE; /*Clear the interrupt*/
/*Double check beam state to see if it has already gone blocked*/
if (BEAM_STATE == BEAM_BLOCKED)
{
BEAM_INTR_FLAG = TRUE; /*Force the interrupt to re-enter the ISR after exiting*/
}
BEAM_INTR_EN = TRUE; /*re-enable the beam interrupts*/
The forcing of the interrupt makes the system work with the same state machine, just forcing it round manually to cover the blind spot.
Basically:
Set the edge to detect the next interrupt event
Clear the interrupt flag
if (the event has already occurred)
{
Set the interrupt flag to force the interrupt
}
Enable the interrupt
If the time of the response to an event is has to be consistent (e.g. 1ms +/-10us after after the input line goes high, transmit the event signal) then interrupts are usually best.
If the time of the response to an event is has to be within a certain time (e.g. within 1ms of the input line going high, transmit the event signal), then an interrupt would be best.
The problem with interrupts is you have to start thinking about threading and that two pieces of code can access the same data at the same time.
Interrupts are also good for allow processors to go into low power modes (sleep/idle etc.) whilst waiting for something to happen.
Having said all that polling can give very tight time responses to events if there is only one thing for the processor to do, often interrupt hardware takes several cycles to respond to an event whilst a tight polling loop will do.
If the event is none timing critical and potentially noisy (e.g. someone pressing a switch) then polling allows simple filtering without missing the long term transitions. A common mistake is to poll multiple times when setting things up:
void fnInitialiseSystem(void)
{
if (MODE_INPUT == MODE_A) /*First polling of the MODE_INPUT*/
{
PR2 = PR2_MODE_A;
}
else
{
PR2 = PR2_MODE_B;
}
OpenTimer2( TIMER_INT_ON &
T2_PS_1_1 &
T2_POST_1_8 );
if (MODE_INPUT == MODE_A) /*Second polling of the MODE_INPUT*/
{
CurrentMode = MODE_A;
PROBE_INT_EDGE = CLEAR_TO_BLOCKED;
}
else
{
CurrentMode = MODE_B;
PROBE_INT_EDGE = BLOCKED_TO_CLEAR;
}
}
In the above example MODE_INPUT is an external switch, if the two times MODE_INPUT is polled differ then the behaviour is unexpected. When reading these kinds of signals it is best to use filtering to decide upon the long term state of the input, and perform actions on the filtered version.
For example with switch de-bouncing just check a switch regularly (every 1ms?) and if a number of them (say 16) are different (switch closed) from the filtered version (switch open) then update the result and perform the action required. Be careful with signal aliasing, an oscillating signal may look stable!
An example of use of polling and interrupts is, again, for the use of a input which doesn't change often but is noisy when it does. Yet again a switch is a good example of this: the code can set up an interrupt to check for a change in the switch state, when an interrupt occurs then the switch can be regularly polled until the switch state is "stable" (either changed state or back to what it was). This gives the advantage of low processing overhead when nothing is happening, and noise filtering when something is happening.