Arnaud Labourel Courriel : arnaud.labourel@lif.univ-mrs.fr Université de Provence 9 février 2011 Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 1 / 53
Contexte Epistémologique Comment être sûr qu un programme est vraiment correct? Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 2 / 53
Sections Critique et Exclusion Mutuelle Etant donné du code, on peut y définir des portions particulièrement importantes que l on dénote par section critique (SC). Exemple: manipulation d une variable partagée. /* Du code */... /* Section Critique */... // variable partagée /* Fin de la Section Critique */ /* Encore du Code */... Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 3 / 53
Une solution Exclusion Mutuelle Exclusion Mutuelle : Il y a au plus une entité en section critique. Permet de réguler l accès aux données. Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 4 / 53
Mais... Exclusion Mutuelle Cette spécification est incomplète pour pouvoir être utilisée rigoureusement... La solution il est interdit d entrer en section critique respecte cette spécification... Il faut ajouter une contrainte qui garantisse que toute demande soit satisfaite. pas de famine pas d interblocage (étreinte fatale) Cette satisfaction peut être précisée : l ordre de satisfaction doit être le même que l ordre de demande d entrée une certaine équité est requise l attente est bornée... Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 5 / 53
Famine Exclusion Mutuelle Définition Aucune garantie pour un thread d avoir l accès à la section critique qu il demande. Algorithme d exclusion mutuelle non équitable. Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 6 / 53
Interblocage Exclusion Mutuelle (Étreinte fatale) Définition L interblocage se produit lorsque deux processus concurrents s attendent mutuellement. Les processus bloqués dans cet état le sont définitivement, il s agit donc d une situation catastrophique. Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 7 / 53
Exemple d interblocage Exemple : Thread 1 Thread 2 Obtenir M1 Obtenir M2 section critique Rendre M2 Rendre M1 Obtenir M2 Obtenir M1 section critique Rendre M1 Rendre M2 Le Thread 1 obtient M1. Le Thread 2 obtient M2. Les deux attendent la ressource obtenue par l autre Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 8 / 53
Exclusion Mutuelle Dı ner des philosophes cinq philosophes, chacun a un plat de spaghetti et une fourchette a gauche 3 e tats : affame, penseur, mangeur pour manger on a besoin des deux fourchettes Arnaud Labourel (Universite de Provence) Exclusion Mutuelle 9 fe vrier 2011 9 / 53
Spécification du Problème de l Exclusion Mutuelle Etant donnés des sections critiques (SC), un algorithme d exclusion mutuelle doit satisfaire : 1 exclusion mutuelle (EM) : si un thread est en SC, aucun autre thread ne peut y être. 2 progrès (P) : si un groupe de thread demande à entrer en SC, l un d eux doit obtenir d entrer en SC 3 équité (E) : tout thread demandant à entrer en SC doit y entrer ( au bout d un certain temps) Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 10 / 53
Sûreté et Vivacité EM est une contrainte de sûreté P et E sont des contraintes de vivacité P = pas d étreinte fatale E = pas de famine Les algorithmes d exclusion mutuelle sont des compromis entre sûreté et vivacité... Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 11 / 53
Garantir l Exclusion Mutuelle Pour mettre en place de l exclusion mutuelle, il existe de nombreux outils : conditions variables moniteurs sémaphores... Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 12 / 53
Les Conditions Variables Boucle sur une condition (partagée) while ( condition ); // boucle infinie // sur condition partagée /* Section Critique */ NB : attente active Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 13 / 53
Moniteurs Exclusion Mutuelle Un mécanisme supplémentaire évitant l attente active if ( condition ) wait(); // Appel bloquant /* Section Critique */ notify(); //réveil d un thread bloqué Seul mécanisme avec synchronized pour Java < 1.5 Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 14 / 53
Sémaphores Exclusion Mutuelle Une généralisation de la notion de verrou P(sem); // acquérir le sémaphore sem /* Section Critique */ V(sem); // libérer le sémaphore sem Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 15 / 53
Implémentation Exclusion Mutuelle Comment mettre en place ces outils et les primitives associées?... ou Peut-on se passer de synchronized en Java? On supposera dans la suite que la lecture et l écriture en mémoire est atomique pour les types simples (ce qui est presque vrai en Java). Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 16 / 53
Première Tentative I Une variable partagée tour désigne le thread dont c est le tour : static volatile Thread tour = null ; Cette variable est utilisée pour vérifier si l on peut entrer en section critique ou s il faut attendre. public void Acquerir () { while ( tour!= Thread. currentthread ()) { if ( tour == null ) // la voie est libre tour = Thread. currentthread (); Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 17 / 53
Première Tentative II On remet tour à null en sortant de la section critique. public void Liberer () { tour = null ; Chaque thread exécute une boucle infinie de tentative d accès à la section critique : Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 18 / 53
Première Tentative III public void run () { while ( true ) { Acquerir (); // Section critique System. out. println ("C est le tour de "+ Thread. currentthread (). getname ()); try { Thread. sleep (( int )( Math. random ()*200)); catch ( Exception e) { System. out. println (" Fin du tour de "+ Thread. currentthread (). getname ()); Liberer (); try { Thread. sleep (( int )( Math. random ()*400)); catch ( Exception e) { Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 19 / 53
Première Tentative IV Mais ce code ne vérifie EM. = considérer une exécution synchrone... Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 20 / 53
Deux processus I On va commencer avec uniquement deux processus. ExclusionMutuelle.java : classe abstraite ExclusionMutuelle Tache.java : classe définissant des threads effectuant une boucle infinie de tentatives d accès/sortie de section critique Test.java : définition du main : création des deux threads et leur démarrage. Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 21 / 53
Algorithme1.java I public class Algorithme1 extends ExclusionMutuelle { public Algorithme1 () { turn = 0; public void Pmutex ( int t) { while ( turn!= t) Thread. yield (); public void Vmutex ( int t) { turn = 1-t; private volatile int turn ; Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 22 / 53
Algorithme1.java II Le problème est que la condition P n est pas vérifiée : en effet, seule une exécution en tourniquet est possible. En outre, il a été prouvé que pour n processus, il faut au moins n variables partagées. Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 23 / 53
Deux Variables : l Etat des Processus I public class Algorithme2 extends MutualExclusion { public Algorithme2 () { flag [0] = false ; flag [1] = false ; public void Pmutex ( int t) { int other ; other = 1-t; flag [t] = true ; while ( flag [ other ] == true ) Thread. yield (); Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 24 / 53
Deux Variables : l Etat des Processus II public void Vmutex ( int t) { flag [t] = false ; private volatile boolean [] flag = new boolean [2]; Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 25 / 53
Deux Variables : l Etat des Processus III Considérons Algorithme2.java La condition EM n est pas vérifiée. Considérons l exécution : thread 0 thread 1 flag[t] flag[other] flag[1]=false?: OUI flag[0]=false?: OUI false false flag[0]:= true flag[1]:=true true true Section Critique Section Critique - - Si on initialise à true, alors ce sont les conditions P et E qui ne sont pas vérifiées. Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 26 / 53
public class Algorithme4 extends ExclusionMutuelle { public Algorithme4 () { flag [0] = false ; flag [1] = false ; public void Pmutex ( int t) { int other ; other = 1- t; flag [t] = true ; while ( flag [ other ] == true ) { flag [t] = false ; while ( flag [ other ]) Thread. yield (); flag [t] = true ; public void Vmutex ( int t) { flag [t] = false ; private volatile boolean [] flag = new boolean [2]; Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 27 / 53
Algorithme4.java Exclusion Mutuelle Une exécution synchrone provoque un étreinte fatale... = règle de politesse Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 28 / 53
public class Dekker extends ExclusionMutuelle { public Dekker () { flag [0] = false ; flag [1] = false ; turn = 0; public void Pmutex ( int t) { int other ; other = 1- t; flag [t] = true ; while ( flag [ other ] == true ) if ( turn == other ) { flag [t] = false ; while ( turn == other ) Thread. yield (); flag [t] = true ; public void Vmutex ( int t) { turn = 1- t; flag [t] = false ; private volatile int turn ; private volatile boolean [] flag = new boolean [2]; Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 29 / 53
Dekker (1965) Exclusion Mutuelle L équité est vérifiée si l ordonnanceur sous-jacent est équitable. Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 30 / 53
public class Peterson extends ExclusionMutuelle { public Peterson () { flag [0] = false ; flag [1] = false ; turn = 0; public void Pmutex ( int t) { int other ; other = 1- t; flag [t] = true ; turn = t; while ( flag [ other ] && ( turn == t)) Thread. yield (); public void Vmutex ( int t) { flag [t] = false ; private volatile int turn ; private volatile boolean [] flag = new boolean [2]; Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 31 / 53
Peterson (1981) Exclusion Mutuelle Cet algorithme est bien une solution au problème de l exclusion mutuelle. Comment s en convaincre réellement après toutes nos précédentes et infructueuses tentatives? Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 32 / 53
Correction d Algorithmes Distribués Correction d algorithme nécessaire fiabilité ( pas d étreinte fatale ) sécurité ( = pas de faille de type race condition ) Algorithme = objet mathématique ( cf Cours LDP ) = preuve formelle Algorithme distribué : encore plus difficile Méthode d annotation du code Hoare ( 1969 ) puis Owicki, Gries (1975), Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 33 / 53
Principe Exclusion Mutuelle On associe à chaque transition du programme deux formules logiques. Ces formules expriment des invariants en ce point du programme. {p Instruction ; {q Si la formule p est vraie, alors après exécution de Instruction, la formule q doit aussi être vraie. On infère ainsi logiquement le comportement correct du programme. Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 34 / 53
Exemple d Annotations G. Winskel. The formal semantics of programming languages. The MIT Press, 1993. {x = x 0 y = y 0 q = 0; while (x >= y) { {x = x 0 qy 0 y = y 0 q = q +1; {x = x 0 (q 1)y 0 y = y 0 x = x-y; {x = x 0 qy 0 y = y 0 {x = x 0 qy 0 x < y y = y 0 Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 35 / 53
Non-Interférence Exclusion Mutuelle Attention Une fois la preuve faite pour le cas séquentiel (ie. un thread), il faut prouver que les invariants restent bien invariants si un autre thread s exécute entre deux instructions. Les hypothèses sur le modèle sont prépondérantes Quelles sont les opérations atomiques? = il faut rajouter des variables correspondant au point de contrôle du programme. Il s agit en général plus de non-interférence des preuves que des exécutions... Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 36 / 53
Exemple d Annotations pour la Non-Interférence {x = x 0 y = y 0 c = 0 q = 0; while (x >= y) { {x = x 0 qy 0 y = y 0 c = 1 q = q +1; {x = x 0 (q 1)y 0 y = y 0 c = 2 x = x-y; {x = x 0 qy 0 y = y 0 c = 3 {x = x 0 qy 0 x < y y = y 0 c = 4 Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 37 / 53
Conditions de Non-Interférences On a les processeurs P i la k ième instruction I k les préconditions pre ik et postconditions post ik (bien entendu post ik = pre ik+1 ) alors la condition de non-interférence signifie que chaque instruction I jl ne peut altérer pre ik. Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 38 / 53
Conditions de Non-Interférences On doit donc vérifier, si I jl = x=u; : en terme de triplet de Hoare, {pre ik pre jl x = u ; {pre ik en terme logique, pre ik pre jl = pre ik [x u] Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 39 / 53
Simplification pour Peterson Comme il y a ici une seule variable partagée on simplifie en ne numérotant pas toutes les instructions. On introduit deux variables auxiliaires after[0] et after[1] = vrai si le contrôle est après l instruction turn = t; (t = 0, 1). On rajoute after[t]= true; après celle-ci. Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 40 / 53
Notations Exclusion Mutuelle On note I 0 = {turn = 0 I 1 = {turn = 1 presc 0 = {flag 0 after 0 ( (flag 1 after 1 ) turn = 1) presc 1 = {flag 1 after 1 ( (flag 0 after 0 ) turn = 0) Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 41 / 53
Peterson pour le Thread 0 { flag 0 flag [0]= true ; after [0]= false ; {flag 0 after 0 turn =0; after [0]= true ; {flag 0 after 0 I 0 while ( flag [1] && ( turn ==0)) do {flag 0 after 0 I 0 Thread. yield (); {flag 0 after 0 ( (flag 1 after 1 ) turn = 1) [ Section Critique CS0 ] {flag 0 flag [0]= false ; Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 42 / 53
Peterson pour le Thread 1 { flag 1 flag [1]= true ; after [1]= false ; {flag 1 after 1 turn =1; after [1]= true ; {flag 1 after 1 I 1 while ( flag [0] && ( turn ==1)) do {flag 1 after 1 I 1 Thread. yield (); {flag 1 after 1 ( (flag 0 after 0 ) turn = 0) [ Section Critique CS1 ] {flag 1 flag [1]= false ; Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 43 / 53
Preuve de Correction I En séquentiel : OK Non-interférence : Il y a un problème à la troisième assertion = on remplace I 0 et I 1 par I = {turn = 0 turn = 1 Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 44 / 53
Peterson pour le Thread 0 (version 2) { flag 0 flag [0]= true ; after [0]= false ; {flag 0 after 0 turn =0; after [0]= true ; {flag 0 after 0 I while ( flag [1] && ( turn ==0)) do {flag 0 after 0 I Thread. yield (); {flag 0 after 0 ( (flag 1 after 1 ) turn = 1) [ Section Critique CS0 ] {flag 0 flag [0]= false ; Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 45 / 53
Peterson pour le Thread 1 (version 2) { flag 1 flag [1]= true ; after [1]= false ; {flag 1 after 1 turn =1; after [1]= true ; {flag 1 after 1 I while ( flag [0] && ( turn ==1)) do {flag 1 after 1 I Thread. yield (); {flag 1 after 1 ( (flag 0 after 0 ) turn = 0) [ Section Critique CS1 ] {flag 1 flag [1]= false ; Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 46 / 53
Preuve de Correction I Non-interférence : Maintenant, seules les presc contiennent des références manipulées par l autre thread (en première, deuxième et dernière lignes) Il reste à vérifier la non interférence pour presc 0 Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 47 / 53
Preuve de Correction II presc 0 = flag 0 after 0 ( (flag 1 after 1 ) turn = 1) {presc 0 flag 1 flag [1]= true ; after [1]= false ; {presc 0 {presc 0 (flag 1 after 1 ) turn =1; after [1]= true ; {presc 0 {presc 0 flag 1 flag [1] = false ; {presc 0 Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 48 / 53
Preuve de Correction III presc 0 = flag 0 after 0 ( (flag 1 after 1 ) turn = 1) presc 1 = flag 1 after 1 ( (flag 0 after 0 ) turn = 0) Par symétrie, cela est aussi vrai pour le thread 1. Or presc 0 presc 1 = turn = 0 turn = 1 Par conséquent, on a bien l exclusion mutuelle EM. Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 49 / 53
Et pour n Threads? Si on itère Peterson n 1 fois on obtient une solution à n threads. (cf PetersonN). Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 50 / 53
Peterson pour n Threads turn of size nproc 1 flag = [-1,-1,..., -1] of size nproc for (j =0; j<nproc -1; j ++) flag [t]=j; turn [j]=t; cond = nproc 1 k=0 (flag[k] j) while (cond et turn [ j] = t) yield (); Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 51 / 53
public class PetersonN { private volatile int [] turn ; private int nproc ; private volatile int [] flag ; public PetersonN ( int nb) { int i; nproc = nb; turn = new int [ nproc -1]; flag = new int [ nproc ]; for (i =0; i < nproc ; i ++) { flag [i] = -1; Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 52 / 53
public class PetersonN { public void Pmutex ( int t) { int j, k; boolean cond ; for (j =0; j < nproc -1; j ++) { flag [t] = j; turn [j] = t; cond = true ; for (k =0; (k < nproc ) && (k!= t); k ++) cond = cond ( flag [k] >= j); while ( cond && ( turn [j] == t)) Thread. yield (); public void Vmutex ( int t) { flag [t] = -1; Arnaud Labourel (Université de Provence) Exclusion Mutuelle 9 février 2011 53 / 53