• Nenhum resultado encontrado

5.10 Sincroniza¸c˜ ao intra-objecto

5.11.1 Comunica¸c˜ ao s´ıncrona

Na implementa¸c˜ao do sincronismo condicional para mecanismos s´ıncronos de co- munica¸c˜ao entre processadores pode-se fazer uma aproxima¸c˜ao similar `a utilizada em monitores [Hoare 74]. Os monitores utilizam, para esse fim, as chamadas vari´aveis de condi¸c˜ao. Estas vari´aveis, `as quais n˜ao est´a associado nenhum valor, s˜ao abstrac¸c˜oes para filas de espera de processadores, podendo-se-lhes aplicar trˆes opera¸c˜oes16: espera

(wait), sinaliza (signal ) e sinaliza todos (broadcast). O efeito dessas opera¸c˜oes ´e o se- guinte. A opera¸c˜ao de espera faz com que o processador que a requer seja colocado na fila de espera associada `a vari´avel de condi¸c˜ao (libertando o monitor para outros processadores); a opera¸c˜ao sinaliza faz com que um dos processadores seja retirado da fila sendo-lhe dado, assim que poss´ıvel, o acesso exclusivo ao monitor; por fim, a opera¸c˜ao sinaliza todos faz o mesmo que a opera¸c˜ao anterior mas para todos os proces- sadores existentes na fila de espera. A biblioteca POSIX-Threads para a linguagem C implementa este tipo de vari´aveis.

Esta aproxima¸c˜ao ao sincronismo condicional tem, no entanto, um grave problema: ela n˜ao ´e estaticamente segura j´a que delega nas m˜aos dos programadores a respon- sabilidade de as declarar e utilizar correctamente as vari´aveis de condi¸c˜ao. Para al´em de n˜ao ser segura, tamb´em n˜ao ´e suficientemente abstracta, j´a que o programador ´e obrigado a construir o c´odigo de sincroniza¸c˜ao condicional ligando-o explicitamente `as verdadeiras condi¸c˜oes associadas ao estado dos objectos (aproxima¸c˜ao operacional). O pr´oprio Hoare [Hoare 74, p´agina 556] reconhece que uma aproxima¸c˜ao alternativa as- sente em instru¸c˜oes de espera condicional seria mais simples e segura. Por outro lado, esta aproxima¸c˜ao permite a implementa¸c˜ao de algoritmos de sincronismo bastante efici- entes j´a que o programador tem a possibilidade de decidir quais os pontos do programa em que ´e necess´ario sinalizar processadores e, mais importante ainda, pode decidir para que processadores esses sinais ser˜ao endere¸cados (recorrendo a diferentes vari´aveis de condi¸c˜ao).

Na linguagem Java a responsabilidade para gerir os mecanismos (de base) de sin- croniza¸c˜ao condicional (designados por Wait, Notify e NotifyAll ) pertence tamb´em ao programador. No entanto, ao contr´ario dos monitores originais, n˜ao existe a possibili- dade de declarar v´arias vari´aveis de condi¸c˜ao por objecto, e como tal, de escolher dife- rentes grupos de processadores (que em Java s˜ao threads) nas opera¸c˜oes de sinaliza¸c˜ao (notifica¸c˜ao). Em Java existe uma ´unica vari´avel de condi¸c˜ao por objecto, `a qual se aplica as opera¸c˜oes de espera e notifica¸c˜ao. Assim, um sinal de notifica¸c˜ao acorda um qualquer processador presente na fila de espera, independentemente da condi¸c˜ao de espera que lhe esteja associada. Se houver v´arios processadores `a espera de diferentes condi¸c˜oes de sincroniza¸c˜ao, existe a possibilidade de uma notifica¸c˜ao acordar o pro- cessador errado (situa¸c˜ao que aconselha o uso alternativo de notifica¸c˜oes para todos [Lea 00, p´aginas 191–192]).

No entanto, nenhuma destas aproxima¸c˜oes se aproxima dos objectivos pretendi- dos: sincroniza¸c˜ao segura, abstracta e automaticamente realiz´avel pelo sistema de com- pila¸c˜ao da linguagem.

Um algoritmo poss´ıvel nesse sentido17 ser´a associar uma ´unica vari´avel de condi¸c˜ao

a cada objecto (como em Java), implementando todas as ac¸c˜oes de espera condicional como opera¸c˜oes de espera nessa vari´avel (sejam as relacionadas com o sincronismo intra-objecto, ou com o sincronismo inter-objecto), e colocando opera¸c˜oes de sinaliza¸c˜ao para todos os processadores sobre essa vari´avel no fim de todas as rotinas p´ublicas do objecto18. Os processadores, ao ganharem o acesso exclusivo ao objecto, verificam se a

condi¸c˜ao que os fez esperar (se existir alguma) ´e verdadeira, executando a rotina caso o seja, ou voltando a colocar-se em espera sobre a vari´avel de condi¸c˜ao caso n˜ao o seja. Obviamente que este algoritmo, apesar de ir de encontro aos objectivos pretendidos, ´e potencialmente muito ineficiente.

Este algoritmo pode ser melhorado caso o sistema de compila¸c˜ao tenha a capacidade de distinguir entre comandos e consultas (puras). Nesta situa¸c˜ao s´o ´e necess´ario sinali- zar todos os processadores em espera no fim da execu¸c˜ao de comandos (e de eventuais consultas n˜ao puras), uma vez que apenas estas rotinas podem alterar as condi¸c˜oes de espera.

Esta ´e a implementa¸c˜ao autom´atica utilizada neste momento na linguagem prot´otipo que est´a a ser desenvolvida no ˆambito deste trabalho (MP-Eiffel) [OeS 06a]. Os exemplos de implementa¸c˜ao autom´atica dos v´arios esquemas de sincroniza¸c˜ao intra- objectos apresentados na sec¸c˜ao C.1 utilizam tamb´em este algoritmo.

Poss´ıveis implementa¸c˜oes mais eficientes

Neste problema da implementa¸c˜ao do sincronismo condicional, a aproxima¸c˜ao ope- racional – na qual s˜ao os programadores que a implementam directamente – apesar da sua inseguran¸ca (est´atica) ´e ainda a que melhor consegue construir algoritmos muito eficientes.

17Similar ao apresentado por Hoare [Hoare 74, p´agina 557] na descri¸c˜ao da implementa¸c˜ao de instru¸c˜oes de

espera condicional.

18Uma vez mais podemos constatar a importˆancia de impor a inexistˆencia de atributos publicamente modi-

fic´aveis, j´a que, nessa situa¸c˜ao, a sinaliza¸c˜ao dos processadores em fila de espera poderia ter de ser propagada para todos os clientes da classe que pudessem modificar atributos p´ublicos.

Em [OeS 06a] propomos duas aproxima¸c˜oes (mas que carecem ainda de imple- menta¸c˜ao e valida¸c˜ao experimental) que podem fornecer algoritmos seguros para este problema e que se aproximam bastante mais da eficiˆencia dos algoritmos feitos directa- mente pelos programadores. Uma das aproxima¸c˜oes faz uso das asser¸c˜oes concorrentes, e a outra da associa¸c˜ao entre todas as rotinas da classe aos atributos dos quais depen- dem, ou modificam.