6.5 Construc¸˜oes Universais
6.5.1 Um Construc¸˜ao Universal N˜ao-bloqueante Simples
A construc¸˜ao n˜ao-bloqueante apresentada nesta sec¸˜ao segue a mesma linha de construc¸˜oes pr´evias da literatura [9, 67, 85]. A id´eia ´e fazer com que todos os processos corretos percebam e exe- cutem a mesma seq¨uˆencia de operac¸˜oes invocadas no objeto emulado. Cada processo pi mant´em
uma r´eplica do estado do objeto emulado Si. Uma operac¸˜ao op ´e executada aplicando-se a func¸˜ao
applyT(Si, op) neste estado. O problema ent˜ao fica reduzido essencialmente a definir uma ordem total
para a execuc¸˜ao das operac¸˜oes.
As operac¸˜oes a serem executadas no objeto emulado podem ser invocadas em qualquer um dos processos, portanto a definic¸˜ao de uma ordem para ela requer consenso entre os processos. Assim, precisamos de um objeto com n´umero de consenso m, i.e., um objeto universal.
Tendo este objeto universal, a soluc¸˜ao ´e fazer com que as operac¸˜oes a serem executadas no ob- jeto sejam adicionadas a uma lista onde cada elemento tem um n´umero de seq¨uˆencia. O elemento com maior n´umero de seq¨uˆencia representa a ´ultima operac¸˜ao executada no objeto emulado. A con- sistˆencia da lista, i.e. a propriedade de que cada um de seus elementos (cada operac¸˜ao) ´e seguida por no m´aximo um elemento, ´e garantida pelo objeto universal, o PEATS neste caso. Dada essa lista, cada processo executa as operac¸˜oes do objeto emulado em ordem crescente do n´umero de seq¨uˆencia.
Conforme j´a discutido, a lista de operac¸˜oes ´e implementada usando o PEATS. A id´eia chave ´e representar cada operac¸˜ao como uma tupla SEQ com um campo posic¸˜ao e inserir essas tuplas no espac¸o usando a operac¸˜ao cas. Quando um processo quer executar uma operac¸˜ao op ele invoca cas da seguinte forma: se n˜ao existe uma tupla SEQ com o maior n´umero de seq¨uˆencia conhecido pelo processo no espac¸o, ent˜ao ele insere uma tupla SEQ com esse n´umero de seq¨uˆencia e sua invocac¸˜ao `a op. A figura 6.5 ilustra esta id´eia.
p2 1 p cas(<SEQ,3,?inv>,<SEQ,3,inv1>) cas(<SEQ,5,?inv>,<SEQ,5,inv2>) <SEQ,4,i4> PEATS <SEQ,1,i1> <SEQ,3,i3> <SEQ,2,i2>
Figura 6.5: Construc¸˜ao universal usando PEATS.
Nesta figura, o processo p1tenta inserir uma tupla contendo a invocac¸˜ao com n´umero de seq¨uˆencia
3 no PEATS, enquanto o processo p2executa cas na esperanc¸a de inserir uma operac¸˜ao com n´umero
de seq¨uˆencia 5. Dado que no PEATS j´a existem tuplas com n´umeros de seq¨uˆencia de 1 a 4, o processo p1n˜ao conseguir´a inserir sua tupla, enquanto o processo p2ter´a sucesso em sua inserc¸˜ao. O algoritmo
14 apresenta este objeto universal.
O algoritmo assume que cada processo pi comec¸a sua execuc¸˜ao com o estado inicial do objeto
emulado (state = ST, linha 2) e conhecendo uma lista de operac¸˜oes vazia no PEATS (pos = 0, linha
3). Quando um processo pi invoca uma operac¸˜ao inv no objeto emulado, ele itera atrav´es da lista
de operac¸˜oes no PEATS atualizando sua vari´avel state (lac¸o das linhas 4-11) e tentando inserir sua operac¸˜ao no final da lista usando a operac¸˜ao cas (linha 6). Se a operac¸˜ao cas ´e bem sucedida por pi,
a vari´avel state de pi ´e atualizada e a resposta de sua invocac¸˜ao ´e retornada (linhas 7 e 8).
O algoritmo ´e n˜ao-bloqueante devido a operac¸˜ao cas: quando dois processos tentam concorren- temente colocar uma tupla no final da lista, pelo menos um deles consegue. Entretanto, o algoritmo n˜ao ´e livre de espera porque algum processo pode ser bem sucedido em inserir suas operac¸˜oes na lista infinitas vezes, atrasando outros processos para sempre.
A pol´ıtica de acesso para a construc¸˜ao universal (apresentada na figura 6.6) define que uma tupla SEQ com um segundo campo pos s´o pode ser inserida no espac¸o (usando cas) se existe uma tupla com segundo campo com valor pos − 1 no espac¸o.
Algoritmo 14 Construc¸˜ao universal n˜ao-bloqueante (processo pi).
Vari´aveis compartilhadas:
1: ts= ∅ {PEATS}
Vari´aveis locais:
2: state= ST {estado atual do objeto}
3: pos= 0 {posic¸˜ao do fim da lista de operac¸˜oes}
invoked inv
4: loop
5: pos← pos + 1
6: if ts.cas(hSEQ, pos, ?pos invi, hSEQ, pos, invi) then
7: hstate, replyi ← applyT(state, inv)
8: return reply
9: end if
10: hstate, replyi ← applyT(state, pos inv) 11: end loop
Object State T S
Rcas: execute(cas(hSEQ, pos, xi, hSEQ, pos, invi)) : −
invoke(p, cas(hSEQ, pos, xi, hSEQ, pos, invi)) ∧ formal(x)∧ (pos = 1 ∨ ∃y : hSEQ, pos − 1, yi) ∈ T S)
Figura 6.6: Pol´ıtica de acesso para o PEATS usado no algoritmo 14.
A prova de correc¸˜ao do algoritmo ´e baseada nos seguintes lemas:
Lema 10 Em qualquer execuc¸˜ao do sistema, as seguintes propriedades s˜ao invariantes do PEATS usado no algoritmo 14:
1. Para qualquer pos≥ 1, existe no m´aximo uma tupla hSEQ, pos, invi no espac¸o de tuplas; 2. Para qualquer tuplahSEQ, pos, invi no espac¸o de tuplas, com pos > 1, existe exatamente uma
tuplahSEQ, pos − 1, invi no espac¸o.
Prova: Estas duas invariantes s˜ao conseq¨uˆencias diretas do algoritmo 14 e de sua pol´ıtica de acesso (figura 6.6):
1. A partir da pol´ıtica de acesso pode-se verificar que uma tupla s´o pode ser inserida no espac¸o atrav´es de uma invocac¸˜ao da operac¸˜ao cas, onde o molde e a entrada passados como argumento devem ser tuplas SEQ com o mesmo n´umero de seq¨uˆencia seq e o campo da invocac¸˜ao do molde deve ser formal. Com essa propriedade e a definic¸˜ao do cas, ´e f´acil ver que n˜ao pode haver duas tuplas SEQ com o mesmo n´umero de seq¨uˆencia no espac¸o de tuplas.
2. Novamente, a partir da pol´ıtica de acesso ´e poss´ıvel ver que a operac¸˜ao cas s´o pode ser exe- cutada para inserir uma operac¸˜ao na posic¸˜ao pos se existe uma tupla no espac¸o com n´umero de seq¨uˆencia uma unidade menor. Isto garante que existe uma tupla hSEQ, pos − 1, invi no espac¸o quando da inserc¸˜ao da tupla pos. A garantia de que n˜ao existe mais de uma tupla dessa
Lema 11 A construc¸˜ao universal do algoritmo 14 ´e n˜ao-bloqueante.
Prova: Este lema ´e provado por contradic¸˜ao. Considere, sem perda de generalidade, uma execuc¸˜ao α onde apenas dois processos corretos p1 e p2executam as invocac¸˜oes inv1 e inv2, respectivamente.
Suponha que eles permanecem parados para sempre, n˜ao recebendo resposta para essas invocac¸˜oes. Vamos mostrar que α n˜ao existe. Uma inspec¸˜ao no algoritmo mostra que os processos permanecem atualizando suas c´opias do estado do objeto at´e que eles executem a mais recente operac¸˜ao inserida no espac¸o de tuplas (com campo posic¸˜ao igual `a pos). Neste ponto, p1 e p2 v˜ao tentar adicionar
suas invocac¸˜oes no espac¸o na posic¸˜ao pos + 1 atrav´es da execuc¸˜ao da operac¸˜ao cas (linha 6). Como assumimos que o PEATS ´e lineariz´avel, as duas invocac¸˜oes a operac¸˜ao cas devem acontecer uma ap´os a outra, assim ou a tupla SEQ com inv1ou a com inv2ser´a inserida no espac¸o na posic¸˜ao pos + 1. O
processo bem sucedido executando cas para esta posic¸˜ao vai inserir sua invocac¸˜ao para o objeto, e vai retornar o resultado desta operac¸˜ao (linhas 7 e 8). Este fato contradiz a definic¸˜ao de α. Teorema 7 O algoritmo 14 provˆe uma construc¸˜ao universal n˜ao-bloqueante.
Prova: O resultado apresentado no lema 10 implica na existˆencia de uma ordem total das operac¸˜oes executadas no objeto emulado. Atrav´es de uma inspec¸˜ao no algoritmo, ´e f´acil ver que um processo correto atualiza sua c´opia do estado do objeto emulado aplicando a func¸˜ao determinista applyT em
todas as tuplas SEQ na ordem definida pelos seus n´umeros de seq¨uˆencia. Desta forma, todas as operac¸˜oes s˜ao executadas na mesma ordem por todos os processos corretos e esta ordem ´e condizente com especificac¸˜ao seq¨uencial do objeto, provida pela func¸˜ao applyT. Isto ´e suficiente para provar que a construc¸˜ao universal satisfaz linearizac¸˜ao. O lema 11 prova que a construc¸˜ao ´e n˜ao-bloqueante.