Dado um microcontrolador que está executando o seguinte código:
volatile bool has_flag = false;
void interrupt(void) //called when an interrupt is received
{
clear_interrupt_flag(); //clear interrupt flag
has_flag = true; //signal that we have an interrupt to process
}
int main()
{
while(1)
{
if(has_flag) //if we had an interrupt
{
has_flag = false; //clear the interrupt flag
process(); //process the interrupt
}
else
sleep(); //place the micro to sleep
}
}
Suponha que a if(has_flag)
condição seja avaliada como falsa e estamos prestes a executar a instrução de suspensão. Logo antes de executarmos a instrução sleep, recebemos uma interrupção. Depois de deixarmos a interrupção, executamos a instrução sleep.
Essa sequência de execução não é desejável porque:
- O microcontrolador foi dormir em vez de acordar e ligar
process()
. - O microcontrolador nunca poderá ser ativado se nenhuma interrupção for recebida posteriormente.
- A chamada para
process()
é adiada até a próxima interrupção.
Como o código pode ser escrito para impedir que essa condição de corrida ocorra?
Editar
Alguns microcontroladores, como o ATMega, possuem um bit de habilitação para suspensão que impede que essa condição ocorra (obrigado Kvegaoro por apontar isso). O JRoberts oferece um exemplo de implementação que exemplifica esse comportamento.
Outros micros, como os PIC18s, não possuem esse bit e o problema ainda ocorre. No entanto, esses micros são projetados de modo que as interrupções ainda possam ativar o núcleo, independentemente de o bit de ativação de interrupção global estar definido (obrigado supercat por apontar isso). Para essas arquiteturas, a solução é desativar as interrupções globais antes de dormir. Se uma interrupção for acionada imediatamente antes de executar a instrução de suspensão, o manipulador de interrupções não será executado, o núcleo será ativado e, quando as interrupções globais forem reativadas, o manipulador de interrupções será executado. No pseudo-código, a implementação seria assim:
int main()
{
while(1)
{
//clear global interrupt enable bit.
//if the flag tested below is not set, then we enter
//sleep with the global interrupt bit cleared, which is
//the intended behavior.
disable_global_interrupts();
if(has_flag) //if we had an interrupt
{
has_flag = false; //clear the interrupt flag
enable_global_interrupts(); //set global interrupt enable bit.
process(); //process the interrupt
}
else
sleep(); //place the micro to sleep
}
}
interrupt_flag
como um int
e incrementá-lo sempre que houver interrupção. Então mude if(has_flag)
para while (interrupts_count)
e depois durma. No entanto, a interrupção pode ocorrer depois que você sai do loop while. Se isso for um problema, faça o processamento na própria interrupção?