Étant donné un microcontrôleur qui exécute le code suivant:
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
}
}
Supposons que la if(has_flag)
condition soit fausse et que nous soyons sur le point d'exécuter l'instruction sleep. Juste avant d'exécuter l'instruction sleep, nous recevons une interruption. Après avoir quitté l'interruption, nous exécutons l'instruction sleep.
Cette séquence d'exécution n'est pas souhaitable car:
- Le microcontrôleur s'est endormi au lieu de se réveiller et d'appeler
process()
. - Le microcontrôleur peut ne jamais se réveiller si aucune interruption n'est reçue par la suite.
- L'appel à
process()
est reporté à la prochaine interruption.
Comment le code peut-il être écrit pour empêcher cette condition de concurrence critique de se produire?
Éditer
Certains microcontrôleurs, comme à l'ATMega, ont un bit d'activation de veille qui empêche cette condition de se produire (merci Kvegaoro de l'avoir signalé). JRoberts propose un exemple d'implémentation qui illustre ce comportement.
D'autres micros, tels que les PIC18, n'ont pas ce bit et le problème persiste. Cependant, ces micros sont conçus de telle sorte que les interruptions peuvent toujours réveiller le cœur indépendamment du fait que le bit d'activation d'interruption globale soit défini (merci supercat de l'avoir signalé). Pour de telles architectures, la solution consiste à désactiver les interruptions globales juste avant de s'endormir. Si une interruption se déclenche juste avant l'exécution de l'instruction sleep, le gestionnaire d'interruption ne sera pas exécuté, le cœur se réveillera et une fois les interruptions globales réactivées, le gestionnaire d'interruption sera exécuté. En pseudo-code, l'implémentation ressemblerait à ceci:
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
comme un int
, et je l'incrémenterais chaque fois qu'il y a une interruption. Puis changez le if(has_flag)
en while (interrupts_count)
puis dormez. Néanmoins, l'interruption peut se produire une fois que vous avez quitté la boucle while. Si c'est un problème, alors le traitement dans l'interruption lui-même?