• Aucun résultat trouvé

4.3 Impl´ ementation des sections critiques

4.3.3 Rollback et restart

certain overhead. Mais mˆeme ainsi, l’overhead de ce verrou dans le cas favorable est faible (35 cycles). Une ´evaluation plus compl`ete dans un contexte de programme normal reste `a faire, ainsi qu’une ´evaluation du temps n´ecessaire pour continuer l’ex´ecution d’un autre thread.

Conclusion Dans le cas d’applications normales, le rollforward lock est extrˆemement int´eressant : tr`es efficace, non bloquant donc utilisable pour le temps r´eel, il s’utilise comme le mod`ele simple du spinlock avec masquage des interruptions dans le noyau. On peut le voir comme une impl´ementation l´eg`ere et plus g´en´erale de l’h´eritage de priorit´e pour les ordonnancements `a priorit´e fixe.

Ce lock introduit une l´eg`ere entorse au principe de ressources CPU pr´evisibles (§ 3.1.2.2), puisqu’un peu de temps CPU est perdu pour d´ebloquer la section critique. Le service doit donc s’arranger pour que les sections critiques soient petites et que ce temps perdu ne soit pas significatif (et pris en compte dans l’´evaluation du temps pass´e dans le service).

Il est limit´e en ce qu’il ne permet pas d’appels de services `a l’int´erieur de la section critique. ´Egalement, nous n’avons pas impl´ement´e la prise de lock r´ecursive ou la prise de plusieurs locks simultan´ement, mais nous n’en avons pas besoin pour les services d’Anaxagoros.

4.3.3 Rollback et restart

Le rollforward lock impl´emente un verrou avec la s´emantique usuelle, et une protec- tion contre les 4 types de probl`emes de la section 4.1.1.3. Dans cette section, nous voyons comment impl´ementer d’autres m´ecanismes.

Rollback lock Le rollforward lock permet de d´ebloquer la section critique en permettant `a un thread de la terminer, i.e. d’aller jusqu’au unlock. Au contraire, le rollback permet de retourner en arri`ere, i.e. de retourner au moment du lock.

Le rollback n´ecessite de maintenir un journal des ´ecritures qui sont faites, afin que le recouvrement puisse revenir en arri`ere. Ce journal peut ˆetre simplement une pile de paires (adresse, ancienne valeur) : avant chaque ´ecriture `a une adresse de la structure partag´ee, on ajoute `a la pile l’adresse et l’ancienne valeur.

Ce m´ecanisme de synchronisation est tr`es int´eressant. Il demande un support de la part du programmeur, mais qui reste relativement simple. Surtout si on fournit une API simple pour utiliser cela (i.e. une fonction de type void transaction write( void *address, uint new value)qui remplit le journal et ´ecrit la valeur), voire un support de la part du compilateur. Cette fonction ex´ecuterait :

void transaction_write( uint *address, uint new_value) { uint index = log->index ;

log->array[index].address = address ; log->array[index].old_value = *address ; log->index++ ;

*address = new_value ; }

o`u log serait le journal li´e au lock15

. Notons que cette fonction est con¸cue pour que le rollback puisse intervenir sans danger `a n’importe quel moment. De plus le recouvrement peut ˆetre ex´ecut´e plusieurs fois sans probl`eme. Diff´erentes optimisations sont possibles ; par exemple si les ´ecritures se font toujours dans le mˆeme ordre, il est inutile de copier l’adresse ; etc.

Au niveau du d´esavantage, la n´ecessit´e (sans support du compilateur) pour le programmeur de modifier son programme pour utiliser l’API ; la perte de temps qu’occasionne la r´evocation (pour avoir entam´e la transaction, puis pour l’effacer) ; la taille du journal et l’overhead CPU lorsque beaucoup d’´ecritures sont faites.

Nous n’avons pas eu le temps d’explorer cette option en d´etail, mais elle est certainement tr`es int´eressante ; et tr`es facile `a impl´ementer `a l’aide du recoverable lock vu apr`es.

Restart lock On peut impl´ementer une version simplifi´ee du rollback lock, sans maintenir de journal. Si le thread est pr´eempt´e dans la section critique, il recommence tout simplement son ex´ecution de juste avant qu’il aie pris le lock. En regardant notre cat´egorisation des probl`emes de concurrence (§ 4.1.1.3), on s’aper¸coit que ce verrou garanti contre les probl`emes d’op´erations simultan´ees et op´erations retard´ees. On peut alors ´ecrire le code de mani`ere `a ´eviter les probl`emes restants d’observation des ´etats interm´ediaires et de non-terminaison, mais cela est beaucoup plus simple que d’´ecrire des programmes compl`etement lock-free. Bershad [Ber93] utilise ce lock pour pour simuler des instructions atomiques sur des processeurs qui n’en disposent pas, par exemple.

Le restart lock peut ˆetre g´en´eralis´e en le revocable lock, que nous voyons ci-apr`es. Sections restart Avant d’examiner plus en d´etail le restart lock, on peut ´egalement observer qu’on peut faire des sections restart sans lock. Le principe est simple : on d´efinit une certaine section de code. Si on re¸coit une pr´eemption dans une certaine section, l’ex´ecution reprend de cet endroit.

Il y a beaucoup de moyens d’impl´ementer ceci `a l’aide de la pr´eemption pro- grammable : en changeant la valeur de l’upcall de reprise upcall entry, en changeant la valeur de addr dump, en changeant la valeur de addr lock ou de expected value...

Les sections restart garantissent contre les probl`emes d’op´erations retard´ees. En monoprocesseur, elles garantissent ´egalement contre les op´erations simultan´ees, et on peut donc impl´ementer l’´equivalent du restart lock, mais sans lock.

En multiprocesseur, elle permet ´egalement de limiter le nombre de threads dans la section restart. S’il y a N processeurs sur la machine, il ne peut y avoir que N processeurs simultan´ement dans la section restart. On peut par exemple s’en servir pour contrer les probl`emes de d’allocation dynamique dans les algorithmes lock-free : comme le nombre de threads est born´e, l’allocation l’est aussi. Cela fonctionne bien puisque les algorithmes lock-free sont fait pour “r´eessayer” lorsqu’ils n’arrivent pas `a faire leur “commit”. Nous avons ainsi pu ´ecrire une version du “small-object protocol” de Herlihy [Her90] qui se sert de cette propri´et´e.

15

Pour garder l’API simple, l’adresse de log doit ˆetre gard´ee dans des donn´ees thread-locales, comme dans l’UTCB