5.2 Formalisation du mod`ele en TLA+
5.2.4 Les donn´ees partag´ees
Dans notre mod`ele, on utilise deux types de ressources partag´ees, des ressources prot´eg´ees et non prot´eg´ees. Les ressources prot´eg´ees le sont par un m´ecanisme explicite d’exclusion mutuelle, le protocole que l’on a choisi d’impl´ementer est IPCP(Immediate Priority Ceiling Protocol[RC04]). Ce protocole a principalement ´et´e choisi pour sa simplicit´e d’implantation : lorsqu’une tˆache acc`ede `a une ressource, elle change au plus une fois de prio-rit´e pour prendre la plus haute prioprio-rit´e. D`es qu’un thread entre dans une zone critique, il prend la plus haute priorit´e des threads pouvant acc´eder `a cette ressource. Les ressources non prot´eg´ees servent `a mod´eliser des ressources partag´ees dont l’int´egrit´e est cens´ee ˆetre garantie par l’ordonnancement des threads. On v´erifiera que ces donn´ees non prot´eg´ees ne sont jamais utilis´ees par deux threads au mˆeme instant.
On introduit ici de nouvelles constantes permettant de d´ecrire les res-sources et leurs propri´et´es :
– SharedData : l’ensemble des ressources partag´ees,
– Protected : l’ensemble des ressources partag´ees prot´eg´ees, – Unprotected : l’ensemble des ressources partag´ees non prot´eg´ees, – data Priority : la priorit´e d’une ressource (plus haute priorit´e des
threads pouvant acc´eder `a la ressource),
– accessing thread : fonction qui associe `a chaque donn´ee la liste des threads pouvant l’utiliser,
– access time : fonction qui associe `a un couple donn´ee, thread l’inter-valle pendant lequel le thread acc`ede `a la ressource.
On ajoute aussi de nouvelles variables au mod`ele :
– awaiting resource : l’ensemble des threads en attente d’une res-source,
– AccessedBy : une fonction qui associe `a chaque donn´ee le thread qui l’utilise,
– actual Priority : la priorit´e courante du thread.
Verrouiller et d´everrouiller une ressource
On commence par d´efinir deux op´erations permettant de modifier l’´etat d’une ressource partag´ee. La variable AccessedBy associe `a chaque ressource le nom du thread qui est en train de l’utiliser. On utilise la valeur sp´eciale bot thread dans le cas o`u la ressource est libre. La premi`ere op´eration, lock resourcemodifie cette variable, elle associe `a la ressource pass´ee en param`etre le thread en cours d’ex´ecution. La seconde, release resource, associe `a la ressource la valeur bot thread signifiant que la ressource est libre.
lock resource(sd ) =∆
∧ AccessedBy′ = [d ∈ SharedData 7→ if d = sd
then computing thread else AccessedBy[d ]] release resource(sd ) =∆
∧ AccessedBy′ = [d ∈ SharedData 7→ if d = sd
then bot thread else AccessedBy[d ]] Acc`es `a une ressource
On va de nouveau enrichir notre automate d’une nouvelle transition, la garde de cette transition devient vraie lorsque le thread en cours d’ex´ecution entre dans sa section critique. L’action associ´ee `a cette transition verrouille la ressource si elle est libre. Si la ressource est d´ej`a verrouill´ee et que la ressource est prot´eg´ee alors, le thread suspend son ex´ecution et passe dans un ´etat o`u il attend que la ressource redevienne libre. Dans le cas o`u la ressource n’est pas prot´eg´ee, on consid`ere que l’acc`es par deux threads en mˆeme temps `a une ressource est une erreur de conception, on va donc faire passer notre automate dans un ´etat puits. Lors de la phase de v´erification, si le syst`eme arrive dans cet ´etat, le model checker consid´erera qu’il a trouv´e un chemin menant `a un interblocage et affichera la trace correspondante.
5.2. FORMALISATION DU MOD `ELE EN TLA+ 87
– le thread en cours d’ex´ecution fait partie des threads pouvant acc´eder `
a la ressource pass´ee en param`etre , – ce thread entre dans sa section critique ,
– et que ce thread n’a pas d´ej`a verrouill´e cette ressource.
Dans l’op´eration associ´ee `a cette transition, on formalise les trois cas pr´esent´es plus haut :
– la ressource n’est pas prot´eg´ee, mais un autre thread y acc`ede, on bloque l’ex´ecution,
– si elle est prot´eg´ee et verrouill´ee le thread en cours d’ex´ecution passe dans un ´etat o`u il attend la ressource,
– sinon il la verrouille et prend sa priorit´e si celle-ci est sup´erieure `a la sienne.
Afin de rendre plus lisible la sp´ecification, on a d´efini trois op´erations, protected locked(sd), unprotected locked(sd) et block(mt). Les deux premi`eres permettent de tester si la donn´ee pass´ee en param`etre est ver-rouill´ee par un thread, la troisi`eme fait passer le thread en param`etre dans l’´etat en attente de ressource.
Les expressions block(mt), protected locked(sd), et unprotected locked(sd) ont ´et´e introduites pour segmenter le code et le rendre plus lisible.
must access(d ) =∆
∧ computing thread 6= bot thread ∧ d ∈ SharedData
∧ computing thread ∈ accessing thread [d ] ∧ access time[d , computing thread ][1]
= execution timer [computing thread ] ∧ AccessedBy[d ] 6= computing thread
access =∆
let sd = choose sd ∈ SharedData : must access(sd )in∆ if unprotected locked(sd ) then
false
else if protected locked(sd ) then ∧ block (computing thread ) ∧ computing thread′ = bot thread else ∧ lock resource(sd )
∧ actual Priority′ = [t ∈ Lifted Thread 7→ if t = computing thread
∧ sd ∈ Protected
∧ data Priority[sd ] > actual Priority[t] then data Priority[sd ]
block(mt) =∆
∧ awaiting resource′ = awaiting resource ∪ {mt} ∧ ready′ = ready \ {mt}
protected locked(sd ) =∆
sd ∈ Protected ∧ AccessedBy[sd ] 6= bot thread unprotected locked(sd ) =∆
sd ∈ Unprotected ∧ AccessedBy[sd ] 6= bot thread
Lib´erer une ressource
On d´efinit de mani`ere tr`es similaire `a la transition pr´ec´edente une tran-sition pour la sortie de section critique. La garde indique simplement que la ressource est bien verrouill´ee par le thread en cours d’ex´ecution et que celui-ci arrive `a la fin de sa section critique.
Lorsque cette garde est vraie, on lib`ere la ressource (appel `a l’op´eration release resource) et la priorit´e du thread redescend soit `a la priorit´e du thread, soit `a la plus haute priorit´e des ressources qu’il verrouille encore.
must release(d ) =∆ ∧ d ∈ SharedData
∧ computing thread 6= bot thread
∧ internal mode[computing thread ] ∈ accessing thread [d ] ∧ access time[d , computing thread ][2]
= execution timer [computing thread ] ∧ AccessedBy[d ] = computing thread
accessed data =∆ {d ∈ SharedData :
AccessedBy[d ] = computing thread } max prio data =∆
choose d ∈ accessed data :
∀ od ∈ accessed data : data Priority[od ] ≤ data Priority[d ]
release =∆
let sd = choose sd ∈ SharedData : must release(sd )in∆ ∧ release resource(sd )
∧ actual Priority′ = [t ∈ Lifted Thread 7→ if t = computing thread then
if accessed data6= {} then data Priority[max prio data] else Priority[t]
5.2. FORMALISATION DU MOD `ELE EN TLA+ 89
Sortir de l’´etat “ en attente de ressource ”
On ajoute enfin une derni`ere transition permettant aux threads en at-tente d’une ressource critique de retourner dans l’´etat ready d`es que la ressource se lib`ere.
La garde de cette op´eration est vraie si au moins un thread de l’ensemble awaiting resourcen’est plus bloqu´e par une resource partag´ee verrouill´ee. Dans ce cas, on calcule l’ensemble des threads `a lib´erer et cet ensemble de threads sort de l’´etat awaiting resource et revient dans l’´etat ready.
canUnblock = {x ∈ awaiting resource : ¬protected locked (x )} 6= {}∆ unblock =∆
let free = {x ∈ awaiting resource : ¬protected locked (x )}in∆ ∧ awaiting resource′= awaiting resource \ free
∧ ready′= ready ∪ free