DCC 837 Programac~ao Paralela Osvaldo Carvalho - DCC/UFMG 2o Semestre - 1998 (U ltima Modicac~ao: 30 de Novembro de 1998) 2 Indice 1 Apresentac~ao do Curso 1.1 Motivac~ao . . . . . 1.1.1 Objetivos . 1.2 Informac~oes . . . . 1.2.1 Ementa . . 1.2.2 Calendario 1.2.3 Avaliac~ao . 1.2.4 Bibliograa 1.2.5 LINKS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I Express~ao e Implementac~ao do Paralelismo 7 7 9 10 11 12 12 13 13 15 2 Introduc~ao a Programac~ao Paralela Assncrona 17 2.1 Paralelismo Programac~ao Paralela . . . . . . . . . . . . . . . . 17 2.1.1 Modo de execuc~ao Modelo de Programac~ao . . . . . . . 18 2.2 Programaca~o Paralela Assncrona . . . . . 2.2.1 Porque PP assncrona? . . . . . . . 2.2.2 Express~ao do Paralelismo . . . . . 2.2.3 Exemplos de Programas Paralelos 2.3 Sem^antica de Programas Paralelos . . . . 2.3.1 Conitos . . . . . . . . . . . . . . 2.3.2 Sem^antica Operacional . . . . . . . 2.3.3 Comandos Conitantes . . . . . . 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 19 20 21 23 23 25 26 INDICE 4 2.4 2.5 2.6 2.7 2.3.4 Resumo . . . . . . . . . . . . . . . . . . . . . . . . . . . . Regi~oes Crticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.4.1 Exclus~ao Mutua . . . . . . . . . . . . . . . . . . . . . . . 2.4.2 Liberac~ao do Processador . . . . . . . . . . . . . . . . . . 2.4.3 Propriedades de programas paralelos e o problema dos Leitores e Escritores . . . . . . . . . . . . . . . . . . . . . 2.4.4 Imposic~ao de Polticas de Escalonamento: Programac~ao de um Servidor de Impressora . . . . . . . . . . . . . . . . 2.4.5 Conclus~oes . . . . . . . . . . . . . . . . . . . . . . . . . . Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Respostas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.6.1 Quicksort Paralelo . . . . . . . . . . . . . . . . . . . . . . 2.6.2 Comparac~ao e Troca em Paralelo . . . . . . . . . . . . . . 2.6.3 Transac~oes . . . . . . . . . . . . . . . . . . . . . . . . . . 2.6.4 A rvore B . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.6.5 Pool de Recursos Id^enticos . . . . . . . . . . . . . . . . . 1a Prova . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.7.1 1a Quest~ao . . . . . . . . . . . . . . . . . . . . . . . . . . 2.7.2 2a Quest~ao . . . . . . . . . . . . . . . . . . . . . . . . . . 2.7.3 3a Quest~ao . . . . . . . . . . . . . . . . . . . . . . . . . . 2.7.4 4a Quest~ao . . . . . . . . . . . . . . . . . . . . . . . . . . 2.7.5 5a Quest~ao . . . . . . . . . . . . . . . . . . . . . . . . . . 3 Nucleos de Multiprogramac~ao 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 Interrupc~oes em PCs . . . . . . Co-rotinas . . . . . . . . . . . . Multiplexac~ao do Processador . Chaves e Camas . . . . . . . . Resumo e Conclus~oes . . . . . Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 Introduc~ao a Programac~ao Paralela em Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 28 29 32 33 39 43 44 46 46 47 47 48 49 52 52 52 52 53 53 55 55 57 58 61 63 63 65 4.1 Introduc~ao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 INDICE 4.2 4.3 4.4 4.5 4.6 5 4.1.1 O Fen^omeno Java . . . . . . 4.1.2 Hello World . . . . . . . . . . 4.1.3 Java n~ao tem pointers . . . . 4.1.4 Uma aplicac~ao real: Prj2tex . Applets . . . . . . . . . . . . . . . . 4.2.1 Vis~ao Geral . . . . . . . . . . Eventos . . . . . . . . . . . . . . . . Threads . . . . . . . . . . . . . . . . Conclus~oes . . . . . . . . . . . . . . 1o Trabalho Pratico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 Outras Linguagens e Notaco~es para Programac~ao Paralela 5.1 Especicaca~o de Execuc~ao em Paralelo . . . 5.2 Sincronizaca~o com Memoria Compartilhada 5.2.1 Acessos Indivisveis a Memoria . . . 5.2.2 Semaforos . . . . . . . . . . . . . . . 5.2.3 Monitores . . . . . . . . . . . . . . . 5.2.4 \Path Expressions" { 1 . . . . . . . 5.2.5 Mensagens . . . . . . . . . . . . . . 5.2.6 Conclus~oes . . . . . . . . . . . . . . 5.3 CSP { Communicating Sequential Processes 5.3.1 Conceitos e Notac~oes . . . . . . . . . 5.3.2 Exemplos de Uso . . . . . . . . . . . 5.3.3 Notas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 67 67 67 68 68 70 71 76 77 79 79 80 80 81 86 87 88 89 89 90 98 103 II Formulac~ao e Prova de Propriedades de Programas Paralelos 105 6 Modelos Formais para Programas Paralelos 107 6.0.1 O Buer Compartilhado . . . . . . . . . . . . . . . . . . . 108 6.0.2 Invariantes . . . . . . . . . . . . . . . . . . . . . . . . . . 111 6.0.3 Variaveis Auxiliares . . . . . . . . . . . . . . . . . . . . . 113 INDICE 6 6.0.4 Atingibilidade: o mais forte invariante 6.1 Modelos Formais: Progresso . . . . . . . . . 6.1.1 Tecnicas de Prova de P leads;to Q . . 6.1.2 Produtor e Consumidor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115 116 118 119 7 Transac~oes e o Protocolo 2-fases 121 III Programac~ao Distribuda 129 8 Programac~ao Distribuda 131 7.0.1 Travamentos . . . . . . . . . . . . . . . . . . . . . . . . . 122 7.1 Conclus~oes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127 8.1 The Drinking Philosophers Problem . . . . . . . . . 8.2 Exclus~ao Mutua Distribuda . . . . . . . . . . . . . . 8.2.1 Relogios Logicos e Timestamps . . . . . . . . 8.2.2 Algoritmo de Ricart e Agrawala (1981) . . . 8.2.3 Carvalho e Roucairol (1983) { 1 . . . . . . . . 8.2.4 O Algoritmo de Maekawa (1985) . . . . . . . 8.2.5 Algoritmo de Carvalho e Campos (1991) { 1 . 8.2.6 O Algoritmo de Naimi-Trehel . . . . . . . . 8.3 2o Trabalho Pratico . . . . . . . . . . . . . . . . . . 8.3.1 Voyager . . . . . . . . . . . . . . . . . . . . . IV Balanco e Conclus~oes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 139 140 143 145 147 152 157 170 170 173 Cap tulo 1 Apresentac~ao do Curso Programac~ao Paralela 1.1 Motivac~ao Alguns dos primeiros computadores tinham uma instruc~ao l^e cart~ao , que comandava diretamente a leitora de cart~oes, enviando instruc~oes e recebendo dados, ate a leitura completa do cart~ao, quanto o uxo normal de execuc~ao de instruc~oes da CPU era retomado. A leitura de um cart~ao envolvia a movimentac~ao mec^anica do cart~ao, e era intrinsecamente lenta, tomando um tempo em que a CPU poderia executar alguns milhares de instruc~oes. CPUs eram muito caras, da ordem de milh~oes de dolares, e todo esforco para um melhor aproveitamento de seus recursos era justicavel. Surgiu ent~ao a ideia de leitura antecipada1 dos cart~oes. Os 80 bytes lidos de um cart~ao seriam colocados em uma area de memoria, chamada buer . Enquanto a leitora estivesse operando, o programa do usuario continuaria fazendo seus calculos, usando a CPU. Quando o programa do usuario quisesse ler um cart~ao, sua imagem seria obtida diretamente da memoria, numa operac~ao rapida. Uma ideia simples e boa, que entretanto colocava novos problemas: o modelo de programac~ao para o usuario n~ao deveria ser altera- do. O programador comum poderia continuar a pensar que o comando read(cardReader, cardImage) acionava a leitora, e lia efetivamente os mesmos cart~oes, na mesma ordem em que estavam colocados no escaninho de entrada; era necessario um mecanismo para a leitora avisar do termino da leitura de um cart~ao, de onde surgiu a interrupc~ao 1 http://www.dcc.ufmg.br/~vado/java/CardReaderApplet/CardReaderApplet.html 7 ~ DO CURSO CAPITULO 1. APRESENTACAO 8 o programa do usuario deveria esperar quando o buer estivesse vazio, e a leitora deveria esperar quando o buer estivesse cheio A Disciplina Programac~ao Paralela Todas estas ideias surgiram na decada de 50, quando tecnicas ad-hoc de programac~ao foram empregadas para resolver os problemas de sincronizac~ao gerados pela leitura antecipada. A CPU cou dividida em duas tarefas: uma executava o programa do usuario, e a outra era o driver da leitora de cart~oes. As interrupc~oes eram usadas para alternar a tarefa em execuc~ao pela CPU. A programac~ao paralela , ou programac~ao concorrente , e a disciplina que veio a tratar de forma sistematica as tecnicas de programac~ao associadas a problemas como a leitura antecipada. O artigo Cooperating Sequential Processes [Dijkstra, 1965a], publicado por Dijkstra em 1965, pode ser considerado um marco para a area. Conceitos como processos, independ^encia de velocidades relativas, regi~oes crticas, exclus~ao mutua, semaforos foram ali introduzidos { ao menos para a academia, pois varios dentre eles ja eram de uso corrente na industria. Hoje em dia s~ao incontaveis as aplicac~oes e areas de pesquisa associadas a programac~ao paralela. Leitores e Escritores Esta applet dispara 12 processos (threads ) paralelos, sujeitos a um tipo de sincronizac~ao que ilustra os problemas estudados neste curso. Seis processos s~ao \leitores", e podem ler simultaneamente uma certa estrutura de dados. A vida de um processo leitor e cclica: 1. ele \pensa", isto e, faz qualquer atividade n~ao relacionada com esta sincronizac~ao, 2. ca \faminto", isto e, faz uma requisic~ao de leitura, e 3. l^e efetivamente, ao receber uma autorizac~ao de leitura, apos o que volta a pensar. Seis outros processos s~ao \escritores", com ciclos de vida analogos. Quando um escritor escreve, nenhum outro pode escrever, e nenhum leitor pode ler. Aperte Start, e veja o que acontece. Nosso curso se preocupa com a construc~ao de programas como este. Preocupase tambem com a formulac~ao e prova de propriedades destes programas. Um tipo de propriedade e expresso pela obedi^encia a restric~ao de sincronizac~ao que falamos. Outras propriedades podem ser satisfeitas ou n~ao, como por exemplo: toda operac~ao de escrita e bem sucedida, ou e possvel que um escritor permaneca esperando por um tempo indeterminado? Experimente: aperte Stop, ~ 1.1. MOTIVACAO 9 modique os valores de entrada que determinam os tempos medios de leitura e escrita, assim como os tempos medios em que um processo ca \pensando", e aperte Start outra vez. Voc^e vera facilmente que se o tempo em que os leitores pensam for reduzido, pode ser que um escritor n~ao consiga nunca realizar sua escrita. Objeto do Curso Programac~ao Paralela Assncrona Aplicac~oes: animac~oes na Web sistemas operacionais protocolos sistemas de tempo real sistemas reativos como centrais telef^onicas interfaces gracas bancos de dados arquiteturas cliente/servidor aplicac~oes numericas ... 1.1.1 Objetivos O objetivo deste curso e o estudo de conceitos, metodos e algoritmos apropriados para a construc~ao e a analise de programas paralelos e distribuidos. Ao termino do curso o aluno tera acumulado um pequeno arsenal de so- luc~oes classicas, estara apto a desenvolver e analisar programas paralelos, e tambem a ler e analisar artigos de pesquisa na area. O que este curso n~ao e um curso sobre o uso ecaz de maquinas paralelas (veja o curso de Ci^encia Computacional2) 2 http://www.dcc.ufmg.br/~kitajima/FCC98 ~ DO CURSO CAPITULO 1. APRESENTACAO 10 um curso sobre analise de complexidade de algoritmos paralelos, utilizando PRAM ou outros modelos similares (veja o curso de Estruturas de Dados) um curso de algoritmos numericos paralelos mesmo se os princpios aqui estudados s~ao de alguma utilidade para estas areas. Pre-Requisitos Espera-se que o aluno tenha maturidade em programac~ao e em estruturas de dados. Java sera a linguagem adotada, mas orientaca~o a objetos n~ao e explorada a n~ao ser no inevitavel. Comentarios Programac~ao paralela se aprende como a programac~ao sequencial, atraves do estudo de algoritmos e linguagens; Os programas paralelos que vamos estudar reagem a eventos assncronos , como a chegada de um pacote pela rede, ou o apertar de uma tecla; (Um outro nome para este curso seria \Programac~ao de Sistemas Reativos" ) Isto introduz caractersticas de n~ao-determinismo , e de difcil reproduc~ao de uma execuc~ao; Estas diculdades tornam necessario um formalismo mais rigoroso para o tratamento de programas paralelos 1.2 Informac~oes Segundas e quartas, de 16:35 as 18:20 Sala 2077 do ICEx Professor: Osvaldo Carvalho { e-mail: [email protected] { www: http://www.dcc.ufmg.br/~vado4 { Sala 4018 { Telefone 499 5860 3 4 mailto:[email protected] http://www.dcc.ufmg.br/~vado ~ 1.2. INFORMACOES 11 { As transpar^encias do curso se encontram disponveis na rede, mas o aluno deve sempre se lembrar de que est~ao em construc~ao , com modicac~oes introduzidas com o avanco do curso. { O mesmo vale para o arquivo postscript; use o ghostview para imprimir apenas as paginas das proximas uma ou duas aulas. { Para ver as applets, e necessario usar um browser compatvel o JDK (Java Development Kit) 1.1.5 , como o Netscape Communicator do DCC/UFMG. 1.2.1 Ementa 1. Express~ao e Implementac~ao do Paralelismo [Andrews and Schneider, 1983],[Bal et al., 1989]. Express~ao do paralelismo, conitos no acesso a dados compartilhados, ferramentas de sincronizac~ao e sua implementac~ao; 2. Formulac~ao e Prova de Propriedades de Programas Paralelos [Keller, 1976], [Owick and Gries, 1976], [Shankar, 1993], [Eswaran et al., 1976],[Kohler, 1981],[Gray and Reuter, 1993a]. Modelos de programas paralelos; metodos formais para a express~ao e prova de propriedades como \toda requisic~ao sera satisfeita num tempo nito", ou \nunca teremos dois clientes utilizando um certo recurso simultaneamente" 3. Programac~ao Distribuda [Lamport, 1978],[Ricart and Agrawala, 1981], [Carvalho and Roucairol, 1983], [Chandy and Misra, 1984], [de Aguiar Campos and Carvalho, 1988], [Barbara and Garcia-Molina, 1986]. Programaca~o paralela com interac~ao entre processos restrita a troca de mensagens, que sofrem atrasos de transmiss~ao, impossibilitando consultas ao estado global. Seminarios: Ambientes de programac~ao paralela Coer^encia de cache Sistemas de transac~oes Arquiteturas cliente/servidor Programac~ao por Agentes Hardware de Sincronizaca~o ~ DO CURSO CAPITULO 1. APRESENTACAO 12 1.2.2 Calendario Aula Data 1 31/08/98 02/09/98 07/09/98 2 09/09/98 3 14/09/98 16/09/98 4 21/09/98 5 23/09/98 6 28/09/98 7 30/09/98 8 05/10/98 9 07/10/98 10 12/10/98 11 14/10/98 12 19/10/98 13 21/10/98 14 26/10/98 15 28/10/98 02/11/98 16 04/11/98 17 09/11/98 18 11/11/98 19 16/11/98 20 18/11/98 21 23/11/98 22 25/11/98 23 30/11/98 24 02/12/98 25 07/12/98 26 09/12/98 27 14/12/98 28 16/12/98 29 21/12/98 30 23/12/98 Dia 2a 4a 2a 4a 2a 4a 2a 4a 2a 4a 2a 4a 2a 4a 2a 4a 2a 4a 2a 4a 2a 4a 2a 4a 2a 4a 2a 4a 2a 4a 2a 4a 2a 4a Assunto Apresentac~ao aula adiada Feriado Programac~ao Paralela Assncrona Sem^antica de Programas Paralelos aula adiada Regi~oes Crticas Exemplos Exerccios 1a Prova 1a Prova - Gabarito Java - Introduc~ao Java - GUI, Applets Java - Threads Nucleos de Multiprogramac~ao Linguagens e Construc~oes CSP Exerccios Feriado Modelos Formais para Programas Paralelos Invariantes Progresso Transac~oes e o Protocolo Duas-Fases O Jantar dos Filosofos Exclus~ao Mutua - Lamport, RA, CR Exclus~ao Mutua - Maekawa Exclus~ao Mutua - Naimi Trehel Exerccios Exerccios 2a Prova 2a Prova - Gabarito Seminarios Seminarios Balanco e Conclus~oes 1.2.3 Avaliac~ao A avaliac~ao dos alunos sera feita por provas, trabalhos praticos e uma monograa. As provas s~ao longas, e os alunos ter~ao 24 horas para faz^e-las. Os alunos poder~ao propor outros temas para as monograas, alem dos sugeridos na ementa. Toda monograa sera objeto de uma apresentac~ao a ser feita pelo aluno no m do curso. Para cada seminario o aluno devera elaborar uma monograa no ~ 1.2. INFORMACOES 13 formato de um artigo a ser submetido para um congresso. Duas provas 50 Dois trabalhos praticos 30 Seminario 20 1.2.4 Bibliograa O curso sera baseado em notas de aula e artigos. 1.2.5 LINKS Latex e Latex2html Um Exemplo Simples do Uso de Latex e Latex2html Tex, Uppsala University5 Advanced LaTeX6 LaTeXe help 1.67 The LATEX2HTML Translator8 All About LaTeX2HTML9 Java Pagina do Marco Aurelio10 Tutorial de Java11 (local) Ambiente Java no DCC-UFMG12 Tutorial sobre threads em Java13 ObjectSpace: excelentes bibliotecas Java14 http://www.csd.uu.se/documentation/tex/ http://www-h.eng.cam.ac.uk/help/tpl/textprocessing/latex advanced/latex advan ced.html 7 http://www.loria.fr/services/tex/general/latex2e.html 8 http://www-math.mpce.mq.edu.au/texdev/LaTeX2HTML/docs/manual/ 9 http://cbl.leeds.ac.uk/ nikos/tex2html/doc/latex2html/latex2html.html 10 http://hematita.dcc.ufmg.br/~corelio/java.html 11 http://www.dcc.ufmg.br/javadoc 12 http://www.dcc.ufmg.br/~crc/servicos/java.html 13 http://www.magelang.com/thread course/ 14 http://www.objectspace.com 5 6 14 ~ DO CURSO CAPITULO 1. APRESENTACAO Links Java15 Curso Programac~ao Concorrente, McGill16 The mother of all Java FAQ lists17 Bibliograa Colec~ao de bibliograas em Ci^encia da Computac~ao18 Biblioteca digital da ACM19 Biblioteca digital da IEEE20 Book Pool21 Amazon Books22 Outros Algoritmos distribudos23 Pagina ocial PVM24 CENAPAD-MG/CO25 15 16 17 18 19 20 21 22 23 24 25 http://home.intranet.org/~mouse/links/java.html http://www.openface.ca/~skow/623/schedule.html http://www-net.com/java/faq http://liinwww.ira.uka.de/bibliography/index.html#search http://www.acm.org/dl http://www.computer.org/epub/ http://www.bookpool.com http://www.amazon.com http://www.mpi-sb.mpg.de/%7Etsigas/DISAS/ http://www.epm.ornl.gov/pvm/pvm home.html http://www.cenapad.ufmg.br Parte I Express~ao e Implementac~ao do Paralelismo 15 Cap tulo 2 Introduc~ao a Programac~ao Paralela Assncrona Motivac~ao : uso simult^aneo de recursos (ex.: CPU e leitora de cart~oes) acelerac~ao de programas (ex.: resoluc~ao de um sistema de equac~oes num multiprocessador) simplicac~ao do modelo de programac~ao: uma maquina paralela assncrona e mais simples de se programar que uma maquina sequencial com interrupc~oes interatividade com o usuario (ex.: interfaces gracas) e desaante! 2.1 Paralelismo Programac~ao Paralela Encontramos paralelismo em todos os lugares: { CPU e perifericos { Pipelines { Maquinas vetoriais { Canais de E/S { Multiprocessadores { Redes { e e bom lembrar que todos os computadores do mundo que encontramse ligados neste instante est~ao funcionando em paralelo! 17 ~ A PROGRAMACAO ~ PARALELA ASSINCRONA 18CAPITULO 2. INTRODUCAO 2.1.1 Modo de execuc~ao Modelo de Programac~ao Um problema central da programac~ao paralela e a implementac~ao de uma camada de abstrac~ao isto deve ser feito utilizando recursos de uma maquina dada para construir uma outra maquina; esta outra maquina oferece um modelo de programac~ao mais adequado para seus utilizadores Hardware de mono-processadores a maquina de base e formada de componentes de hardware : portas, ULAs, memorias, etc.; e projetado de forma a explorar o paralelismo (geralmente) sncrono inerente aos circuitos eletr^onicos; oferece uma maquina cujo modelo de programaca~o e sequencial com interrupc~oes . Nucleos de multiprogramac~ao a maquina de base e sequencial com interrupco~es (oferecida pelo hardware de um mono-processador) e projetado de forma a esconder as interrupc~oes oferece um modelo de programac~ao paralelo assncrono , com primitivas para coordenaca~o de processos (este mesmo modelo pode ser construdo a partir de um multiprocessador) Sistema operacional tradicional a maquina de base e paralela e assncrona (oferecida por um nucleo de multiprogramac~ao) e projetado de forma a explorar o paralelismo no uso dos recursos de hardware oferece um modelo de programac~ao sequencial sem interrupco~es ~ PARALELA ASSINCRONA 2.2. PROGRAMACAO 19 Conclus~oes Paralelismo 6= Programac~ao Paralela Um programa sequencial pode ser executado em paralelo Um programa paralelo pode ser executado sequencialmente Modo de execuc~ao e modelo de programac~ao s~ao conceitos distintos! 2.2 Programac~ao Paralela Assncrona Na programac~ao paralela assncrona, um programa e um conjunto de processos sequenciais ( conceito abstrato de processo n~ao deve ser confundido com o de processo de um sistema operacional como o Unix) a correc~ao de um programa n~ao deve depender das velocidades relativas dos processos primitivas de sincronizac~ao controlam as interac~oes entre os processos Pontos estudados neste curso: Como programar uma maquina paralela assncrona? Como implementar uma maquina paralela assncrona? 2.2.1 Porque PP assncrona? um computador deve reagir a estmulos cujo instante de chegada e im- previsvel, como a chegada de um pacote pela rede, ou o apertar de uma tecla. a troca de um componente por outro de desempenho superior ocorre todos os dias se podemos ter uma soluca~o mais geral, porque n~ao adota-la? ~ A PROGRAMACAO ~ PARALELA ASSINCRONA 20CAPITULO 2. INTRODUCAO 2.2.2 Express~ao do Paralelismo Linguagens como C ou Pascal n~ao possuem comandos para expressar exe- cuc~ao em paralelo (existem muitas bibliotecas que oferecem rotinas para isto) O Unix oferece o comando fork para cria c~ao de processos pesados e uma biblioteca para tratamento de processos leves Java oferece classes para criac~ao e execuca~o de processos leves, ou threads Existem inumeras propostas comerciais e acad^emicas Vamos comecar por uma proposta acad^emica: o par cobegin .. coend O Comando COBEGIN O comando S0; cobegin S1 || S2 || S3 coend; S4 prescreve a execuc~ao de S0, seguida da execuc~ao em paralelo de S1, S2 e S3, seguida da execuc~ao de S4 nenhuma suposic~ao e feita sobre as velocidades relativas de S1, S2 e S3 Grafo de Preced^encias grafo dirigido que expressa relac~oes de preced^encia entre possveis execuc~oes dos comandos de um programa paralelo nos : execuc~oes de comandos arcos : relac~oes imediatas de preced^encia ~ PARALELA ASSINCRONA 2.2. PROGRAMACAO Processo pai S0 S1 S2 21 S3 Processos filhos S4 N~ao existe relac~ao de preced^encia entre S1, S2 e S3 S1, S2 e S3 s~ao ditos paralelos , ou concorrentes Insistindo, n~ao fazemos suposico~es sobre velocidades relativas dos processos 2.2.3 Exemplos de Programas Paralelos Somas de 4 numeros: int x, y, z, w, s1, s2, s; ... cobegin s1 = x + y || s2 = z + w coend; s = s1 + s2 { com paralelismo efetivo, o tempo total e de 2 operac~oes de soma (o tempo sequencial seria de 3 somas) de 2 vetores: int A[100],B[100],C[100],i; ... cobegin ||(0..99) C[i] = A[i] + B[i]; coend ~ A PROGRAMACAO ~ PARALELA ASSINCRONA 22CAPITULO 2. INTRODUCAO Exerccio 2.2.1 Qual e o tempo mnimo para se fazer esta soma sequencialmente? E em paralelo? Sistema Operacional em Batch com SPOOL readFile(cardReader, inputFile); while (true) do { exec(inputFile, outputFile); cobegin printFile(outputFile, printer); || readFile(cardReader, inputFile); coend; } readFile exec readFile printFile exec readFile printFile exec Aninhamento S0; cobegin S1; || S2; cobegin S3; || S4; coend; S5; ^ 2.3. SEMANTICA DE PROGRAMAS PARALELOS 23 || S6; coend; S7; Exerccio 2.2.2 Qual e o grafo de preced^encias associado a este programa? 2.3 Sem^antica de Programas Paralelos 2.3.1 Conitos O que sera impresso por este programa? Programa IncDec: n = 4; cobegin n = n + 3; || n = n * 10 coend; printf(n); Devemos escolher uma sem^antica que torne confortavel a programac~ao e sua implementac~ao. Sem^antica sequencial : o efeito do comando paralelo deve ser equiva lente ao da execuc~ao sequencial de seus subcomandos (e o problema do implementador de um pipeline) Sem^antica n~ao-determinstica : o efeito do comando paralelo deve ser equivalente ao de uma das composic~oes seriais de seus subcomandos Com a sem^antica sequencial, este programa escreveria 70 Com a sem^antica n~ao-determinstica, este programa poderia escrever 70 ou 43 (e nada diferente disto!) A sem^antica n~ao-determinstica e a mais utilizada, por estar em acordo com a hipotese de independ^encia das velocidades relativas Programa IncDec Compilado para um Monoprocessador Adotamos a sem^antica n~ao determinstica. Mas sera que o programa vai mesmo imprimir 70 ou 43? Consideremos o seguinte programa, resultado de uma compilac~ao tradicional de IncDec (vamos omitir o codigo correspondente a criac~ao dos processos) ~ A PROGRAMACAO ~ PARALELA ASSINCRONA 24CAPITULO 2. INTRODUCAO n = 4; cobegin LDA n; ADD 3; STA n; || LDA n; MULT 10; STA n; } coend; printf(n); Compartilhamento de um monoprocessador A gura abaixo ilustra a forma tradicional de compartilhamento de um processa- Add Mu Nucleo Interrupcao Salva registradores Recarrega registradores RTI Interrupcao Salva registradores Recarrega registradores RTI dor; voltaremos a este ponto na seca~o3. Registradores s~ao variaveis locais! ^ 2.3. SEMANTICA DE PROGRAMAS PARALELOS 25 Uma computac~ao do programa IncDec compilado LDA n (A = 4) LDA n ADD 3 STA n (A = 4) (A = 7) (n = 7) MULT 10 STA n (A = 40) (n = 40) Este resultado (n=40) n~ao pode ser inferido do programa IncDec original ) a sem^antica do programa IncDec n~ao e denida! 2.3.2 Sem^antica Operacional Um programa paralelo tem a sua sem^antica denida se todos os seus possveis comportamentos podem ser inferidos de sua codicac~ao original Para isto, a atomicidade de qualquer conjunto de ac~oes com possvel execuc~ao em paralelo deve ser garantida Um conjunto de duas ou mais ac~oes e dito at^omico quando o efeito de sua execuc~ao em paralelo e serializavel , isto e, equivalente ao efeito da execuc~ao sequencial destas ac~oes em pelo menos uma ordenac~ao Reparem que a noc~ao de atomicidade se aplica a um conjunto de ac~oes, e n~ao a uma ac~ao em particular Implementac~ao de ac~oes at^omicas o hardware de qualquer computador garante a atomicidade de certos conjuntos de instruc~oes ex.: um monoprocessador simples so pode ser interrompido entre duas instruc~oes, o que faz com que o conjunto de todas as suas instruc~oes seja at^omico nestes casos a inibic~ao de interrupc~oes pode ser usada para garantir a formac~ao de blocos de instruc~oes com atomicidade garantida. o programa abaixo imprime 43 ou 70, como queramos: ~ A PROGRAMACAO ~ PARALELA ASSINCRONA 26CAPITULO 2. INTRODUCAO n = 4 cobegin DI; /* disable interrupts */ LDA n; ADD 3; STA n; EI; /* enable interrupts */ || DI; LDA n; MULT 10; STA n; EI; coend; printf(`n); 2.3.3 Comandos Conitantes a noc~ao de comandos conitantes e utilizada para determinar a atomicidade de conjuntos de comandos sejam: W (c) o conjunto de posic~oes de memoria escritas pelo comando c R(c) o conjunto de posic~oes de memoria lidas pelo comando c o comando c1 esta em conito com o comando c2 se W (c1 ) \ W (c2 ) 6= ;; ou W (c1 ) \ R(c2 ) 6= ;; ou R(c1 ) \ W (c2 ) 6= ; e (quase sempre) at^omico = n * 10) n~ ao e at^omico = 2*n) n~ ao e at^omico (a = 0, b = 0) (n = n + 3, n (n = n + 3, x Atomicidade em Linguagens de Alto Nvel Espera-se que qualquer conjunto de ac~oes denidas por comandos n~ao conitantes seja at^omico A noc~ao de comandos conitantes nem sempre e de facil aplicac~ao, pois depende do mapeamento de variaveis de alto nvel na memoria Par^ametros formais s~ao um caso particularmente complicado Variaveis locais em C, Pascal ou Java nunca constituem fonte de conitos, pois normalmente s~ao alocadas nas pilhas exclusivas dos processos ^ 2.3. SEMANTICA DE PROGRAMAS PARALELOS 27 Quando um programa prescreve a execuc~ao em paralelo de comandos conitantes, diz-se que ele esta provocando uma race condition (um nome tradicional mas infeliz, pois deixa a impress~ao de que um programa paralelo cujos resultados dependem das velocidades relativas dos processos esta obrigatoriamente errado) Um programa sequencial sintaticamente correto possui uma sem^antica bem denida, mesmo que n~ao seja aquela que se desejava E difcil fazer o mesmo com uma linguagem de alto nvel sem perda de paralelismo: (A[i]++,A[j]--) s~ao conitantes? A garantia de uma sem^antica bem denida ca aos cuidados do programador! Quais numeros este programa pode imprimir? int n; void soma100(void) { int i; for(i=0; i <= 100; i++) { LDA n; ADD 1; STA n; } } void main(void) { n = 0; cobegin P1:: soma100(); || P2:: soma100(); coend; printf(n) } 2.3.4 Resumo Paralelismo 6= Programac~ao Paralela A correc~ao de um programa paralelo assncrono n~ao deve depender das velocidades relativas dos processos A express~ao do paralelismo e relativamente simples Conitos abrem a possibilidade de programas sintaticamente corretos n~ao possuirem sem^antica denida Programas com sem^antica denica podem ter um conjunto de computac~oes complexo e surpreendente ~ A PROGRAMACAO ~ PARALELA ASSINCRONA 28CAPITULO 2. INTRODUCAO 2.4 Regi~oes Crticas O Par Produtor-Consumidor: Processos Produtor Consumidor Buffer Produtor-Consumidor: Primeira Tentativa void Produce(void) { while (true) do { ... /* produz o caracter c */ while(!Put(c)); /* insiste ate conseguir colocar no buffer*/ } } void Consume(void) { while (true) do { while(!Get(&c)); /* insiste ... */ ... /* consome o caracter c */ } } void main(void) { cobegin Produce(); || Consume(); coend } O Par Produtor-Consumidor: Buer char B[10]; ~ CRITICAS 2.4. REGIOES 29 int p=0, c=0, n=0; int Put(char x) { boolean ok = false; if(n < 10) { B[p] = x; p = (p+1) % 10; n++; ok = } return ok; } true; int Get(char *x) { boolean ok = false; if (n > 0) { *x = B[c]; c = (c+1) % 10; n--; ok = true; } return ok; } Programac~ao correta para uso sequencial Incorreta para uso em paralelo , pois ha conito na variavel n 2.4.1 Exclus~ao Mutua Regi~oes crticas s~ao trechos de codigo que tratam variaveis em conito Exclus~ao mutua : soluc~ao mais simples para se obter a sem^antica n~ao determinstica Pode-se usar variaveis do tipo key , com as operac~oes lock e unlock Todas as regi~oes crticas devem ser colocadas entre pares lock e unlock: key mutex; ... lock(mutex); regiao critica unlock(mutex) Um processo que executa lock sobre uma chave destravada trava a chave e continua sua progress~ao Um processo que executa lock sobre uma chave travada tem a sua execuc~ao suspensa ~ A PROGRAMACAO ~ PARALELA ASSINCRONA 30CAPITULO 2. INTRODUCAO Um processo que executa unlock destrava a chave, e permite que outros processos a espera desta chave tentem trava-la Nenhuma suposic~ao deve ser feita sobre a poltica de escolha do vencedor Uma chave e travada por no maximo um processo de cada vez Buer com exclus~ao mutua key mutex; char B[10]; int p=0, c=0, n=0; int Put(char x) { boolean ok = false; lock(mutex)} if(n $<$ 10) { B[p] = x; p = (p+1) % 10; n++; ok = true; } unlock(mutex); return ok; } int Get(char *x) { boolean ok = false; lock(mutex); if (n > 0) { *x = B[c]; c = (c+1) % 10; n--; ok = true; } unlock(mutex) return ok; } O paralelismo e perdido no acesso ao buer, mas ... o consumo e a produc~ao de caracteres continuam em paralelo! Buer com sincronizac~ao interna Vamos agora simplicar o uso do buer, embutindo em seu codigo a sincronizac~ao que era feita pelos clientes: void Produce(void) char c; while (true) { ~ CRITICAS 2.4. REGIOES 31 ... /* Produz o caracter c */ Put(c)} /* a sincronizacao eh problema do buffer */ } } void Consume(void) { char c; while (true) { Get(&c); ... /* consome c */ } } void main(void) { cobegin Produce(); || Consume(); coend } Buer com sincronizac~ao interna: programac~ao sujeita a bloqueios char B[10]; int p = 0, c = 0, n = 0; key mutex; void Put(char x) { lock(mutex); while (n >= 10); /* espera por uma posicao vazia */ B[p] = x; p = (p+1) % 10; n++; unlock(mutex); } void Get(char *x) { lock(mutex); while (n <= 0) ; /* espera por uma posicao cheia */ *x = B[c]; c = (c+1) % 10; n--; unlock(mutex); } Deadlocks Se p. ex. o produtor encontrar n 10, entrara em loop de espera, de posse da chave! ~ A PROGRAMACAO ~ PARALELA ASSINCRONA 32CAPITULO 2. INTRODUCAO Situac~oes de bloqueio como esta s~ao conhecidas como pelo termo em ingl^es deadlock Um deadlock e caracterizado por uma espera circular Exemplo: o processo p1 esta de posse do recurso r1 , e precisa do recurso r2 ; o processo p2 esta de posse de r2 , e precisa de r3 ; p3 esta de posse de r3 , e precisa de r1 para progredir. No caso acima, o produtor tem a chave, e precisa de uma posic~ao vazia; o consumidor tem a posica~o vazia, e precisa da chave. Programac~ao com espera ocupada char B[10]; int p = 0, c = 0, n = 0; key mutex; void Put(char x) { lock(mutex); while (n >= 10) {unlock(mutex); lock(mutex);} /* espera por uma posicao vazia */ B[p] = x; p = (p+1) % 10; n++; unlock(mutex); } void Get(char *x) { lock(mutex); while (n <= 0) { unlock(mutex); lock(mutex); } /* espera por uma posicao cheia */ *x = B[c]; c = (c+1) % 10; n--; unlock(mutex); } Programac~ao correta, mas com espera ocupada A espera ocupada e caracterizada por testes repetidos por uma condic~ao que impede o progresso de um processo, e que so pode ser alterada por outro processo Pode levar a desperdcios de tempo de processador 2.4.2 Liberac~ao do Processador A espera ocupada pode ser evitada com o uso de variaveis do tipo bed ; ~ CRITICAS 2.4. REGIOES 33 as operac~oes associadas s~ao wait(bed b; key k) e wakeup(bed b; key k) o processo que executa wait(b,k) deve estar de posse da chave k ; a chave k e liberada; o processador e liberado, e um descritor e colocado numa la associada a cama b o processo que executa wakeup(b,k) tambem deve faz^e-lo de posse da chave k ; todos os processos na la associada a b s~ao \acordados", e passam a disputar a chave k, que n~ao e liberada com esta operac~ao Buer com Liberac~ao do Processador char B[10]; int p = 0, c = 0, n = 0; key mutex; bed b; void Put(char x) { lock(mutex); while (n >= 10) wait(b,mutex); /* espera por uma posicao vazia */ B[p] = x; p = (p+1) % 10; n++; wakeup(b,mutex); unlock(mutex); } void Get(char *x) { lock(mutex); while (n <= 0) wait(b,mutex); /* espera por uma posi\c{c}\~{a}o cheia */ *x = B[c]; c = (c+1) % 10; n--; wakeup(b,mutex); unlock(mutex); } 2.4.3 Propriedades de programas paralelos e o problema dos Leitores e Escritores A exclus~ao mutua e o meio mais simples de se obter um comportamento serializavel. O paralelismo e eliminado nas regi~oes crticas, e a serializabilidade decorre trivialmente: o comportamento e serializavel porque e serial. ~ A PROGRAMACAO ~ PARALELA ASSINCRONA 34CAPITULO 2. INTRODUCAO As vezes a restric~ao imposta ao paralelismo e forte demais, como no classico problema dos leitores e escritores : Diversos processos acessam uma mesma tabela Processos leitores apenas consultam esta tabela Processos escritores podem modica-la Durante uma operac~ao de escrita o acesso a tabela deve ser restrito a um unico processo escritor N~ao existem restric~oes a leituras simult^aneas Escritor: ... requestWrite(); /* write */ ; releaseWrite(); Leitor: ... requestRead(); /* read */ ; releaseRead(); Leitores e Escritores: 1a soluc~ao key mutex; bed b; int nr = 0, nw = 0; void requestWrite(void) { lock(mutex); while((nr $>$ 0) \verb#||# (nw $>$ 0)) { wait(b,mutex); } nw++; unlock(mutex); } void requestRead(void) { lock(mutex); while(nw $>$ 0) { wait(b,mutex); } nr++; unlock(mutex); } Leitores e Escritores: 1a soluc~ao (continuac~ao) void releaseWrite(void) { lock(mutex); nw--; ~ CRITICAS 2.4. REGIOES 35 wakeup(b,k); unlock(mutex); } void releaseRead(void) { lock(mutex); nr--; wakeup(b,k); unlock(mutex); } Leitores e Escritores: Comentarios Um ponto importante a ser observado nesta soluc~ao e a tranquilidade garantida pela construc~ao while (condition) { wait(b,mutex); } /* sabemos que !condition \'{e} valida aqui! */ Um processo acordado n~ao cona nos testes sobre os dados compartilhados realizados antes de ir dormir; Muito importante, pois como a chave e liberada ao dormir, outro processo pode ter modicado estes dados. Leitores e Escritores: Propriedades Alguns autores classicam as propriedades de programas paralelos em dois grupos: Invari^ancia , ou seguranca , ou safety : uma coisa ruim nunca vai acontecer Progresso , ou liveness : uma coisa boa sempre vai acontecer A 1a soluc~ao para os leitores e escritores tem as seguintes propriedades de invari^ancia: Nunca teremos dois ou mais processos escritores escrevendo simultaneamente Nunca teremos um escritor escrevendo e um ou mais leitores lendo simultaneamente Nunca teremos uma espera circular congurando um deadlock ~ A PROGRAMACAO ~ PARALELA ASSINCRONA 36CAPITULO 2. INTRODUCAO Invari^ancia Propriedades de invari^ancia s~ao demonstradas por induc~ao , ou seja, para mostrar que P e um invariante, devemos mostrar que 1. P e valido inicialmente 2. toda operac~ao sobre a estrutura de dados compartilhada que se inicia com P valido deixa P valido Por \operac~ao" deve ser entendida toda modicac~ao feita com a posse da chave. A construc~ao while (condition) do wait(b,k) simplica muito este raciocnio indutivo. Progresso Uma propriedade de progresso que seria interessante para o problema dos leitores e escritores seria: Toda requisic~ao feita por processo leitor ou escritor sera satisfeita ao m de uma espera nita Nossa soluc~ao teria esta propriedade? Para ter alguma chance de ser satisfeita, nos devemos introduzir as hipoteses: 1. toda operac~ao de leitura ou escrita termina ao m de um tempo nito 2. a velocidade de progresso de todos os processos que n~ao est~ao dormindo e estritamente positiva Mas mesmo com estas hipoteses nossa soluc~ao n~ao satisfaz a propriedade de progresso acima, porque: um escritor pode esperar indenidamente enquanto processos leitores v~ao se revezando no uso da estrutura de dados, e um leitor pode esperar indenidamente enquanto processos escritores fazem o mesmo tipo de conspirac~ao. Nestes casos n~ao se congura um bloqueio do sistema, mas um processo tem o seu progresso impedido pela ac~ao de outros processos. Starvation Diz-se de um programa que n~ao satisfaz a uma propriedade de progresso que ele e injusto , pois permite que alguns processos morram de inanic~ao , ou starvation . ~ CRITICAS 2.4. REGIOES 37 Programas \justos" s~ao tambem chamados de equ^animes . A propriedade de justica e conhecida em ingl^es pelo termo fairness . Estes termos devem ser interpretados com frieza tecnica: um programa injusto pode ser mais adequado para uma certa tarefa do que um programa justo. Tudo depende da especicac~ao do problema: um escalonador n~ao equ^anime que privilegie tarefas urgentes como desligar um reator at^omico no caso de super aquecimento e evidentemente muito melhor que um escalonador justo. Propriedades de progresso exigem tecnicas mais elaboradas de prova, porque: Pode-se mostrar que um algoritmo n~ao obedece a uma especicac~ao de invari^ancia exibindo-se uma computac~ao que viole esta especicac~ao A inanic~ao so se revela em computac~oes innitas, e por isto exige uma demonstrac~ao formal de exist^encia ou inexist^encia Leitores e Escritores: Soluc~ao Justa Uma tecnica simples e poderosa para se impor um comportamento justo e o uso de tickets , como nos bancos ou lojas de reserva de passagens Ao chegar, um cliente pega um ticket; Os tickets s~ao numerados sequencialmente, e determinam a ordem de atendimento Tickets crescem monotonamente, e indenidamente; Com um numero razoavel de bits consegue-se programas que duram seculos. TicketQueue waiting; ... void requestWrite() { lock(mutex); int myTicket = waiting.getTicket(); while ((nW > 0) || (nR > 0)||(!waiting.isTheBest(rq))) { wait(b,mutex); } nW++; waiting.ok(myTicket); unlock(mutex); } void requestRead() { ~ A PROGRAMACAO ~ PARALELA ASSINCRONA 38CAPITULO 2. INTRODUCAO lock(mutex); int myTicket = waiting.getTicket(); while ((nW > 0) ||(!waiting.isTheBest(myTicket))) { wait(b,mutex); } nR++; waiting.ok(myTicket); wakeup(b,mutex); unlock(mutex); } void releaseWrite() { lock(mutex); nW--; wakeup(b,mutex); unlock(mutex); } void releaseRead() { lock(mutex); nR--; wakeup(b,mutex); unlock(mutex); } TicketQueue int ticket = 0; OrderedQueue q; // fila ordenada com operacoes put, top e pop int getTicket() { q.put(++ticket); return ticket; } boolean isTheBest(int ticket) { if (q.top() != null){ return ticket == q.top(); } return true; } void ok(int ticket) { if (ticket != q.pop()) { //pop() devolve o melhor e o retira da lista ERRO!; ~ CRITICAS 2.4. REGIOES 39 } } 2.4.4 Imposic~ao de Polticas de Escalonamento: Programac~ao de um Servidor de Impressora Uma impressora e compartilhada por diversos processos Seu servidor deve garantir a exclus~ao mutua no seu uso Soluc~ao trivial: usar uma chave para controlar o acesso: key printerKey; void print(file F) { lock(printerKey); /* impressao de F */ unlock(printerKey); } Inconvenientes: 1. se quisermos acrescentar procedimentos no estilo lpq e lprm para vericac~ao e alterac~ao do estado da la de impress~ao, teramos que ter acesso ao estado da la da chave, o que nem sempre e possvel 2. o escalonamento da impressora e feito por um nucleo de multiprogramac~ao, impossibilitando a imposic~ao de qualquer poltica de uso da impressora Servidor de Impressora: Soluc~ao com Erro Um exemplo para mostrar como o uso do if ao inves do while pode ser muito perigoso: void print(ProcessId client, File f) { requestPrinter(client); /* impress\~{a}o de F */ releasePrinter(client); } boolean busy = false; key mutex; bed b; Queue q = new Queue(); ProcessId owner; ~ A PROGRAMACAO ~ PARALELA ASSINCRONA 40CAPITULO 2. INTRODUCAO void requestPrinter(ProcessId client) { lock(mutex); q.enter(client); if (busy) { wait(b,mutex); } busy = true; q.leave(client); owner = client; unlock(mutex); } void releasePrinter(ProcessId client) { lock(mutex); busy = false; owner = null; wakeup(b, mutex); unlock(mutex); } O escalonamento continua sendo decidido pelo nucleo, mas isto n~ao e o mais grave; Um processo acordado pode perder a disputa pela chave para outro processo executando lock; Dois - ou mais - processos poder~ao ser autorizados a usar a impressora simultaneamente! Servidor de Impressora: Soluc~ao \correta" Substituindo if por while, corrigimos o problema de desrespeito a propriedade de invari^ancia da exclus~ao mutua no uso da impressora: boolean busy = false; key mutex; bed b; Queue q = new Queue(); ProcessId owner; void requestPrinter(ProcessId client) { lock(mutex); q.enter(client); while (busy) { wait(b,mutex); } busy = true; q.leave(client); owner = client; unlock(mutex); ~ CRITICAS 2.4. REGIOES 41 } void releasePrinter(ProcessId client) { lock(mutex); busy = false; owner = null; wakeup(b, mutex); unlock(mutex); } Mas continuamos com o escalonamento imposto pelo nucleo! SJF: Shortest Job First O uso de um recurso como uma impressora pode ser melhorado com uma poltica de escalonamento Shortest Job First , ou SJF , privilegia os servicos menores, propiciando um tempo medio de espera menor Para forcar esta poltica, podemos criar uma la de requisic~oes, similar a TicketQueue usada para impor fairness no problema dos leitores e escritores (sec~ao 2.4.3). Nos vamos aqui procurar evitar o problema de eci^encia causado ao se acordar todos os processos usando wakeup, sendo que apenas um deles tera condic~oes de progresso, enquanto todos os outros voltar~ao para dormir. Como wakeup acorda todos os processos dormindo numa cama, vamos precisar de diversas camas para implementar o nosso escalonamento. Vamos acordar somente o processo selecionado pela poltica de escalonamento. O Escalonador SJF - 1a vers~ao A seguinte estrutura de dados e conveniente para o registro de uma requisic~ao de impress~ao: class Request { int fileSize; Bed bed; ProcessId client; } SJFQueue waiting; Com isto, temos: ~ A PROGRAMACAO ~ PARALELA ASSINCRONA 42CAPITULO 2. INTRODUCAO void requestPrinter(ProcessId client, int fileSize) { lock(mutex); Request rq = waiting.getRequest(client, fileSize); while (busy) wait(rq.bed, mutex); q.ok(rq); busy = true; unlock(mutex); } void releasePrinter() { lock(mutex); busy = false; wakeup((waiting.top().bed, mutex); unlock(mutex); } SJFQueue OrderedQueue q = new OrderedQueue(new SizeComparator()); /* a fila \'{e} criada com o criterio de comparacao correspondente */ Request getRequest(ProcessId client, int jobSize) { Request rq = new Request(client, jobSize); q.put(rq); return rq; } void ok(Request rq) { q.del(rq); } Request top() { return (Request) q.top(); } Mas ainda existem chances de um arquivo grande furar a la! Como? Qual seria uma soluc~ao para este problema? O Escalonador SJF - 2a vers~ao void requestPrinter(ProcessId client, int fileSize) { lock(mutex); Request rq = waiting.getRequest(client, fileSize); while (busy || !waiting.isTheBest(rq)) wait(rq.bed, mutex); ~ CRITICAS 2.4. REGIOES 43 q.ok(rq); busy = true; unlock(mutex); } void releasePrinter() { lock(mutex); busy = false; wakeup((waiting.top().bed, mutex); unlock(mutex); } SJFQueue OrderedQueue q = new OrderedQueue(new SizeComparator()); /* a fila \'{e} criada com o criterio de comparacao correspondente */ Request getRequest(ProcessId client, int jobSize) { Request rq = new Request(client, jobSize); q.put(rq); return rq; } void ok(Request rq) { q.del(rq); } boolean isTheBest(Request rq ) { return ( rq == top() ); } Request top() { return (Request) q.top(); } 2.4.5 Conclus~oes 1. A exclus~ao mutua e a ferramenta basica para se obter um comportamento serializavel de operac~oes sobre uma estrutura de dados compartilhada 2. Ferramentas para liberac~ao do processador s~ao indispensaveis na pratica 3. Escalonamentos com polticas arbitrarias podem ser implementados com uma programac~ao mais elaborada ~ A PROGRAMACAO ~ PARALELA ASSINCRONA 44CAPITULO 2. INTRODUCAO 4. Existem propriedades de programas paralelos que n~ao t^em similares para programas sequenciais, como a aus^encia de deadlocks, ou starvation 5. Resta o problema da implementac~ao de lock e unlock; nos voltaremos a este ponto na sec~ao3. 2.5 Exerccios Exerccio 2.5.1 (Quicksort paralelo) Proponha uma vers~ao paralela para o Quicksort. Mostre o grafo de preced^encias associado ao procedimento. Supondo que a mediana de cada partic~ao e sempre escolhida como piv^o, calcule o tempo necessario para a ordenac~ao de n elementos por um computador com muitos processadores. Considere nulo o tempo necessario para a criac~ao e termino de processos. Observac~ao: Classicac~ao em paralelo constitui um campo ativo de pesquisa; um panorama da area pode ser encontrado em [Bitton et al., 1984] Exerccio 2.5.2 (Comparac~ao e Troca em paralelo) (Soluc~ao na sec~ao2.6.2 ) Proponha um \sort" paralelo baseado no algoritmo de comparac~ao e troca, onde fases pares e mpares se alternam. Nas fases pares os elementos de ndice par s~ao comparados com os seus sucessores imediatos, sendo feitas trocas nas posic~oes desordenadas. As fases mpares s~ao analogas. Calcule o tempo necessario para a ordenac~ao de um vetor com n elementos. Exerccio 2.5.3 (Transaco~es) (Soluc~ao na sec~ao2.6.3 ) Considere o seguinte programa: a = 1; b = 1; cobegin T1:: a := a + 100; b := b + 100; || T2:: a := a * 2; b := b * 2; coend Na terminologia introduzida por [Eswaran et al., 1976], uma transac~ao e uma unidade de consist^encia , isto e, um procedimento que, se executado isoladamente, preserva a consist^encia de um banco de dados. Supondo que a = b e a condic~ao de consist^encia no programa acima, T1 e T2 seriam transac~oes. O controle de concorr^encia de um banco de dados deve cuidar para que o efeito de transac~oes executadas em paralelo seja equivalente a sua execuc~ao numa sequ^encia qualquer. Acrescente ao programa acima comandos de sincronizac~ao que garantam a serializabilidade de sua execuc~ao. Procure maximizar o paralelismo, n~ao utilizando uma unica regi~ao crtica. 2.5. EXERCICIOS 45 Exerccio 2.5.4 (Escalonamento SJF) Uma impressora e compartilhada por diversos processos. Cada processo cliente submete seu arquivo chamando printerScheduler.submit(int leSize); Seu servidor deve: 1. garantir a exclus~ao mutua no uso da impressora, e 2. dar prioridade aos arquivos menores, impondo uma poltica SJF ( Shortest Job First) no escalonamento. Exerccio 2.5.5 (Aging) Arquivos muito grandes podem ser preteridos indenidamente numa poltica SJF. Mude a soluc~ao do problema anterior, usando a tecnica de aging (envelhecimento), onde: a prioridade inicial de cada submiss~ao e o tamanho do arquivo a ser impresso; cada vez que um processo ganha a impressora, ele aumenta a prioridade de todos os processos que est~ao esperando. Compare a poltica de aging com as polticas SJF e FIFO. Exerccio 2.5.6 (Controlando o nvel de paralelismo) Modique a soluc~ao para o problema dos leitores e escritores de forma a permitir que no maximo 4 leitores consigam ler simultaneamente. Exerccio 2.5.7 (A rvore-B) (Soluc~ao na sec~ao2.6.4 ) Um sistema de controle de concorr^encia de uma arvore-B pode adotar a seguinte sistematica: pode-se ter um numero arbitrario de leituras simult^aneas; um processo escritor (que modica a arvore) comporta-se da seguinte maneira: { requisita acesso exclusivo com relac~ao aos outros escritores; leitores podem continuar lendo, e novos leitores podem entrar; { obtida a autorizac~ao, caminha na arvore-B ate atingir o ponto onde faz efetivamente a modicac~ao; { requisita ent~ao a extens~ao aos leitores da exclusividade do acesso; { faz a modicac~ao, e libera a arvore. Programe um modulo que faca este controle de concorr^encia. Observac~ao : este controle de concorr^encia e ing^enuo, restringindo desnecessariamente o paralelismo. Algoritmos muito mais ecientes podem ser encontrados por exemplo em [Johnson and Shasha, 1993]. ~ A PROGRAMACAO ~ PARALELA ASSINCRONA 46CAPITULO 2. INTRODUCAO Exerccio 2.5.8 (Pool de Recursos Id^enticos) R recursos id^enticos s~ao compartilhados por C processos clientes. Um recurso n~ao pode ser utilizado por mais de um processo simultaneamente. A vida de um processo cliente segue o ciclo: pensa; requisita n recursos, onde n e um valor arbitrario entre 1 e R; utiliza os n recursos; devolve os n recursos. a. Proponha uma soluc~ao que utilize a tecnica de aging para aumentar a taxa de utilizac~ao dos recursos mantendo a garantia de equanimidade. b. Argumente a favor da correc~ao da soluc~ao proposta. Exerccio 2.5.9 (Buer com mutiplos clientes) Encontre uma soluc~ao equ^anime (livre de starvation ) para o problema do compartilhamento de um buer limitado por um numero qualquer de processos produtores e consumidores. 2.6 Respostas 2.6.1 Quicksort Paralelo (Exerccio 2.5.1) void quicksort(int a[], int left, int right) { if(left < right) { int pivot = choosePivot(a,left,right); Partition p = partition(a, left, right, pivot); cobegin quicksort(a,left,p.right); || quicksort(a,p.left,right); coend; } } Analise : Supondo que o pivot escolhido e sempre a mediana, o algoritmo fara n comparac~oes na primeira partic~ao. No segundo nvel de recursividade ser~ao feitas duas partic~oes, cada uma de tamanho n=2; no total ser~ao tambem feitas n comparaco~es. Entretanto, como as partic~oes s~ao feitas simultaneamente, o tempo gasto sera proporcional a n=2. No terceiro nvel, 2.6. RESPOSTAS 47 teremos quatro partic~oes simult^aneas, cada uma de tamanho n=4, e assim por diante. Desta forma, o tempo total gasto sera dado pela soma T = n1 + n2 + + 1 = n (2 ; 1=n) Temos portanto um sort com complexidade (para o melhor caso) de O(n), melhor do que o limite de O(n log(n) para algoritmos sequenciais. O metodo partition pode ser implementado com paralelismo, o que reduz a metade o tempo ideal de processamento, mas n~ao altera a ordem de complexidade. 2.6.2 Comparac~ao e Troca em Paralelo (Exerccio 2.5.2) void sort(int a[],int for(int i = 0; i < cobegin ||(k=0..n/2) coend; cobegin ||(k=1..n/2) coend; } } n) { n/2; i++) { compareAndSwap(a[2k], a[2k+1]); compareAndSwap(a[2k-1],a[2k]); Claramente temos um algoritmo de complexidade O(n), para o pior caso, com desempenho bem superior ao do Quicksort paralelo. Este e um exemplo de que paralelizac~oes de bons algoritmos sequenciais podem resultar em algoritmos paralelos ruins, e vice-versa. 2.6.3 Transac~oes (Exerccio 2.5.3) key mutexA, mutexB; a = 1; b = 1; cobegin T1:: lock(mutexA); a = a + 100; lock(mutexB); unlock(mutexA); b = b + 100; unlock(mutexB); || ~ A PROGRAMACAO ~ PARALELA ASSINCRONA 48CAPITULO 2. INTRODUCAO T2:: lock(mutexA); a = a * 2; lock(mutexB); unlock(mutexA); b = b * 2; unlock(mutexB); coend Obs.: a tecnica utilizada nesta soluca~o e bastante geral, sendo conhecida como two-phase locking (veja a seca~o 7), pois nenhum lock e adquirido apos o relaxamento de qualquer lock. Exerccio 2.6.1 O que aconteceria se tivessemos T2:: b = b * 2; a = a * 2, e empregassemos two-phase locking? 2.6.4 A rvore B (Exerccio 2.5.7) class BTreeConcurrencyController { int nww = 0, nwr = 0, nr = 0; TicketQueue q; Bed bed; Key mutex; void readerRequestRead() { lock(mutex); Ticket myTicket = q.getTicket(); while ((nww > 0) || (!q.isTheBest(myTicket))) { wait(bed,mutex); } nr++; waiting.ok(myTicket); wakeup(bed,mutex); unlock(mutex); } void writerRequestRead() { lock(mutex); Ticket myTicket = q.getTicket(); while ((nww + nwr > 0) || (!q.isTheBest(myTicket))) { wait(bed, mutex); } nwr++; waiting.ok(myTicket); wakeup(bed,mutex); unlock(mutex); } 2.6. RESPOSTAS 49 A rvores-B: Soluc~ao (cont) void requestWrite() { lock(mutex); Ticket myTicket = q.getTicket(); while ((nr > 0) || (!q.isTheBest(myTicket))) { wait(bed, mutex); } nww++; nwr--; q.ok(myTicket); unlock(mutex); } void releaseWrite() { lock(mutex); nww--; wakeup(bed,mutex); unlock(mutex); } void releaseRead() { lock(mutex); nr--; wakeup(bed,mutex); unlock(mutex); } Observac~ao : O controle de concorr^encia em arvores-B e um problema muito estudado, e as soluc~oes conhecidas s~ao mais sosticadas, possibilitando um paralelismo bem superior ao da nossa proposta. 2.6.5 Pool de Recursos Id^enticos (Exercicio 2.5.8) Cliente i: loop ... r = randomChoice(1,R); List myResources = pool.getResources(myProcessId, r); /* usa os recursos na lista myResources */ pool.releaseResources(myResources); ... end class Pool { ~ A PROGRAMACAO ~ PARALELA ASSINCRONA 50CAPITULO 2. INTRODUCAO Key mutex; AgingQueue q; List free = new List(); List getResources(ProcessId pid, int r) { List result; lock(mutex); Request rq = q.getRequest(pid, r); while ((free.count() < r) || (!q.isTheBest(rq)))) { wait(rq.bed, mutex); } q.ok(rq); wakeup(q.top().bed, mutex); /* os restantes podem servir */ result = free.getList(r); unlock(mutex); return result; } void releaseResources(List rList) { lock(mutex); free.put(rList); wakeup(q.top().bed, mutex); unlock(mutex); } } class Request { int number; int age; Bed bed; ProcessId client; } class AgingQueue { OrderedQueue waiting = new OrderedQueue(new NumberPlusAgeComparator()); Request getRequest(ProcessId client, int nResources) { Request rq = new Request(client, nResources, 0); waiting.put(rq); return rq; } void ok(Request rq) { waiting.del(rq); waiting.getOlder(); } 2.6. RESPOSTAS boolean isTheBest(Request rq ) { return ( rq == waiting.top() ); } Request top() { return (Request) waiting.top(); } } 51 ~ A PROGRAMACAO ~ PARALELA ASSINCRONA 52CAPITULO 2. INTRODUCAO 2.7 1a Prova Distribuic~ao: 30/09/98 Devoluc~ao por e-mail ou disponibilizac~ao na pagina do aluno: 01/10/98 A prova e estritamente individual. A consulta ao material do curso e permitida, mas por favor n~ao procurem soluc~oes prontas em artigos por a. Construam suas proprias soluc~oes. A avaliac~ao de alunos e do professor e o objetivo essencial desta prova. Gabarito 2.7.1 1a Quest~ao Descreva os conceitos listados a seguir, explicando sua import^ancia no contexto da programac~ao paralela: a. processos assncronos b. exclus~ao mutua c. serializabilidade d. espera ocupada e. equanimidade 2.7.2 2a Quest~ao Considere uma ponte estreita, com passagem para um so carro, em uma estrada que permite trafego em duas m~aos, de leste para oeste e no sentido contrario. Um carro que se dirija para leste so pode entrar na ponte se n~ao houver nenhum carro na ponte vindo na direc~ao contraria. Dois ou mais carros podem estar sobre a ponte, desde que estejam trafegando na mesma direc~ao. Programe um sincronizador para a ponte, usado por processos associados aos carros. 2.7.3 3a Quest~ao Resolva o problema dos leitores e escritores impondo a seguinte poltica: um leitor n~ao pode comecar a ler se algum escritor estiver esperando, a n~ao ser no caso abaixo; 2.7. 1A PROVA 53 ao m de uma operac~ao de escrita, todos os leitores que estiverem esperando devem comecar a ler. Sua soluc~ao tem problemas de starvation? Porque? 2.7.4 4a Quest~ao Uma barbearia consiste de uma sala de espera com n cadeiras comuns, e de uma unica cadeira de barbeiro. O barbeiro atende a um cliente de cada vez. Se um cliente entra na barbearia e todas as cadeiras est~ao ocupadas, ele vai embora. Se o barbeiro esta ocupado mas existem cadeiras comuns disponveis, o cliente espera usando uma cadeira. Programe um sincronizador para a barbearia, usado pelo processo barbeiro e por processos clientes. 2.7.5 5a Quest~ao Uma barreira e uma construc~ao para a sincronizac~ao de processos que e util para assegurar que todos os processos envolvidos em uma tarefa atingiram um ponto determinado em suas computac~oes. A barreira e inicializada com um valor inteiro. Se um processo executa bar.barrier(int b), ele e bloqueado na barreira bar ate que o numero de processos igualmente bloqueados na barreira atinja b, quando a progress~ao de todos os processos em espera na barreira e liberada. Programe uma classe Barrier que ofereca este tipo de sincronizac~ao. ~ A PROGRAMACAO ~ PARALELA ASSINCRONA 54CAPITULO 2. INTRODUCAO Cap tulo 3 Nucleos de Multiprogramac~ao Objetivo de um nucleo de multiprogramac~ao: obter uma maquina virtual paralela, com primitivas de sincronizac~ao, a partir de uma maquina sequencial com interrupc~oes Plano da aula: 1. Interrupc~oes em PCs 2. Co-rotinas 3. Multiplexac~ao do processador 4. Camas e chaves 5. Resumo e Conclus~oes 3.0.1 Interrupc~oes em PCs Interrupc~oes est~ao na base das tecnicas de multiplexac~ao de um monoprocessador A construc~ao de um nucleo de multiprogramac~ao exige um conhecimento detalhado do mecanismo de interrupc~ao da maquina alvo Vamos estudar um nucleo para um PC-XT, que usa um processador simples, com a arquitetura Intel 8086, onde interrupc~oes entre instruc~oes, sem memoria virtual. Este nucleo implementa uma poltica round-robin para compartilhamento do processador, onde o controle do processador e trocado a cada interrupc~ao do relogio 55 ~ CAPITULO 3. NUCLEOS DE MULTIPROGRAMACAO 56 O relogio e um dispositivo externo cuja unica funca~o e interromper o processador central periodicamente; o perodo entre interrupc~oes e programavel. O relogio n~ao e o clock do processador! A atomicidade de trechos de codigo formados por uma unica instruc~ao e garantida A inibic~ao de interrupc~oes e o mecanismo usado para garantir a atomicidade de trechos mais longos de codigo. O Controlador de Interrupc~oes O processador principal (8086, ou 8088) possui um pino de interrupc~ao; A este pino de interrupc~ao esta ligado um integrado, o 8259, que e um controlador programavel de interrupc~oes Cada dispositivo de E/S esta ligado a uma linha IRQ (Interrupt ReQuest) do 8259 Existem 8 linhas IRQ, numeradas de 0 a 7; a IRQ 0 possui a mais alta prioridade Interrupc~oes podem ser inibidas tanto no 8086 como (de forma seletiva) no 8259 A ligac~ao padr~ao num XT e: IRQ 0 1 2 3 4 5 6 7 Dispositivo Relogio Teclado Porto assncrono 1 Porto assncrono 2 Controlador de disco xo Controlador de disco exvel Controlador da impressora Driver do vdeo Interrupc~oes: Tratamento pelo Hardware Um dispositivo que deseja interromper o 8086 \levanta" a sua IRQ, e o 8259 tenta ent~ao interromper o 8086 Ao se deixar interromper, o 8086 57 1. empilha os registradores PSW (Program Status Word), CS (Code Segment), e IC (Instruction Counter), de forma a poder retornar ao ponto onde foi interrompido; 2. pede ao 8259 o numero n do dispositivo que interrompeu o 8259 transmite ao 8086 este numero n, que e igual ao numero da IRQ + 8 (se mais de uma IRQ estiver levantada, e o numero da IRQ de mais alta prioridade que e transmitido ao 8086) Ao receber n, o 8086 coloca em seu registrador CS os dois bytes com endereco absoluto 4n, e em seu registrador IC os dois bytes com endereco 4n + 2; isto inicia o tratamento por software da interrupc~ao Ex.: em uma interrupc~ao do relogio, n = 0, CS recebe os bytes 32 e 33, e IC os bytes 34 e 35 Este conjunto de 4 bytes e chamado de vetor de interrupc~ao associado a n Interrupc~oes: Tratamento por Software O tratador da interrupc~ao deve salvar os registradores, o que normalmente e feito na pilha Os valores de BP (Base Pointer) e de SP(Stack Pointer) devem ser armazenados para posterior recuperac~ao Apos o tratamento da interrupc~ao, BP e SP devem ser restabelecidos, os demais registradores desempilhados, e a instruc~ao RETI (Return of interrupt) deve ser executada 3.0.2 Co-rotinas O nucleo apresentado aqui e inspirado em Modula-2 (Wirth, 1980?), uma linguagem sucessora do Pascal, com boas ferramentas de modularizac~ao mas sem orientac~ao a objetos O conceito de co-rotina implementado em Modula-2 e util para a estruturac~ao da programac~ao de uma aquina sequencial sujeita a interrupc~oes Uma variavel do tipo co-rotina pode conter um \contexto" (no caso do XT, essencialmente BP e SP) do processador Trocas de contexto podem ser feitas atraves do procedimento transfer(fromCoroutine, toCoroutine), que armazena o contexto corrente em fromCoroutine, e estabelece o contexto armazenado em toCoroutine ; ~ CAPITULO 3. NUCLEOS DE MULTIPROGRAMACAO 58 Uma co-rotina e criada atraves de um procedimento newCoroutine(procedimento inicial, area de trabalho) A area de trabalho ira conter a pilha associada a esta co-rotina, e e inicializada como se esta co-rotina tivesse sido interrompida no incio de seu procedimento inicial Co-rotinas e Interrupc~oes Transfer^encias de contexto tambem podem ser engatilhadas para ocorrer no momento de uma interrupc~ao, atraves do procedimento iotransfer(fromCoroutine, toCoroutine, interruptVector); iotransfer tambem armazena o contexto corrente em FromCoroutine, e estebelece toCoroutine Alem disto, na ocorr^encia de uma interrupc~ao associada a interruptVector, o contexto corrente sera armazenado em toCoroutine, e sera feita uma transfer^encia para fromCoroutine Importante: o contexto de uma co-rotina inclui o status de habilitac~ao ou desabilitac~ao de interrupc~oes 3.0.3 Multiplexac~ao do Processador Interface de um nucleo simples Process createProcess( initialProcedure, workSpaceSize) cria um novo processo; processos lhos n~ao podem criar processos; um processo criado n~ao comeca a rodar void coStart() dispara todos os processos criados; o processo pai e atrasado ate a morte dos lhos void processEnd() indica ao nucleo o termino de um processo; so pode ser utilizada por processos lhos Co-rotinas e processos A cada processo (incluindo o processo pai) deve corresponder uma corotina Uma co-rotina extra corresponde ao nucleo O procedimento coStart() executa um transfer da co-rotina father para a co-rotina kernel 59 O nucleo escalona o proximo processo a ser rodado, e executa um iotransfer da co-rotina kernel para a co-rotina escolhida, associado a interrupc~ao do relogio Quando o relogio interrompe, o contexto corrente e armazenado na corotina correspondente, e o contexto da co-rotina kernel e restabelecido Multiplexac~ao do Processador entre as co-rotinas father kernel coStart transfer p1 p2 iotransfer interrupt iotransfer interrupt iotransfer transfer endProcess iotransfer transfer endProcess transfer Estruturas de Dados Process: campos coroutine, state Queue ready: Coroutine fatherCoroutine, kernelCoroutine; Process currentProcess int nrofActiveProcesses contem os processos prontos para rodar esta ultima e criada na inicializac~ao do nucleo, com o procedimento inicial schedule(); 60 ~ CAPITULO 3. NUCLEOS DE MULTIPROGRAMACAO Criac~ao de processos e coStart class Process { Coroutine coroutine; byte[] workArea; int state; public Process(procedure initialProcedure, int workAreaSize) { coroutine = createCoroutine(initialProcedure, new byte[workAreaSize]); /* cria a co-rotina correspondente ao processo */ kernel.addProcess(this); state = READY; } } no kernel: public void addProcess(Process p){ ready.put(p); nrofActiveProcess++; } public void coStart() { transfer(fatherCoroutine, kernelCoroutine); } Escalonamento e m dos processos void schedule() { disableInterrupts(); while(nrofActiveProcesses > 0) { currentProcess = ready.get(); /* escolhe o proximo processo */ iotransfer(kernelCoroutine, currentProcess.coroutine, clockInterruptVector); if(currentProcess.state == READY) ready.put(currentProcess); } transfer(kernelCoroutine, fatherCoroutine); } void processEnd() { disableInterrupts(); nrofActiveProcesses--; currentProcess.state = FINISHED; transfer(currentProcess.coroutine, kernel); 61 } 3.0.4 Chaves e Camas Chaves class Key { boolean locked; Queue waiting; } void lock(Key k) { InterruptStatus is = saveInterruptStatus(); disableInterrupts(); if(k.locked) { k.waiting.put(currentProcess); currentProcess.state = WAITING; transfer(currentProcess.coroutine, kernel); } k.locked = true; is.restore(); /* lock pode ser chamada por processos com interrupcoes inibidas ou nao, e por isto o status \'{e} restabelecido */ } void unlock(Key k) { InterruptStatus is = saveInterruptStatus(); disableInterrupts(); if(k.waiting.empty()) { k.locked = false; } else { Process p = k.waiting.get(); p.state = READY; ready.put(p); } is.restore(); } Camas class Bed { Queue waiting; } 62 ~ CAPITULO 3. NUCLEOS DE MULTIPROGRAMACAO void wait(Bed b, Key k) { InterruptStatus is = saveInterruptStatus(); disableInterrupts(); currentProcess.state = WAITING; unlock(k); b.waiting.put(currentProcess); transfer(currentProcess.coroutine, kernel); is.restore(); } void wakeup(Bed b, Key k) { InterruptStatus is = saveInterruptStatus(); disableInterrupts(); while ((Process p = b.waiting.get()) != null) { p.state = WAITING; k.waiting.put(p); } is.restore(); } Multiprocessadores Em um multiprocessador com memoria compartilhada a inibic~ao de interrupc~oes n~ao e suciente para obter a exclus~ao mutua no acesso ao nucleo; Este mecanismo e implementado por instruco~es especiais ( [Dinning, 1989] descreve este e inumeros outros mecanismos; [Dubois et al., 1988] tambem parece ser interessante) como boolean testAndSet(boolean lockState, boolean newLockState) Instruc~oes testAndSet tem a atomicidade relativa a outras testAndSet garantida pelo hardware; boolean testAndSet(boolean lockState, boolean newLockState) faz lockState = newLockState e retorna o valor antigo deste par^ametro Codigo para travamento do nucleo: while (testAndSet(lockState, true))fg Codigo para destravamento do nucleo: testAndSet(lockState, false) (reparem no uso de espera ocupada) 63 3.0.5 Resumo e Conclus~oes A reaca~o a interrupc~oes e feita em parte por hardware, e em parte por software Co-rotinas s~ao um bom meio de estruturar as mudancas de contexto que ocorrem apos as interrupc~oes Um nucleo simples de multiprogramac~ao utiliza uma co-rotina por processo criado, uma co-rotina para o processo pai, e uma co-rotina para o proprio nucleo A exclus~ao mutua no acesso ao nucleo e obtida por inibic~ao de interrupc~oes 3.0.6 Exerccios Exerccio 3.0.1 (Sleep) Modique o nucleo da sec~ao 3 para implementar a primitiva sleep(int nrofTicks) , que faz com que um processo pare de disputar o processador pelo numero de ticks especicado. Exerccio 3.0.2 (Mensagens) Mensagens s~ao muitas vezes utilizadas para a interac~ao entre processos. Modique o nucleo da sec~ao 3 de forma a implementar as primitivas send(Process dest, Message m) e receive(Process source, Message m) . Implemente os modos sncrono, onde o processo que envia uma mensagem e bloqueado ate a sua recepc~ao, e assncrono, sem bloqueio do remetente. Exerccio 3.0.3 (Cobegin-Coend generalizado) Discuta a implementac~ao de nveis arbitrarios de cobegin-coend. Exerccio 3.0.4 (Fork-Join) Modique o nucleo da sec~ao 3 para implemen- tar a funca~o fork(Process p) , que dispara o processo recem-criado p sem interromper a execuc~ao do processo que faz o disparo, e a func~ao join(Process p) , que bloqueia o processo que a executa at e a morte do processo p . Exerccio 3.0.5 (Controle da Multiplexac~ao) Acrescente ao nucleo dado em aula os procedimentos stopRoundRobin() e startRoundRobin(int timeSlice) para controlar a preempc~ao de processos por ocasi~ao das interrupc~oes de relogio. Exerccio 3.0.6 (Mensagens Assncronas) Modique o nucleo de multipro- gramac~ao dado em aula acrescentando primitivas para comunicac~ao assncrona por mensagens. 64 ~ CAPITULO 3. NUCLEOS DE MULTIPROGRAMACAO Cap tulo 4 Introduc~ao a Programac~ao Paralela em Java 4.1 Introduc~ao 4.1.1 O Fen^omeno Java A Linguagem de Programac~ao Java Segundo a Sun, Java e simples, neutra com relac~ao a arquiteturas, orientada a objetos, portatil, distribuda, tem alto desempenho, e interpretada, multitarefa, robusta, din^amica e segura Java e compilada e interpretada: Os bytecodes podem ser vistos como o codigo de maquina da JVM, a Java Virtual Machine. 65 ~ A PROGRAMACAO ~ PARALELA EM JAVA 66CAPITULO 4. INTRODUCAO A Plataforma Java Dois componentes: a Java Virtual Machine (JVM) a Java Application Programming Interface (Java API) O que voc^e pode fazer em Java programas (aplicac~oes) applets servidores servlets agentes The core Java API O Basico: Objetos, strings, threads, numeros, entrada/sada, estruturas de dados, etc. Applets: Convenc~oes usadas por applets Rede: URLs, sockets, enderecos IP Internacionalizac~ao: Ferramentas para produca~o de programas poliglotas Seguranca: Assinaturas eletr^onicas, ger^encia de chaves publicas e privadas, controle de acesso e certicados Componentes: conhecidos como \Java Beans", podem se conectar a diversas arquiteturas de componentes Serializac~ao de Objetos: possibilita a transmiss~ao e a persist^encia de objetos, assim como a comunicac~ao via RMI (Remote Method Invocation) Conectividade com Bancos de Dados: O uso de JDBC permite um acesso uniforme a diversos bancos de dados ~ 4.1. INTRODUCAO 67 Outras vantagens Aprendizado facil Codigo compacto (ate 4 vezes menor que C++) Codigo de qualidade (sem coleta de lixo) Desenvolvimento rapido Independente de plataforma Distribuic~ao e manutenc~ao centralizadas para applets 4.1.2 Hello World Um passeio pelo tutorial da Sun: The "Hello World"Application1 4.1.3 Java n~ao tem pointers Muito elegante, mas como e que eu implemento uma lista encadeada? Veja os arquivos: Makele2 TestFIFO.java3 FIFO.java4 FIFOElements.java5 E importante observar o uso do conceito de interface , o mecanismo de heranca multipla de Java. No caso, foi implementada a interface java.util.Enumeration , extremamente util para a escrita de loops. 4.1.4 Uma aplicac~ao real: Prj2tex Esta e uma aplicac~ao que e utilizada no CENAPAD para produc~ao de relatorios em Latex a partir de arquivos produzidos por scripts CGI, e do arquivo de senhas. Os arquivos que constituem a entrada do programa s~ao: 1 2 3 4 5 http://www.dcc.ufmg.br/javadoc/getStarted/application/index.html http://www.dcc.ufmg.br/~vado/java/TestFIFO/Makele http://www.dcc.ufmg.br/~vado/java/TestFIFO/TestFIFO.java http://www.dcc.ufmg.br/~vado/java/TestFIFO/FIFO.java http://www.dcc.ufmg.br/~vado/java/TestFIFO/FIFOElements.java ~ A PROGRAMACAO ~ PARALELA EM JAVA 68CAPITULO 4. INTRODUCAO projetos.txt6 usuarios.txt7 passwd8 Os arquivos Java s~ao: EmptyTokenStringTokenizer.java9 Projeto.java10 EntradaProjeto.java11 Projetos.java12 Login.java13 Usuario.java14 Logins.java15 Usuarios.java16 MinhaData.java17 Util.java18 Prj2tex.java19 4.2 Applets 4.2.1 Vis~ao Geral Para incluir esta applet as linhas seguintes foram inseridas no meu arquivo latex: 6 7 8 9 10 11 12 13 14 15 16 17 18 19 http://www.dcc.ufmg.br/~vado/java/Prj2Tex/projetos.txt http://www.dcc.ufmg.br/~vado/java/Prj2Tex/usuarios.txt http://www.dcc.ufmg.br/~vado/java/Prj2Tex/passwd http://www.dcc.ufmg.br/~vado/java/Prj2Tex/EmptyTokenStringTokenizer.java http://www.dcc.ufmg.br/~vado/java/Prj2Tex/Projeto.java http://www.dcc.ufmg.br/~vado/java/Prj2Tex/EntradaProjeto.java http://www.dcc.ufmg.br/~vado/java/Prj2Tex/Projetos.java http://www.dcc.ufmg.br/~vado/java/Prj2Tex/Login.java http://www.dcc.ufmg.br/~vado/java/Prj2Tex/Usuario.java http://www.dcc.ufmg.br/~vado/java/Prj2Tex/Logins.java http://www.dcc.ufmg.br/~vado/java/Prj2Tex/Usuarios.java http://www.dcc.ufmg.br/~vado/java/Prj2Tex/MinhaData.java http://www.dcc.ufmg.br/~vado/java/Prj2Tex/Util.java http://www.dcc.ufmg.br/~vado/java/Prj2Tex/Prj2tex.java 4.2. APPLETS 69 \begin{rawhtml} <APPLET CODE= Simple.class CODEBASE=//www.dcc.ufmg.br/~vado/java/Simple WIDTH=500 HEIGHT=20> </APPLET> \end{rawhtml} Uma applet reage a eventos no browser: Evento ao ser carregada, ou recarregada ao ser visitada ao deixar de ser visitada ao ser descarregada, quando o browser e terminado, ou quando se aperta reload Metodo public void init() public void start() public void stop() public void destroy() Observac~oes O metodo init() deve em geral conter o codigo que seria usado em um construtor Applets em geral n~ao t^em construtores, pois o ambiente de execuc~ao so esta completo quando init() e chamado O metodo start() em geral e quem faz o trabalho da applet, ou que dispara threads para faz^e-lo O metodo stop() suspende a execuca~o da applet, de forma a liberar os recursos utilizados enquanto o usuario n~ao v^e a applet. O metodo destroy() e muitas vezes desnecessario, pois stop() ja faz todo o trabalho de liberac~ao de recursos. O metodo paint(Graphics g) A denic~ao da apar^encia de uma applet e feita atraves do metodo paint(Graphics g). O browser e quem chama paint, passando como par^ametro uma inst^ancia da classe Graphics, que a applet usa para se desenhar. No caso, o metodo usado e: public void paint(Graphics g) { //Draw a Rectangle around the applet's display area. g.drawRect(0, 0, getSize().width - 1, ~ A PROGRAMACAO ~ PARALELA EM JAVA 70CAPITULO 4. INTRODUCAO getSize().height - 1); //Draw the current string inside the rectangle. g.drawString(buffer.toString(), 5, 15); } A applet pode induzir o browser a chamar o metodo paint, como em void addItem(String newWord) { System.out.println(newWord); buffer.append(newWord); repaint(); } 4.3 Eventos Eventos s~ao o conceito essencial para a construc~ao de interfaces gracas. Vamos aqui examinar o modelo de eventos da AWT 1.120 . Eventos s~ao gerados por fontes de eventos: menus, bot~oes, etc. Um ou mais listeners podem se registrar e ser noticados da ocorr^encia de eventos de um determinado tipo oriundos de uma fonte particular. Tratadores de eventos podem ser de qualquer classe, desde que implementem a interface listener apropriada. Em todo programa com tratadores de eventos trechos de codigo similares aos seguintes est~ao presentes: Na classe do tratador de eventos: public class MyClass implements ActionListener { O codigo que registra o listener: someComponent.addActionListener(instanceOfMyClass); O codigo do listener que reage ao evento: public void actionPerformed(ActionEvent e) { ...//code that reacts to the action... } 20 http://java.sun.com/docs/books/tutorial/ui/components/eventintro.html 4.4. THREADS 71 Beeper O codigo desta applet esta aqui21 . Comentarios: uma applet e um container , e como tal pode receber componentes atraves do metodo add a disposic~ao dos componentes em um container e controlada por um layout manager ; no caso, BorderLayout cada layout manager tem a sua losoa de arranjo dos componentes MultiListener Este e um exemplo onde dois listeners atendem aos eventos de um mesmo componente. Codigo: MultiListener.java22 MouseEventDemo Codigo: MouseEventDemo.java23 BlankArea.java24 4.4 Threads Aqui usei o seguinte codigo para incluir a applet: \begin{rawhtml} <CENTER> <APPLET CODE= CardReaderApplet.class ARCHIVE="CardReaderApplet.zip,progpar.zip" WIDTH=500 HEIGHT=400> </APPLET> 21 22 23 24 http://www.dcc.ufmg.br/~vado/java/Beeper/Beeper.java http://www.dcc.ufmg.br/~vado/java/MultiListener/MultiListener.java http://www.dcc.ufmg.br/~vado/java/MouseEventDemo/MouseEventDemo.java http://www.dcc.ufmg.br/~vado/java/MouseEventDemo/BlankArea.java ~ A PROGRAMACAO ~ PARALELA EM JAVA 72CAPITULO 4. INTRODUCAO </CENTER> \end{rawhtml} ilustrando outra maneira de indicar a fonte dos arquivos .class. Os arquivos .zip foram produzidos atrav es do comando jar cvf file.zip *.class *.gif no diretorio onde se encontram os arquivos .class e .gif. Os .zip resultantes s~ao depois movidos para o mesmo diretorio onde se encontram as paginas html. Os seguintes arquivos foram utilizados nesta applet: CardReader.java25 UserProgram.java26 Buer.java27 CardReaderApplet.java28 : Observac~oes CardReaderApplet.java As threads s~ao criadas pelos comandos: cardReaderThread = new Thread(new CardReader(cardReaderPanel, buffer, MAXCARDS)); userProgramThread = new Thread(new UserProgram(userProgramPanel,buffer,MAXCARDS)); usando o construtor Thread(Runnable runnable) que faz uso da interface Runnable, que exige que a classe implemente o metodo: public void run() : Observac~oes Buffer.java O ponto chave esta no uso de synchronized em metodos que s~ao executados com exclus~ao mutua Todo metodo synchronized de um objeto realiza, no incio de sua execuc~ao, um lock em uma chave implcita associada ao objeto, e ao terminar realiza um unlock sobre esta mesma chave; 25 26 27 28 http://www.dcc.ufmg.br/~vado/java/CardReaderApplet/CardReader.java http://www.dcc.ufmg.br/~vado/java/CardReaderApplet/UserProgram.java http://www.dcc.ufmg.br/~vado/java/CardReaderApplet/Buer.java http://www.dcc.ufmg.br/~vado/java/CardReaderApplet/CardReaderApplet.java 4.4. THREADS 73 Existe tambem uma unica cama associada ao objeto; o metodo wait() libera a chave do objeto, e coloca o processo que o executa dormindo nesta cama; Um processo que executa wait pode ser despertado { pela execuc~ao de notify() por um outro processo, ou { pela ocorr^encia de uma excec~ao (que tambem pode provocada por outro processo) A possibilidade de ocorr^encia da excec~ao forca o uso de try e catch , , : Observac~oes Consumer.java Producer.java Queue.java Producer e Consumer t^em o metodo run(), e implementam a interface Runnable Ao serem criados, recebem uma area de texto Atrasos rand^omicos s~ao impostos pelo metodo sleep ; A classe Queue e uma PE SSIMA programac~ao de uma la em Java; a biblioteca jgl (veja nos links) tem otimos exemplos de las e de outras estruturas de dados. Applet: leitores e escritores, soluc~ao injusta Os seguintes arquivos foram usados na confec~ao desta applet: Makele29 RWApplet.java30 RWScheduler.java31 Reader.java32 Writer.java33 29 30 31 32 33 http://www.dcc.ufmg.br/~vado/java/RWApplet/Makele http://www.dcc.ufmg.br/~vado/java/RWApplet/RWApplet.java http://www.dcc.ufmg.br/~vado/java/RWApplet/RWScheduler.java http://www.dcc.ufmg.br/~vado/java/RWApplet/Reader.java http://www.dcc.ufmg.br/~vado/java/RWApplet/Writer.java ~ A PROGRAMACAO ~ PARALELA EM JAVA 74CAPITULO 4. INTRODUCAO RWApplet.java, Reader.java, Writer.java, RWScheduler.java Esta applet cria threads pertencendo a dois ThreadGroups, de leitoras e escritoras; Quando o bot~ao start e apertado, as threads s~ao disparadas; Quando o bot~ao stop e apertado, a applet termina (aqui certamente ela pode ser melhorada) Os processos leitores e escritores executam um loop (quase) eterno, com esperas forcadas por sleep para ajudar na animac~ao RWScheduler e uma implementac~ao direta do algoritmo da transpar^encia 2.4.3 Leitores e escritores: Applet para soluc~ao equ^anime Os seguintes arquivos foram usados na confec~ao desta applet: 34 35 36 37 38 39 40 41 42 Makele34 FairRWApplet.java35 RWFairScheduler.java36 TicketQueue.java37 Request.java38 TimeComparator.java39 ValuePanel.java40 Reader.java41 Writer.java42 http://www.dcc.ufmg.br/~vado/java/FairRWApplet/Makele http://www.dcc.ufmg.br/~vado/java/FairRWApplet/FairRWApplet.java http://www.dcc.ufmg.br/~vado/java/FairRWApplet/RWFairScheduler.java http://www.dcc.ufmg.br/~vado/java/FairRWApplet/TicketQueue.java http://www.dcc.ufmg.br/~vado/java/FairRWApplet/Request.java http://www.dcc.ufmg.br/~vado/java/FairRWApplet/TimeComparator.java http://www.dcc.ufmg.br/~vado/java/FairRWApplet/ValuePanel.java http://www.dcc.ufmg.br/~vado/java/FairRWApplet/Reader.java http://www.dcc.ufmg.br/~vado/java/FairRWApplet/Writer.java 4.4. THREADS 75 Para usar a biblioteca jgl a seguinte linha deve ser acrescentada ao seu .cshrc: A applet abaixo deveria ilustrar o comportamento de uma soluc~ao em Java para este problema, incluindo ainda a tecnica de aging. N~ao sei por qual raz~ao o Netscape 3.1 n~ao implementa interrupt() como o appletviewer do JDK, e a applet se bloqueia. Fontes: Makele43 Client.java44 PrinterScheduler.java45 Request.java46 SJFApplet.java47 SJFQueue.java48 SizeComparator.java49 ValuePanel.java50 R recursos: Soluc~ao Java Fontes: 43 44 45 46 47 48 49 50 51 52 53 54 55 Makele51 Client.java52 NResourcesScheduler.java53 Request.java54 NResourcesApplet.java55 http://www.dcc.ufmg.br/~vado/java/SJFApplet/Makele http://www.dcc.ufmg.br/~vado/java/SJFApplet/Client.java http://www.dcc.ufmg.br/~vado/java/SJFApplet/PrinterScheduler.java http://www.dcc.ufmg.br/~vado/java/SJFApplet/Request.java http://www.dcc.ufmg.br/~vado/java/SJFApplet/SJFApplet.java http://www.dcc.ufmg.br/~vado/java/SJFApplet/SJFQueue.java http://www.dcc.ufmg.br/~vado/java/SJFApplet/SizeComparator.java http://www.dcc.ufmg.br/~vado/java/SJFApplet/ValuePanel.java http://www.dcc.ufmg.br/~vado/java/NResourcesApplet/Makele http://www.dcc.ufmg.br/~vado/java/NResourcesApplet/Client.java http://www.dcc.ufmg.br/~vado/java/NResourcesApplet/NResourcesScheduler.java http://www.dcc.ufmg.br/~vado/java/NResourcesApplet/Request.java http://www.dcc.ufmg.br/~vado/java/NResourcesApplet/NResourcesApplet.java ~ A PROGRAMACAO ~ PARALELA EM JAVA 76CAPITULO 4. INTRODUCAO NResourcesQueue.java56 SizeComparator.java57 ValuePanel.java58 R recursos: Justicativa O processo cliente entra na la numa posic~ao dependente do instante em que chega e do numero de recursos pedidos: mais recursos, pior prioridade. Cada vez que um cliente em espera e preterido, sua prioridade aumenta, e assim todo cliente com fome chega a comer. O controle da exclus~ao mutua e feito atraves da la available . setenv CLASSPATH .:/usr/local/java/lib:/usr/local/java/lib/jgl 2 0 4.5 Conclus~oes 56 57 58 http://www.dcc.ufmg.br/~vado/java/NResourcesApplet/NResourcesQueue.java http://www.dcc.ufmg.br/~vado/java/NResourcesApplet/AgeComparator.java http://www.dcc.ufmg.br/~vado/java/NResourcesApplet/ValuePanel.java 4.6. 1O TRABALHO PRATICO 77 4.6 1o Trabalho Pratico Distribuic~ao: 19/10/98 Entrega: 09/11/98 Primeira parte : descobrir o que esta acontecendo com as minhas applets, que est~ao se redesenhando a cada vez que se volta na pagina, explicar e propor uma soluc~ao. Segunda parte : Construa applets em Java para animar os algoritmos da prova. Procure fazer uma visualizac~ao t~ao didatica quanto possvel, mas sem fazer com que a interface torne obscura a codicac~ao original do algoritmo. Utilize o mecanismo de prioridades de Java para implementar um metodo de acordar seletivamente um determinado processo. ~ A PROGRAMACAO ~ PARALELA EM JAVA 78CAPITULO 4. INTRODUCAO Cap tulo 5 Outras Linguagens e Notaco~es para Programac~ao Paralela Vamos nesta seca~o examinar diversas construc~oes e linguagens propostas para a programac~ao paralela assncrona. Algumas destas construc~oes, como as path expressions, caram em desuso; outras s~ao ainda muito utilizadas, como os semaforos. Vamos dedicar uma atenc~ao especial a linguagem CSP, pela grande inu^encia que exerceu sobre a academia e a industria. 5.1 Especicac~ao de Execuc~ao em Paralelo Cobegin / Coend Fork / Join Program P1; ... fork(P2); ... join(P2); Program P2; ... ... ... END { A execuc~ao de P2 e iniciada quando o fork em P1 e executado; { P1 e P2 executam em paralelo ate que P1 atinja o comando join , ou ate que P2 termine; 79 ~ PARA PROGRAMACAO ~ PARALELA 80CAPITULO 5. OUTRAS LINGUAGENS E NOTACOES { P1 so ultrapassa o comando join apos o termino de P2 ; { ferramenta pratica e poderosa, mas seu uso indisciplinado pode levar a programas inextricaveis; { a variante Unix e (muito) utilizada para a criac~ao de processos pesados. 5.2 Sincronizac~ao com Memoria Compartilhada A sincronizac~ao e obtida com o uso de um conjunto de operac~oes garantidamente at^omicas, que fazem parte da linguagem, ou s~ao adicionadas a um ambiente de programac~ao sequencial; Objetivos: { impedir a execuc~ao em paralelo de ac~oes conitantes, garantindo assim a denic~ao de uma sem^antica para programas paralelos; { racionalizar a utilizac~ao dos processadores. 5.2.1 Acessos Indivisveis a Memoria Regras do jogo: U nico conjunto de ac~oes at^omicas: leituras e escritas de palavras na memoria; N~ao existe possibilidade de implementac~ao de espera n~ao-ocupada; A import^ancia pratica restringe-se a implementac~ao em hardware de mecanismos primitivos de exclus~ao mutua; A import^ancia acad^emica e muito grande; Dijkstra[Dijkstra, 1965b], Knuth[Knuth, 1966] e diversos outros cientistas de calibre ja se ocuparam do problema da obtenc~ao da exclus~ao mutua nestas condic~oes; A primeira soluc~ao deve-se ao matematico holand^es Dekker, e foi publicada por Dijkstra [Dijkstra, 1965a] em 1968. Algoritmo de Dekker, simplicado por Peterson PROGRAM DekkerPeterson; VAR need: ARRAY[1..2] OF BOOLEAN; turn: 1..2; ~ COM MEMORIA 5.2. SINCRONIZACAO COMPARTILHADA 81 COBEGIN P1:: LOOP VAR need1: BOOLEAN; turn1: 1..2; /* locais ao processo P1 */ ... /* Protocolo de entrada */ < need[1] := TRUE > < turn := 2 > /* <..> eh atomico */ REPEAT < need1 := need[2] >; < turn1 := turn >; UNTIL (NOT need1) OR (turn1 = 1); /* regiao critica */ ... /* Protocolo de saida */ < need[1] := FALSE >; ... END; // P2:: LOOP /* codigo simetrico */ END; Este e possivelmente o algoritmo mais complexo deste curso; sua correc~ao n~ao deve ser aceita sem o uso de tecnicas formais. 5.2.2 Semaforos Introduzidos por Dijkstra em 1968[Dijkstra, 1965a]; S~ao inteiros n~ao negativos com as operac~oes: P(s) se s for maior do que zero, s e decrementado em uma operac~ao at^omica ; caso contrario, atrasa o processo que a executa ate que s seja maior que 0; V(s) incrementa s em uma operac~ao at^omica. e V s~ao as iniciais de Proberen e de Verhogen, palavras em holand^es que querem dizer tentar e incrementar P P e V s~a tambem chamadas wait e signal , ou up e down ; existem implementac~oes tanto com espera-ocupada como com liberac~ao do processador. ~ PARA PROGRAMACAO ~ PARALELA 82CAPITULO 5. OUTRAS LINGUAGENS E NOTACOES Semaforos em Java public class Semaphore { int value; public Semaphore(int initialValue){ value = initialValue; } public synchronized void P() { while (value <= 0 ) { try { wait(); catch(InterruptedException e){} } value--; } public synchronized void V() { p++; notify(); } } Exclus~ao Mutua com Semaforos Semaphore semaphore = new SEMAPHORE(1); COBEGIN P1:: LOOP ... semaphore.P(); /* secao critica */ semaphore.V(); ... END; // P2:: LOOP ... semaphore.P(); /* secao critica */ semaphore.V(); ... END; COEND semaforos que so assumem os valores 0 e 1 s~ao conhecidos como semaforos ~ COM MEMORIA 5.2. SINCRONIZACAO COMPARTILHADA 83 binarios ; os outros s~ao chamados de semaforos gerais , ou semaforos contadores . Produtor/Consumidor com semaforos O semaforo cheios conta as posic~oes ocupadas no buer; O semaforo vazios conta as posic~oes vazias. class Buffer{ int p=0, c=0; Semaphore cheios = new Semaphore(0); Semaphore vazios = new Semaphore(MAX); public void put(char x) { vazios.P(); buf[p] = x; p = (p + 1) % MAX; cheios.V(); } public char get() { cheios.P(); char x = Buf[c]; c := (c + 1) MOD (MAX + 1); vazios.V(); return x; }} O paralelismo e maior, pois partes distintas do buer s~ao acessadas simultaneamente; A complexidade da soluc~ao e maior. ~ PARA PROGRAMACAO ~ PARALELA 84CAPITULO 5. OUTRAS LINGUAGENS E NOTACOES Jantar dos Filosofos Provavelmente o mais famoso dos problemas classicos de sincronizac~ao: Cinco losofos est~ao em uma mesa; no centro desta mesa esta um prato de espaguete. Existem cinco garfos, dispostos na mesa de forma a que cada losofo compartilha um garfo com cada um de seus dois vizinhos Cada losofo tem um comportamento cclico: { pensa; { tem fome; { come; { volta a pensar. Um losofo faminto precisa dos dois garfos que compartilha com seus vizinhos para se servir. Uma soluc~ao deve implementar a exclus~ao mutua no uso dos garfos e impedir a formac~ao de ciclos de espera gerando deadlocks. ~ COM MEMORIA 5.2. SINCRONIZACAO COMPARTILHADA Soluc~ao com Semaforos { 1 Soluc~ao do Tanembaum ([Tanenbaum, 1987]): #define N #define LEFT #define RIGHT 5 (i-1)%N (i+1)%N int state[N]; SEMAPHORE mutex = 1; SEMAPHORE s[N]; /* todos iguais a zero inicialmente */ void philosopher(int i) { while (TRUE) { think(); take_forks(i); eat(); put_forks(i); } } Soluc~ao com Semaforos { 2 void take_forks (int i) { down(mutex); /* exclusao mutua nas variaveis de controle */ state[i] = HUNGRY; test(i); up(mutex); down(s[i]); /* exclusao mutua com vizinhos */ } void test(int i) { if (state[i] == HUNGRY && state[LEFT] != EATING && state[RIGHT] != EATING) { state[i] = EATING; up(s[i]); }; /* } void put_forks(int i) { down(mutex); /* exclusao mutua nas variaveis de controle */ state[i] = THINKING; 85 ~ PARA PROGRAMACAO ~ PARALELA 86CAPITULO 5. OUTRAS LINGUAGENS E NOTACOES test(LEFT); // notifica test(RIGHT);// os vizinhos up(mutex); } 5.2.3 Monitores Sugeridos por Hoare[Hoare, 1974] e Brinch-Hansen[Hansen, 1972] por vol- ta de 1972 como meio de encapsular numa unica estrutura sintatica todos os comandos relativos a uma mesma estrutura de dados; E a ideia de modulo associada a exclus~ao mutua implcita no uso de seus procedimentos publicos; As propostas para monitores diferem nas ferramentas oferecidas para espera por condic~oes favoraveis, e na maneira como processos suspensos s~ao reativados. Uma classe Java com todos os metodos synchronized e um monitor. Monitores: Proposta de Hoare Processos s~ao atrasados em condition variables ; Sobre uma condition variable s~ao denidas as operac~oes de e signal ; A execuc~ao de cond.signal wait por um processo P tem como efeito: { se nenhum processo estiver bloqueado em cond , P prossegue nor- malmente; { sen~ao, um dos processos bloqueados e escolhido para prosseguir, e P e atrasado ate que este processo libere o monitor; { faz parte da denic~ao que a escolha do acordado n~ao e sujeita a \starvation"; { acordadores t^em prioridade sobre os novatos na tomada do monitor. Monitores: Proposta de Brinch-Hansen Processos s~ao atrasados em variaveis do tipo queue ; Operac~oes associadas: delay e continue . A execuc~ao de continue por um processo implica na sua sada do monitor, cuja posse e transmitida ao processo acordado; ~ COM MEMORIA 5.2. SINCRONIZACAO COMPARTILHADA 87 Cada variavel do tipo queue so pode abrigar um unico processo, o que leva a um uso frequente de vetores de mentac~ao. queue s, mas simplica a imple- Inconvenientes das propostas de Hoare e Brinch-Hansen: signal ou continue deve ser um ponto de consist^encia do monitor; a reativac~ao de mutiplos processos so pode ser feita por meios indiretos, e e de difcil coordenac~ao. 5.2.4 \Path Expressions" { 1 Proposic~ao: Campbell e Habermann[Campbell and Habermann, 1974] Ideia: por um unico comando, da forma path (path list) end , pro gramar explicitamente todas as restric~oes na execuc~ao das operac~oes do modulo; N~ao existem outros meios de sincronizac~ao; O uso e vantajoso quando a especicac~ao natural de um modulo e feita em termos de historias admissveis de suas operac~oes, e desvantajoso no caso contrario; Linguagens: COSY, PATH PASCAL; Operadores: , ; n:(<path list>) [<path list>] concorr^encia sequenciamento ate n ativac~oes da <path list> numero n~ao limitado de ativac~oes \Path Expressions" { 2 path deposit , fetch end { n~ao imp~oe restric~oes nem na ordem de execuc~ao das operac~oes de deposit e fetch , nem no numero de ativac~oes; path deposit; fetch end { todo fetch deve ser precedido de deposit ; { o numero de fetch s ativos ou completos n~ao pode exceder o numero de deposit s completos; ~ PARA PROGRAMACAO ~ PARALELA 88CAPITULO 5. OUTRAS LINGUAGENS E NOTACOES path 1: { n: (deposit; fetch) end deposit (1: e fetch se alternam e s~ao mutuamente exclusivas; (deposit) ; 1: (fetch) ) end { as ativac~oes de deposit , assim como as de fetch , s~ao mutuamente exclusivas; { cada ativac~ao de fetch e precedida por um deposit completo; { a diferenca entre o numero de deposit s completos e o numero de fetch s ativos nunca e maior que n ; 1: ( [read] ; write ) end { soluc~ao para leitores e escritores. 5.2.5 Mensagens No modelo \puro", a unica interac~ao permitida e a troca de mensagens; As variaveis de um processo s~ao de acesso exclusivo; A atomicidade de operac~oes do tipo send(destination, msg) e receive(source, e garantida; Variac~oes dependem { do esquema de buerizac~ao, e { do esquema de designac~ao de destinos e fontes; A grande vantagem esta na facil utilizaca~o em ambientes distribudos; msg) Produtor/Consumidor com Mensagens Exemplo do Tanembaum ([Tanenbaum, 1987]) #define N 100 void producer (void) { char item; message m; while (TRUE) { produce_item(&item); receive(consumer, &m); /* espera por msg vazia */ build_message(&m, item); send(consumer, m); } } 5.3. CSP { COMMUNICATING SEQUENTIAL PROCESSES 89 void consumer (void) { char item; int i; message m; /* producao inicial de mensagens vazias */ for (i = 0; i < N; i++) send(producer, m); /* regime */ while (TRUE) { receive(producer, &m); extract_item(m, &item); consume_item(item); send(producer, m); } } Tem-se um maximo de N mensagens em tr^ansito. 5.2.6 Conclus~oes Existem muitas construc~oes para programac~ao paralela (nesta aula so vimos algumas; propostas importantes como Linda ou Erlang n~ao foram examinadas) Java adota monitores (classe com metodos synchronized) e fork (new Thread, start() ) Mensagens s~ao muito utilizadas, e nos ocuparemos muito delas na sec~ao8 . 5.3 CSP { Communicating Sequential Processes Pseudo-linguagem proposta por Hoare em 1978; aqui seguimos elmente o artigo Communicating Sequential Processes [Hoare, 1978] Proposta de estudo e estruturac~ao de construc~oes lingusticas para: { entrada e sada { express~ao de paralelismo { sincronizac~ao de processos Uso de comandos guardados da forma condic~ao ! ac~ao , para controle sequencial e express~ao de n~ao-determinismo; ~ PARA PROGRAMACAO ~ PARALELA 90CAPITULO 5. OUTRAS LINGUAGENS E NOTACOES Comando paralelo baseado em cobegin ... coend; processos paralelos n~ao compartilham variaveis ; A comunicac~ao entre processos concorrentes e feita por comandos simples de entrada e sada ; Comandos de entrada e sada devem se casar : o valor de sada e copiado de um processo para o outro; Um comando de entrada ou de sada e atrasado ate que o outro processo esteja pronto para a execuca~o do comando de sada ou de entrada correspondente; Comandos de entrada podem aparecer em guardas; N~ao e uma linguagem completa : faltam regras para denic~ao de dados, recursividade, etc. 5.3.1 Conceitos e Notac~oes BNF: <command> ::= <simple command> | <structured command> <simple command> ::= <null command> | <assignment command> | <input command> | <output command > <structured command> ::= <alternative command> | <repetitive command> | <parallel command> <null command> ::= skip <command list> ::= {<declaration>; |<command>;} <command> chaves indicam zero ou mais repetic~oes; um comando pode ser bem sucedido ou falhar; um comando nulo n~ao tem nenhum efeito, e nunca falha; uma lista de comandos especica execuc~ao sequencial; cada declarac~ao introduz novas variaveis, cujo escopo vai ate o m da lista. 5.3. CSP { COMMUNICATING SEQUENTIAL PROCESSES 91 Comandos Paralelos <parallel command> ::= [<process> {//<process>}] <process> ::= <process label> <command list> <process label> ::= <empty> | <identifier> :: | <identifier> (<label subscript> {,<label subscript>} ) :: <label subscript> ::= <integer constant> | <range> <integer constant> ::= <numeral> | <bound variable> <bound variable> ::= <identifier> <range> ::= <bound variable> : <lower bound>..<upper bound> <lower bound> ::= <integer constant> <upper bound> ::= <integer constant> os processos de um comando paralelo devem ser disjuntos ; ZeraX(i:1..2):: X(i) := 0 e equivalente a ZeraX(1):: X(1) := 0 // ZeraX(2):: X(2) := 0. Comandos Paralelos { Exemplos Exemplo 1: [ cardreader ? cardimage // lineprinter ! lineimage ] Obs.: \?" e a recepc~ao de uma mensagem; \!" e o envio de uma mensagem. Exemplo 2: ~ PARA PROGRAMACAO ~ PARALELA 92CAPITULO 5. OUTRAS LINGUAGENS E NOTACOES [ west:: DISASSEMBLE // X:: SQUASH // east:: ASSEMBLE ] palavras em maiusculas s~ao \macros" { listas de comandos a serem expandidas Exemplo 3: [ room :: ROOM // fork(i:0..4):: FORK // phil(i:0..4):: PHIL ] este comando paralelo dene 11 processos. O comportamento de fork(1), fork(2), etc. e especicado pela lista de comandos FORK, aonde i e utilizada para identicar cada fork(i). a variavel Comandos de Atribuic~ao - BNF <assignement command> ::= <target variable> := <expression> <expression> ::= <simple expression> | <structured expression> <structured expression> ::= <constructor>(<expression list>) <constructor> ::= <empty> | <identifier> <expression list> ::= <empty> | <expression> {, <expression>} <target variable> ::= <simple variable> | <structured target> 5.3. CSP { COMMUNICATING SEQUENTIAL PROCESSES <structured target> ::= <constructor> (<target variable list>) <target variable list> ::= <empty> | <target variable> {, <target variable> } Comandos de atribuic~ao: Exemplos Exemplo 1 x := x + 1 Exemplo 2 (x , y) := (y , x) troca x com y, usando construtores vazios; Exemplo 3 x := cons(left, right) x recebe um valor estruturado pelo construtor cons; Exemplo 4 cons(left,right) := x falha se x n~ao tem a forma cons(y,z); Exemplo 5 insert(n) := insert(2*x + 1) equivalente a n Exemplo 6 := 2*x + 1 93 ~ PARA PROGRAMACAO ~ PARALELA 94CAPITULO 5. OUTRAS LINGUAGENS E NOTACOES c := P() atribui a c um \sinal" com construtor P e sem componentes; Exemplo 7 P() := c falha se o valor de c n~ao e P(); n~ao tem nenhum efeito nos outros casos; Exemplo 8 insert(n) := has(n) falha sempre. Comandos de Entrada e de Sada: rendez-vous <input command> ::= <source> ? <target variable> <output command> ::= <destination> ! <expression> <source> ::= <process name> <destination> ::= <process name> <process name> ::= <identifier> | <identifier> (<subscripts>) <subscripts> ::= <integer expression> {,<integer expression>} ocorre comunicac~ao entre dois processos paralelos quando: 1. um comando de entrada num processo especica o outro como fonte ; 2. um comando de sada no outro processo especica o primeiro como destino ; 3. a variavel alvo do comando de entrada casa-se com o valor da express~ao de sada ; os comandos casados s~ao executados simultaneamente, e o seu efeito combinado e o de um comando de atribuic~ao da express~ao de sada para a variavel alvo . este tipo de sincronizaca~o e conhecido como rendez-vous - encontro, em franc^es. 5.3. CSP { COMMUNICATING SEQUENTIAL PROCESSES 95 Comandos de Entrada e de Sada: Exemplos Exemplo 1: a variavel cardimage e atribudo o valor de uma express~ao do mesmo tipo enviada pelo processo cardreader cardreader ? cardimage Exemplo 2: transmiss~ao de lineimage para o processo lineprinter lineprinter ! lineimage Exemplo 3 X ? (x,y) o comando de sada correspondente deve enviar uma express~ao formada por dois valores agrupados por um construtor vazio; Exemplo 4 DIV ! (3*a + b, 13) Exemplo 5 console(i) ? c Exemplo 6 console(j-1) ! "A" Exemplo 7 X(i) ? V() Exemplo 8 sem ! P() Comandos Alternativos e Repetitivos: Sintaxe <repetitive command> ::= *<alternative command> ~ PARA PROGRAMACAO ~ PARALELA 96CAPITULO 5. OUTRAS LINGUAGENS E NOTACOES <alternative command> ::= [<guarded command> {|| guarded command>}] <guarded command> ::= <guard> -> <command list> | <range list> <guard> -> <command list> <guard> ::= <guard list>| <guard list> ; <input command> | <input command> <guard list> ::= <guard element> {; <guard element>} <guard element> ::= <boolean expression> | <declaration> Comandos Alternativos e Repetitivos: Caractersticas um comando alternativo especica a execuc~ao de exatamente uma de suas clausulas; se todas as clausulas falham, o comando alternativo falha; a escolha de uma clausula dentre as habilitadas e n~ao-determinstica . um comando repetitivo especica a execuc~ao repetida do comando alternativo correspondente ate que este falhe; quando todas as guardas falham, o comando repetitivo n~ao tem efeito. um comando alternativo em que todas as guardas incluem um comando de entrada sera atrasado ate que 1. um comando de sada que se case com uma das guardas de entrada seja executado, ou 2. todas as fontes tenham terminado. Comandos Alternativos e Repetitivos: Exemplos { 1 Exemplo 1 [ x >= y -> m := x || 5.3. CSP { COMMUNICATING SEQUENTIAL PROCESSES 97 y >= x -> m := y ] Exemplo 2 i := 0; *[ i < size; content(i) <> n -> i := i + 1 ] Exemplo 3 *[ c: character; west ? c -> east ! c ] Exemplo 4 *[ (i:1..10) continue(i); console(i) ? c -> X ! (i,c); console(i) ! ack(); continue(i) := (c <> sign off) ] Comandos Alternativos e Repetitivos: Exemplos { 2 Exemplo 5 *[ n: integer; X ? insert(n) -> INSERT || X ? has(n) -> SEARCH; X ! (i < size) ] a escolha entre estas duas alternativas e determinada pelo primeiro comando de X onde um inteiro e enviado com o construtor insert ou com o construtor has; ~ PARA PROGRAMACAO ~ PARALELA 98CAPITULO 5. OUTRAS LINGUAGENS E NOTACOES Exemplo 6 *[ X ? V() -> val := val + 1 || val > 0; Y ? P() -> val := val - 1 ] 5.3.2 Exemplos de Uso Co-rotinas Co-rotinas s~ao vistas aqui essencialmente como processos servidores, que recebem demandas e executam um servico. COPY: X :: *[ c: character; west ? c -> east ! c ] SQUASH: X :: *[ c: character; west ? c -> [ c <> asterisk -> east ! c; || c = asterisk -> west ? c; [ c <> asterisk -> east ! asterisk; east ! c || c = asterisk -> east ! uparrow ] ] ] DISASSEMBLE: *[cardimage: (1..80) character; cardfile ? cardimage -> i: integer; i := 1; *[i <= 80 -> X ! cardimage(i); i := i + 1 ]; X ! space ] Co-rotinas { 2 ASSEMBLE: 5.3. CSP { COMMUNICATING SEQUENTIAL PROCESSES 99 lineimage: (1..125) character; i: integer; i := 1; *[ c: character; X ? c -> lineimage(i) := c; [ i <= 124 -> i := i + 1; || i = 125 -> lineprinter ! lineimage; i := 1 ] ] [ i = 1 -> skip || i > 1 -> *[ i <= 125 -> lineimage(i) := space; i := i + 1 ]; lineprinter ! lineimage ] Linhas de montagem: [ west:: DISASSEMBLE // X:: COPY // east:: ASSEMBLE ] ou [ west:: DISASSEMBLE // X:: SQUASH // east:: ASSEMBLE ] Subrotinas Uma subrotina pode ser implementada por uma co-rotina se todos os seus parametros s~ao passados por valor e por resultado: [ subr:: *[ client ? (value parameters) -> ... ; X ! (result parameters) ] // client:: *[ ... subr ! (arguments); ~ PARA PROGRAMACAO ~ PARALELA 100CAPITULO 5. OUTRAS LINGUAGENS E NOTACOES ... subr ? (results) ... ] ] Obs.: n~ao ha recursividade; Divis~ao com Resto [ DIV:: *[ x,y: integer; X ? (x,y) -> quot, rem: integer; quot := 0; rem := x; *[ rem >= y -> rem := rem - y; quot := quot + 1 ]; X!(quot, rem) ] // X:: USER ] Recursividade: Fatorial [ fac(i:1..limit):: *[ n: integer; fac(i-1) ? n -> [ n = 0 -> fac(i-1) ! 1 || n > 0 -> fac(i+1) ! n-1; r: integer; fac(i+1) ? r; fac(i-1) ! n*r ] ] // fac(0):: USER 5.3. CSP { COMMUNICATING SEQUENTIAL PROCESSES ] Pesquisa e Inserc~ao Sequencial [ S:: content: (0..99)integer; size: integer; size := 0; *[ n: integer; X ? has(n) -> SEARCH; X!(i < size) || X ? insert(n) -> SEARCH; [ i < size -> skip || i = size; size < 100 -> content(size) := n; size := size + 1 ] ] // X:: *[... S ! insert(n); ... S ! has(k); ... S ? b ] ] Pesquisa e Inserc~ao em Paralelo [ S(i:1..100):: *[ n: integer; S(i-1) ? has(n) -> S(0)!false || S(i-1) ? insert(n) -> *[ m: integer; S(i-1) ? has(m) -> [ m <= n -> S(0) ! (m = n) || m > n -> S(i+1) ! has(m) 101 ~ PARA PROGRAMACAO ~ PARALELA 102CAPITULO 5. OUTRAS LINGUAGENS E NOTACOES ] || S(i-1) ? insert(m) -> [ m < n -> S(i+1) ! insert(n); n := m || m = n -> skip || m > n -> S(i+1) ! insert(m) ] ] ] // X:: *[ ... S(1) ! insert(i); S(1) ! insert(j); ... S(1) ! has(k); ... [(i:1..100)S(i)?b -> skip]; ] ] Buer Limitado X:: buffer: (0..9)portion; in, out: integer; in := 0; out := 0; comment 0 <= out <= in <= out + 10 *[ in < out + 10; producer ? buffer(in MOD 10) -> in := in + 1 || out N in; consumer ? more() -> consumer ! buffer(out MOD 10); out := out + 1 ] Semaforos S:: val: integer; val := 0; *[ (i:1..100) X(i) ? V() -> val := val + 1 || (i:1..100) val > 0; X(i) ? P() -> val := val - 1 ] 5.3. CSP { COMMUNICATING SEQUENTIAL PROCESSES 103 Filosofos PHIL = *[ ... THINK; room ! enter(); fork(RIGHT) ! pickup(); fork(LEFT) ! pickup(); EAT; fork(RIGHT) ! putdown(); fork(LEFT) ! putdown(); room ! exit(); ] FORK = *[ phil(LEFT) ? pickup() -> phil(LEFT) ? putdown() || phil(RIGHT)? pickup() -> phil(RIGHT)? putdown() ] ROOM = n: integer; n := 0; *[ (i:0..4) n < 4; phil(i) ? enter() -> n := n+1 || (i:0..4) phil(i) ? exit() -> n := n-1; ] Filosofos: [ room:: ROOM // fork(i:0..4) FORK // phil(i:0..4) PHIL ] 5.3.3 Notas Esta vers~ao de CSP foi publicada em 1978, epoca em que o paralelismo efetivo era essencialmente experimental; O artigo e considerado como um dos mais inuentes da Ci^encia da Computac~ao, tendo inspirado desenvolvimentos praticos, como a linguagem OCCAM e a arquitetura dos Transputers, e teoricos, como CCS (Calculus of Communicating Systems), proposto por Milner[Milner, 1984] ~ PARA PROGRAMACAO ~ PARALELA 104CAPITULO 5. OUTRAS LINGUAGENS E NOTACOES Exerccios Exerccio 5.3.1 (Uso de Semaforos) Use semaforos para implementar lock(), unlock(), wait() e wakeup(). Parte II Formulac~ao e Prova de Propriedades de Programas Paralelos 105 Cap tulo 6 Modelos Formais para Programas Paralelos Fontes: [Keller, 1976],[Shankar, 1993] Enfoques para a analise e especicac~ao: { asserc~oes baseadas em logica temporal { calculos de Milner (CCS, CSP) Enfoque adotado: logica temporal Precursores: { Floyd [Floyd, 1967]: asserc~oes sobre programas sequenciais { Hoare [Hoare, 1969]: formalizac~ao como uma logica { Dijkstra [Dijkstra, 1975]: comandos guardados { Pnueli[Pnueli, 1979], Manna e Pnueli[Manna and Pnueli, 1981]: logica temporal { Owick e Gries[Owick and Gries, 1976]: n~ao interfer^encia Propriedades de invari^ancia e de progresso Safety (invariance) assertions : uma coisa ruim nunca acontece { se o programa termina, seus resultados est~ao corretos { o numero de lugares reservados num v^oo e menor ou igual ao total de assentos no avi~ao (sem \over-booking") { n~ao existem impasses (\deadlocks") 107 108CAPITULO 6. MODELOS FORMAIS PARA PROGRAMAS PARALELOS Progress (liveness) assertions : uma coisa boa sempre acontece { um programa sempre termina { toda demanda e satisfeita num tempo nito Motivac~ao Argumentac~ao por asserc~oes: mais do que um sistema formal de prova Linguagem conveniente para descric~ao sem ambiguidades de propriedades de sistemas concorrentes Objetivos do topico: { propiciar familiaridade com a tecnica de argumentac~ao por asserc~oes que possa ser aplicada a sistemas concorrentes e distribudos { consolidar a compreens~ao de programas paralelos, aumentando o nvel de complexidade das soluco~es estudadas 6.0.1 O Buer Compartilhado Codigo Java Simplicado class Buffer { private Queue buffer; int n; final int MAX = 10; public Buffer() { buffer = new Queue(MAX); n = 0; } public synchronized void put(char x) { while(n >= MAX) { wait(); } buffer.append(x); n++; notify(); } public synchronized char get() { while (n <= 0){ wait(); } notify(); n--; return buffer.dequeue(); } } 109 Propriedades do Buer Compartilhado A sequ^encia de caracteres consumidos e um prexo da sequ^encia de caracteres produzidos. { \Prova": todo caracter produzido e colocado no m de uma lista fo. Esta mesma lista e usada pelo consumidor para a retirada de caracteres. Todo caracter produzido e consumido ao m de um tempo nito. { \Prova": esta propriedade so se verica se o processo consumidor sempre voltar a chamar o procedimento get() a administrac,a o da la associada ao synchronized for justa Com estas hipoteses adicionais, a propriedade e obvia. Propriedades Formais Abordagem da Logica Temporal : { transformar um algoritmo em um objeto matematico formal { expressar e provar formalmente propriedades destes objetos Inconveniente : dist^ancia entre o objeto formal e o algoritmo real (a di- minuic~ao desta dist^ancia e um objetivo fundamental do projeto de linguagens de programac~ao paralela, e deve sempre fazer parte das preocupac~oes do programador) Vantagens : { express~ao e prova de propriedades sem ambiguidades { possibilidade de automaca~o da vericac~ao Objeto formal: { conjuntos de estados { transico~es de estado { espaco de computac~oes As propriedades de um algoritmo s~ao as propriedades de seu espaco de computac~oes 110CAPITULO 6. MODELOS FORMAIS PARA PROGRAMAS PARALELOS Sistemas de Transic~oes de Estados Um sistema de transic~oes T e uma tripla T = (Q; ;; q0 ) onde { Q e um conjunto de estados - conjunto dos possveis valores assumidos pelas variaveis estaticas do algoritmo { ; e um conjunto de comandos guardados { q0 2 Q e um estado inicial. Um comando guardado 2 ; e um par = (g; a), onde { g : Q ! ftrue; falseg e uma func~ao que tem como par^ametro um elemento de Q e como resultado true ou false; e uma guarda que habilita ou desabilita o comando { a : Q ! Q e uma ac~ao que transforma o estado do sistema. Todo sistema de transic~oes contem obrigatoriamente o comando guardado nop = (true; identity), que sempre pode ser executado mas n~ao altera o estado do sistema. Computaco~es de um Sistema de Transic~oes Uma computac~ao de um sistema de transic~oes T e uma sequ^encia = [q0 ; 0 ]; [q1 ; 1 ]; : : : ; [qm ; m ] em (Q;) (conjunto de sequ^encias no alfabeto formado por pares (estado, comando guardado)) tal que 1. q0 e o estado inicial de T 2. 8k; 0 k < m, se k = (g; a) ent~ao g(qk ) = true e qk+1 = a(qk ) Em outras palavras, uma computaca~o deve { comecar pelo estado inicial, e { prolongar-se atraves das ac~oes de comandos com a guarda habilitada pelo estado corrente; Buer: Estados e Estado Inicial O conjunto Q de estados e determinado pelas variaveis na seguinte tabela: Variavel consumerState producerState buer n product consumption Valores fthinking, hungry g fthinking, hungry g char int char char Valor Inicial thinking thinking 0 111 Buer: Comandos Guardados Com Guarda 1 consumerState = thinking 2 consumerState = hungry ^ n > 0 3 4 |Ac~ao |consumerState = hungry; |consumption = buer.head(); buer = buer.tail(); n = n - 1; consumerState = thinking; producerState = thinking |producerState = hungry; product = randomChar(); producerState = hungry ^ n < MAX |buer.append(product); n = n + 1; producerState = thinking; a execuc~ao dos comandos guardados se da de forma { n~ao-determinstica : qualquer comando com a guarda valida pode ser selecionado para prolongar uma computac~ao e { at^omica : a ac~ao do comando selecionado e executada completamente, sem a interfer^encia de qualquer outra ac~ao a transformac~ao do algoritmo inicial foi feita baseando-se nos pontos em que a exclus~ao mutua e levantada: { no m dos procedimentos { nos comandos wait() Buer: Uma computac~ao Exemplo de computac~ao: \Instante": 0 1 2 3 Habilitados: 1 , 3 3 4 2 , 3 Escolhido: 1 3 4 2 6.0.2 Invariantes Um predicado de estado e uma func~ao P : Q ! ftrue; falseg Um invariante de uma computac~ao e um predicado de estado que e valido em todos os seus estados; Um invariante de um sistema de transic~oes e um invariante de todas as suas computac~oes. 112CAPITULO 6. MODELOS FORMAIS PARA PROGRAMAS PARALELOS Prova de Invari^ancia Prova-se a invari^ancia de um predicado P de estado por induc~ao: { Base: provar que P e invariante para computac~oes de tamanho 1; { Induca~o: supondo que P e invariante para toda computac~ao de tamanho n, provar que P e invariante para toda computac~ao de tamanho n + 1. Em outras palavras: { Base: provar P (q0); { Induca~o: para todo comando guardado = (g; a), mostrar que 8q; q0 2 Q; q0 = a(q); P (q) ^ g(q) =) P (q0 ) (6.1) (Obs.: ^ e a notac~ao para o \e" logico, e _ para o \ou".) Ou: uma computac~ao so pode ser prolongada por um comando cuja guarda e valida, e so precisamos considerar prolongamentos de computac~oes P invariantes. Buer: invari^ancia de P 0 n MAX Claramente P (q0 ) = true, pois n = 0 inicialmente; E preciso agora varrer todos os comandos guardados, mostrando para cada um deles que se P for valido, e se a guarda do comando for valida, o estado resultante q0 tambem obedecera P . Comando Prova 1 e 3 Para estes comandos, n0 = n, e a tese de induc~ao (P (q0 )) se verica trivialmente; 2 Temos n > 0 (guarda), e 0 n MAX (hipotese de induc~ao). Como n0 = n ; 1, 0 n0 MAX . 4 Temos n < MAX e 0 n MAX . Como n0 = n + 1, 0 n0 MAX . Buer: invari^ancia de P jbufferj MAX jbufferj = 0 inicialmente; 113 Comando Prova 1 e 3 buffer0 = buffer, e a tese de induc~ao (P (q0 )) se verica trivialmente; 2 Temos jbufferj MAX . Como jbuffer0j = jbufferj ; 1, jbuffer0j MAX . 4 Temos jbufferj MAX e n < MAX . Mas jbuffer0j = jbufferj +1, e portanto poderemos ter jbuffer0j > MAX ! E agora? Invariantes indutivos e n~ao indutivos Nos sabemos que jbufferj MAX e invariante; porque n~ao foi possvel provar isto? E que jbufferj MAX e invariante, mas n~ao e indutivo , ou seja, e fraco como hipotese de induc~ao. Ao prolongar uma computac~ao, so deveramos nos preocupar com computac~oes cujo ultimo estado e atingvel. A equac~ao 6.1 considera o invariante a ser provado como indicador de atingibilidade. Em casos como este, estamos considerando que estados com jbufferj = MAX e n < MAX s~ao atingveis, o que nos sabemos que n~ao e verdade. Para provar a invari^ancia de um predicado P n~ao indutivo, prova-se a invari^ancia de um predicado R tal que 1. R =) P , e 2. R e indutivo. No caso, um predicado indutivo satisfatorio e R (jbufferj = n) ^ (0 n MAX ) E importante observar a relac~ao que a prova guarda com a concepc~ao do algoritmo: n e claramente um contador dos itens em buffer, fato esquecido na primeira tentativa, e resgatado no predicado indutivo. 6.0.3 Variaveis Auxiliares Vamos agora tentar provar que a sequ^encia de itens consumidos e sempre um prexo da sequ^encia de itens produzidos. N~ao temos como expressar esta propriedade como um predicado de estado (uma func~ao P : Q ! ftrue; falseg); 114CAPITULO 6. MODELOS FORMAIS PARA PROGRAMAS PARALELOS Existem duas soluc~oes para este problema: { enriquecer a linguagem de express~ao de propriedades do conjunto de computac~oes de um sistema de transic~oes, ou { aumentar o sistema com variaveis auxiliares [Owick and Gries, 1976], como faremos aqui; variaveis auxiliares s~ao variaveis de estado que registram alguma historia da execuc~ao, mas n~ao afetam o sistema, e portanto n~ao s~ao (necessariamente) implementadas; Um subconjunto V de variaveis de estado e auxiliar se 1. variaveis em V nunca aparecem em qualquer guarda de comando, e 2. o valor de uma variavel em V nunca afeta o valor de uma variavel fora de V Em outras palavras, as variaveis em V : { so aparecem em comandos de atribuica~o, e { quando aparecem no lado direito do comando de atribuic~ao, a variavel alvo tambem e auxiliar. Buer com variaveis auxiliares Variaveis acrescentadas: Variavel Valores Valor Inicial consumed char produced char Comandos: Com Guarda 1 consumerState = thinking 2 consumerState = hungry ^ n > 0 3 4 |Ac~ao |consumerState = hungry; |consumption = buer.head(); buer = buer.tail(); n = n - 1; consumerState = thinking; consumed.append(consumption); producerState = thinking |producerState = hungry; product = randomChar(); producerState = hungry ^ n < MAX |buer.append(product); n = n + 1; producerState = thinking; consumed.append(product); 115 Buer: P consumed produced n~ao e indutivo Claramente P e valido inicialmente. Comando Prova 1 e 3 evidente, pois consumed0 = consumed e produced0 = produced. 4 Temos consumed produced, consumed0 = consumed e produced0 = produced + product. Portanto, consumed0 produced0 . 2 Temos consumed produced, produced0 = produced mas consumed0 = consumed + buffer:head(). Problema: P n~ao e indutivo! Buer: R consumed + buffer = produced e indutivo Claramente R =) P , e R e valido inicialmente; Comando Prova 1 e 3 N~ao afetam as variaveis em R 2 n > 0, e portando buffer = a + x, onde a e um char, e x uma sequ^encia (talvez nula) de char. Sendo assim, temos consumed0 = consumed + a, buffer0 = x e portanto consumed0 + buffer0 = consumed + a + x = consumed + buffer = produced = produced0 . 4 consumed0 = consumed, buffer0 = buffer + product, e produced0 = produced + product. Portanto consumed0 + buffer0 = produced0 . 6.0.4 Atingibilidade: o mais forte invariante Dado um predicado P proposto como invariante de um sistema de transic~oes, podemos 1. Contestar a invari^ancia, exibindo uma computac~ao em que P e violado. Por exemplo, a invari^ancia de P n = 0 e contestada por qualquer computac~ao em que um item chegue a ser produzido. 116CAPITULO 6. MODELOS FORMAIS PARA PROGRAMAS PARALELOS 2. Provar a invari^ancia, encontrando um predicado indutivo R mais forte que P . Esta segunda etapa n~ao e facil, devido aos seguintes resultados[Keller, 1976]: { Seja A o predicado que e valido para todos os estados atingveis de um sistema de transic~oes T ; { Claramente A e indutivo, e e o mais forte invariante de T (qualquer predicado mais forte que A n~ao e invariante) { Portanto, se P for um invariante, existe sempre um predicado indutivo R mais forte que P , que pode ser usado na prova { Mas como encontrar este invariante indutivo? Isto n~ao e decidvel, pois seria equivalente a resolver o problema da parada da maquina de Turing. Existem heursticas para se encontrar um invariante indutivo, mas na pratica, uma boa compreens~ao do algoritmo e essencial. 6.1 Modelos Formais: Progresso Propriedades de progresso t^em a forma P leads;to Q Esta propriedade e satisfeita por um sistema de transic~oes se em toda computac~ao, toda vez que o estado satisfaz P , mais tarde (ou naquele momento) na computac~ao o sistema satisfara Q. Exemplos: { q = q0 leads;to q = qf inal: toda computac~ao termina { q = hungry leads;to q = eating: toda requisic~ao e satisfeita { jproducedj = n leads;to jconsumedj = n: a sequ^encia produzida acaba sendo consumida Problema: quanto tempo temos que esperar? Em outras palavras, quando podemos acusar uma computaca~o de violar uma propriedade P leads;to Q? { num sistema assncrono, nada obriga um comando com a guarda habilitada a ser executado; { a exist^encia de computac~oes nitas, em que uma requisic~ao foi foi feita mas n~ao foi satisfeita, n~ao deveria nos levar a diagnosticar o sistema de transic~oes como sujeito a starvation. 6.1. MODELOS FORMAIS: PROGRESSO 117 Equidade fraca e equidade forte Os problemas para a caracterizac~ao de propriedades de progresso podem ser contornados { tornando todas as computaco~es innitas , atraves da obrigato- riedade da exist^encia de um comando guardado skip = (true; identity) em todos os sistemas de transic~oes. { skip sempre pode ser escolhido para prolongar uma computac~ao nita, pois sua guarda sempre esta habilitada { a ac~ao de skip n~ao altera o estado, pois e a func~ao identidade { desta forma a toda computac~ao nita corresponde uma computac~ao innita, que e o seu prolongamento por innitos skip . o progresso e forcado, excluindo computac~oes innitas que n~ao satisfazem hipoteses de equidade sobre certos comandos guardados: Equidade fraca : para certos comandos, computac~oes em que o comando permanece com sua guarda continuamente habilitada sem ser executado s~ao excludas { Exemplo: a hipotese de equidade fraca relativa ao comando 1 = (g = consumerState = thinking, a = consumerState = thinking) signica que n~ao se admitem computac~oes em que o processo consumidor se esqueca eternamente de tentar consumir Equidade forte : para certos comandos, computac~oes em que o comando nunca e executado, apesar de ter a sua guarda habilitada e desabilitada intermitentemente, por um numero innito de vezes, s~ao excludas Exemplo: Inteiro Oscilante Exemplo: considere o sistema abaixo, cujo estado e denido por uma unica variavel i, com valor inicial i = 0 Com Guarda Ac~ao 1 i=0 i := ;1 2 i = ;1 i = 0 3 i=0 i=1 4 i=1 i=0 { Se este sistema obedece a hipotese de equidade fraca sobre 1, 2 e 3 , ent~ao ele admite computac~oes (innitas) em que 3 nunca e executado; { se 1 e 2 s~ao executados com equidade fraca, mas 3 e executado com equidade forte, ent~ao o sistema n~ao admite computac~oes em que 3 nunca e executado. 118CAPITULO 6. MODELOS FORMAIS PARA PROGRAMAS PARALELOS Sistemas de Transic~oes de Estados: alterac~oes para prova de propriedades de progresso inclus~ao obrigatoria de skip em todo sistema toda computac~ao e innita hipoteses de equidade fraca ou forte excluem as computac~oes innitas que n~ao as obedecem. 6.1.1 Tecnicas de Prova de P leads;to Q Basicamente temos dois tipos de regras de prova: { regras do tipo leads;to via um comando guardado , e { Regras de fechamento (closure rules ) que nos permitem inferir asserco~es leads;to de outras asserc~oes leads;to , como P leads;to R ^ R leads;to Q =) P leads;to Q Prova de P leads;to Q via com equidade fraca E preciso estabelecer os seguintes pontos: por hipotese, e executado com equidade fraca; P =) g, onde g e a guarda de { ( sempre esta habilitado enquanto P for valido) a execuc~ao de leva a validac~ao de Q a execuc~ao de qualquer outro comando =2 ;; = 6 , que esteja habilitado quando P for valido acarreta a validaca~o de P _ Q { ( ou bem provoca a ocorr^encia de Q, ou mantem habilitado) Com o estabelecimento destes pontos, uma computaca~o innita que viole P leads;to Q esta descartada: suponha a exist^encia de uma computac~ao em que P foi valido num instante i, e Q n~ao se verica para nenhum instante j maior ou igual a i; esta computac~ao teria a guarda de habilitada em todo instante j i, o que contradiz a hipotese de equidade fraca para 6.1. MODELOS FORMAIS: PROGRESSO 119 Prova de P leads;to Q via com equidade forte Os seguintes pontos devem ser estabelecidos: por hipotese, e executado com equidade forte; P =) g, onde g e a guarda de { ( sempre esta habilitado enquanto P for valido) a execuc~ao de leva a validac~ao de Q a execuc~ao de qualquer outro comando =2 ;; = 6 , que esteja habili- tado quando P for valido acarreta a validade de um predicado R tal que R leads;to (P _ Q) { (a execuc~ao de leva ou bem a validac~ao futura de Q, ou bem a re-habilitac~ao de ). Com o estabelecimento destes pontos, uma computac~ao innita que viole P leads;to Q esta descartada: suponha a exist^encia de uma computac~ao em que P foi valido num instante i, e Q n~ao se verica para nenhum instante j maior ou igual a i; esta computac~ao teria a guarda de habilitada por um numero ilimitado de vezes apos o instante i, mas nunca teria sido executado, o que contradiz a hipotese de equidade forte. Equidade forte e fraca: Exemplo Equidade x = 0 leads;to x = 1 x = 1 leads;to x = 0 x = 0 leads;to x = ;1 x = ;1 leads;to x = 0 { F F F F Fraca F T F T Forte T T T T 6.1.2 Produtor e Consumidor Voltando ao buer limitado: Prova de jproducedj l leads;to jconsumedj l (6.2) para qualquer inteiro positivo l. Para isto basta provar que jproducedj l ^ jconsumedj = k < l leads;to jconsumedj = k + 1 (6.3) 120CAPITULO 6. MODELOS FORMAIS PARA PROGRAMAS PARALELOS Vemos aqui um exemplo de uma regra de fechamento interessante, com o uso de uma metrica : temos l ; jconsumedj como uma metrica se a metrica for igual a 0, teremos atingido o progresso desejado; se a metrica for maior que 0, nos provamos que ela sempre diminui; uma metrica (obviamente) n~ao possui uma sequ^encia innita decrescente e maior que zero Portanto, para provar 6.2, e suciente a prova de 6.3. Buer Limitado: Progresso Vamos precisar de ter como hipotese a execuc~ao de 1 e de 2 com equidade fraca. Seja P jproducedj l ^ jconsumedj = k < l Temos P = P ^ (consumerState = hungry _ consumerState = thinking) Mas consumerState = thinking leads;to consumerState = hungry via 1 . Como jproducedj = jbufferj + jconsumedj P implica em n = jbufferj > 0, e portanto P leads;to jconsumedj = k +1 (via 2 ), como queramos demonstrar. Modelos Formais: Conclus~oes Modelos formais nos permitem discursar com precis~ao sobre as propriedades de um programa paralelo A prova de uma propriedade nos leva a um conhecimento detalhado de um algoritmo Modelos formais devem ser usados com sensatez: propriedades obvias devem ser formuladas e tomadas como verdadeiras, e propriedades n~ao obvias merecem o esforco da prova. Ter em mente o conjunto de propriedades expressas formalmente ao criar um algoritmo e uma enorme vantagem, como veremos mais tarde. Cap tulo 7 Transaco~es e o Protocolo 2-fases Fonte: The Notions of Consistency and Predicate Locks in a Database System K. P. Eswaran, J. N. Gray, R. A. Lorie, I. L. Traiger, CACM, 1976 Aspectos importantssimos de sistemas de transac~oes, como deadlocks, recuperac~ao de transac~oes e toler^ancia a falhas n~ao s~ao examinados aqui . Um tratamento formal mais completo encontra-se nos captulos 2 e 3 do livro \Concurrency Control and Recovery in Database Systems"[Bernstein et al., 1987] Para saber tudo sobre sistemas de transac~oes a refer^encia pratica e teorica e o livro \Transaction Processing: Concepts and Techniques"[Gray and Reuter, 1993b] Introduc~ao Banco de Dados: conjunto de entidades Consist^encia expressa por um conjunto de invariantes : A e um contador para B C e um contador para as celulas livres em D E e um ndice para F Estes invariantes s~ao chamados de restrico~es de consist^encia do banco de dados Um estado do BD e consistente se satisfaz as restric~oes de consist^encia 121 ~ E O PROTOCOLO 2-FASES CAPITULO 7. TRANSACOES 122 Transac~oes Ac~oes modicam o estado Sobre cada entidade, locks e unlocks podem ser usados para evitar conitos Pode ser necessario violar temporariamente a consist^encia do sistema Transac~oes s~ao agrupamentos de aco~es em unidades de consist^encia : Cada transac~ao, quando executada isoladamente, transforma um estado consistente em outro estado consistente E este o acordo entre o controle de concorr^encia do banco de dados e o programador de transac~oes, que pode pensar que esta sozinho no sistema tem que garantir a consist^encia da sua transaca~o em condic~oes de isolamento maximizar o paralelismo, dando a cada transac~ao uma vis~ao consistente do sistema Problema: Escalonamentos Um escalonamento e uma sequ^encia de aco~es de um conjunto de transac~oes Mais uma vez: consist^encia 6= determinismo 7.0.1 Travamentos Consideremos as transac~oes: T1 A := A + 100; B := B + 100; T2 A := 2*A; B := 2*B; e suponhamos que a unica restrica~o de consist^encia seja A = B. T1 e T2 s~ao individualmente consistentes, mas sua execuc~ao simult^anea pode provocar os seguintes efeitos: Inconsist^encia temporaria : entre o primeiro e o segundo comando de cada transac~ao, A=B n~ao e obedecida Conitos em uma das entidades 123 Conitos resultantes de um escalonamento em que apos o termino das transac~oes o estado do sistema resulte inconsistente A inconsist^encia temporaria e inerente ao sistema, e n~ao provoca inconvenientes Conitos t^em que ser evitados, pois levam a estados inconsistentes Formas de se evitar conitos: exclus~ao mutua global : penalidade inaceitavel no desempenho particionamento em blocos com exclus~ao mutua, com cada transac~ao restrita a um bloco: complica o acordo com o programador de transac~oes, e e inaceitavel em muitos casos soluc~ao adotada: travamento de entidades individuais com controle de concorr^encia forcando a consist^encia dos escalonamentos Travamento em duas fases O principal resultado deste artigo, e um dos mais importantes resultados do paralelismo assncrono e o 2-phase locking protocol : Se todas as transac~oes s~ao divididas em uma primeira fase de locks, e em outra exclusivamente de unlocks, ent~ao todos os escalonamentos s~ao consistentes Transac~oes: Denic~ao Formal Uma transac~ao T e uma sequ^encia [(T; a1; e1 ); : : : ; (T; an ; en)] onde ai e uma ac~ao ei e uma entidade Ac~oes s~ao sempre operac~oes de escrita, ou locks, ou unlocks Ac~oes de leitura s~ao omitidas para simplicar 124 ~ E O PROTOCOLO 2-FASES CAPITULO 7. TRANSACOES Transac~oes bem formadas Uma transac~ao e bem formada se em uma transac~ao, uma entidade e so e travada se estiver destravada e vice-versa ao terminar, uma transac~ao tera destravado todas as entidades que travou toda ac~ao sobre uma entidade e se faz com e travada, a n~ao ser que seja um lock Exemplo de transac~oes bem formadas: T1 lock A A := A + 100; unlock A lock B B := B + 100 unlock B T2 lock A A := A*2 lock B unlock A B := B * 2 unlock B Escalonamentos Um escalonamento S e uma sequ^encia obtida intercalando-se as ac~oes de T1 ; : : : ; Tn Um escalonamento e legal se nenhuma transac~ao trava uma entidade ja travada por outra Um escalonamento serial n~ao intercala ac~oes de transac~oes distintas: S = Ti1 ; Ti2 ; : : : ; Tin Um escalonamento S induz uma relac~ao de depend^encia entre transac~oes que usem uma mesma entidade e: (T1 ; e; T2) 2 DEP (S ) se e somente se S = [: : : ; (T1 ; a1 ; e); : : : ; (T2 ; a2 ; e); : : :] e se n~ao existe nenhuma ac~ao (Tj ; aj ; e) escalonada no intervalo (T1 ; a1 ; e); : : : ; (T2 ; a2 ; e). ( a sada de T1 e a entrada de T2 ; por este motivo a relac~ao DEP e chamada por alguns autores de reads-from ). Observac~ao: se T1 e T2 s~ao bem formadas, a1 deve ser unlock e, e a2 deve ser lock e 125 Aciclicidade da relac~ao de depend^encia Para os escalonamentos seriais, a relac~ao de depend^encia deve ser acclica: (T1 ; e1 ; T2 ) 2 DEP =)6 9e2 ; (T2 ; e2 ; T1 ) 2 DEP Os escalonamentos S1 e S2 s~ao equivalentes se DEP (S1 ) = DEP (S2 ) Porque? mesmo estado inicial mesma vis~ao do banco de dados para cada uma das transac~oes Um escalonamento S e consistente se for equivalente a algum escalonamento serial Exemplos de escalonamentos S1 A := A + 100 A := 2*A B := B + 100 B := 2*B DEP (S1 ) ( T1 , A, T2 ) ( T1 , B, T2 ) S2 A := A + 100 A := 2*A B := 2*B B := B + 100 DEP (S2 ) ( T1 , A, T2 ) ( T2 , B, T1 ) S3 A := A + 100 B := B + 100 A := 2*A B := 2*B DEP (S3 ) ( T1 , A, T2 ) ( T1 , B, T2 ) Transac~oes duas-fases Na primeira fase ( expans~ao ) s~ao permitidos locks Na segunda fase ( contrac~ao ), que se inicia com o primeiro unlock, n~ao s~ao permitidos locks. Se um sistema possui uma transac~ao que n~ao e duas-fases, e possvel acrescentar a este sistema uma transac~ao duas-fases que permite a construc~ao de um escalonamento legal e inconsistente. O escalonamento S abaixo e legal e inconsistente: 126 ~ E O PROTOCOLO 2-FASES CAPITULO 7. TRANSACOES unlock e1 lock e1 write e1 lock e2 write e2 unlock e1 unlock e2 lock e2 pois tanto (T1 ; e1 ; T2 ) inDEP (S ) como T2 ; e2 ; T1) 2 DEP (S ) Teorema das transac~oes duas-fases Se toda transac~ao em um sistema e bem formada e duas-fases, ent~ao qualquer escalonamento legal S e consistente Prova em duas partes: 1. mostrar que a relac~ao de depend^encia e sempre acclica 2. construir um escalonamento serial equivalente Aciclicidade da relac~ao de depend^encia Consideremos a relac~ao denida por Ti Tj se e somente se 9e; (Ti ; e; Tj ) 2 DEP (S ) onde S e um escalonamento legal qualquer. Vamos provar que e acclica. O escalonamento S dene tambem para cada transac~ao Ti o inteiro shrink(Ti ), que e a posic~ao em S do primeiro unlock de Ti . Proposic~ao: T1 T2 =) shrink(T1 ) < shrink(T2 ) Prova: se (T1 ; e; T2) 2 DEP (S ), ent~ao S = [: : : ; (T1 ; a1 ; e); : : : ; (T2 ; a2 ; e); : : :] e se n~ao existe nenhuma ac~ao (Tj ; aj ; e) escalonada no intervalo (T1 ; a1 ; e); : : : ; (T2 ; a2 ; e). Portanto, a1 e unlock e, e a2 e lock e, e shrink(T1 ) < shrink(T2 ). De onde e uma relac~ao acclica. Escalonamento Serial Equivalente A func~ao shrink ordena totalmente as transac~oes de um sistema duas-fases Esta ordenac~ao e usada para construc~ao do escalonamento serial equivalente. Qualquer outra ordem total compatvel com tambem serviria ~ 7.1. CONCLUSOES 127 7.1 Conclus~oes Se as transac~oes T1 ; : : : ; Tn s~ao bem formadas e duas-fases, ent~ao qualquer escalonamento legal e consistente Se uma transac~ao T n~ao e bem-formada ou duas-fases, existe uma transac~ao T 0 e um escalonamento legal de T e T 0 que n~ao e consistente Existem sistemas de transac~oes que n~ao s~ao duas-fases e que so possuem escalonamentos consistentes: T1 lock A A := A + 100; unlock A lock B B := B + 100 unlock B T2 lock A A := 2 * A unlock A lock C C := C * 2 unlock C Modelos formais servem para o estudo e prova de resultados gerais da mais alta import^ancia pratica 128 ~ E O PROTOCOLO 2-FASES CAPITULO 7. TRANSACOES Parte III Programac~ao Distribuda 129 Cap tulo 8 Programac~ao Distribuda Sistemas separados sicamente, com diculdades de comunicac~ao Diversos modelos de programac~ao, dependendo do nvel em que se trabalha Modelo basico: troca de mensagens (message passing systems ) { N~ao existe memoria compartilhada { N~ao existe relogio comum { A interac~ao se faz exclusivamente por trocas de mensagens, que t^em tempos de transmiss~ao arbitrariamente longos { Perdas de mensagens e invers~oes na ordem de entrega algumas vezes devem ser consideradas. Outros modelos: chamada remota de procedimentos, objetos distribudos, etc. Sistemas com Trocas de Mensagens Principal problema: cada stio deve tomar decis~oes baseado em dados locais; O estado dos dados locais e uma func~ao dos dados iniciais e das mensagens recebidas Uma mensagem traz informac~ao do passado, e cabe ao programador forcar comportamentos que impliquem na validade da informac~ao trazida pela mensagem. A aus^encia de memoria comum probe comandos em que a guarda ou a ac~ao envolva variaveis de dois ou mais stios 131 ~ DISTRIBUIDA CAPITULO 8. PROGRAMACAO 132 A aus^encia de relogio comum impede a programac~ao de mudancas sincronizadas. Enfoque: compartilhamento de recursos. Porque? enunciado simples diversidade de soluc~oes propriedades exigidas incluem invari^ancia e progresso e principalmente gosto pessoal do professor. . . 8.1 The Drinking Philosophers Problem Fonte: \The Drinking Philosophers Problem", Chandy e Misra, 1984[Chandy and Misra, 1984] Introduc~ao Problema: resoluc~ao equ^anime de conitos em sistemas distribudos[Chandy and Misra, 1984] Conitos podem ser resolvidos por uma propriedade que: { sempre consiga decidir o vencedor de um conito ( distinguibilidade ) { nunca deixe alguem morrer de fome ( equidade ) Soluc~oes conhecidas: prioridades (tickets) e selec~ao probabilstica (veremos mais tarde). Proposta: uso da localizac~ao de recursos virtuais como propriedade de resoluc~ao O Problema dos Filosofos Sedentos : paradigma para resoluc~ao de conitos em sistemas distribudos Generalizac~ao do Jantar dos Filosofos proposto por Dijkstra Grafos de Conitos e de Preced^encias Grafo de conitos G: { nos: processos { n~ao dirigido { aresta (u; v) existe se for possvel um conito entre u e v 8.1. THE DRINKING PHILOSOPHERS PROBLEM 133 Grafo de preced^encias H : { e denido por uma func~ao que sempre atribua preced^encias distintas a pares de processos em conito { id^entico a G, mas dirigido { u ! v em H se u tem preced^encia sobre v Exemplos de Grafos de Conito e de Preced^encias 1 3 2 4 6 5 Grafo de Conflitos 1 1 3 2 3 2 4 4 6 5 Grafo de Precedencias Aciclico 6 5 Grafo de Precedencias com Ciclo Profundidade de u em um grafo acclico: maior numero de arestas em uma rota que sai de um no sem precedentes e vai ate u Se H for acclico, a profundidade sempre vai diferenciar de todos os concorrentes potenciais ( vizinhos em G) Porque? Se H for cclico, nem todos os conitos poder~ao ser resolvidos Modicando o Grafo de Preced^encias H so pode ser modicado por mudancas nas direc~oes das arestas Proposta: implementac~ao distribuda de H que garanta que 1. H permanece acclico 2. H seja modicado de maneira a garantir que todo processo em conito chegue a profundidade 0 num tempo nito 3. toda mudanca em H possa ser feita localmente ~ DISTRIBUIDA CAPITULO 8. PROGRAMACAO 134 Regra da Aciclicidade 1 1 3 2 3 2 4 4 6 5 6 1 5 1 3 2 3 2 4 6 4 5 6 5 Regra da Aciclicidade : se um grafo de preced^encias e acclico, ele permanece acclico se todas as arestas incidentes em um no s~ao dirigidas a ^ele numa ac~ao at^omica Prova: suponha que o grafo resultante de uma operac~ao como esta sobre o no i contenha um ciclo. Se este ciclo n~ao contem i, ele ja existia, o que contraria a nossa hipotese Se este ciclo contem i estamos tambem diante de uma impossibilidade, pois todas as arestas incidentes em i est~ao dirigidas a ele. Regra da Equidade Regra da Equidade : Todo processo vencedor de um conito deve dar preced^encia a todos os seus vizinhos dentro de um tempo nito apos a vitoria Problema: implementac~ao distribuda de H : decis~oes devem ser tomadas com base em dados locais 8.1. THE DRINKING PHILOSOPHERS PROBLEM 135 O Problema dos Filosofos Sedentos Cada losofo (processo) esta em um vertice de um grafo G n~ao dirigido Pode estar em um de tr^es estados: 1. tranquilo 2. sedento 3. bebendo A cada aresta de G esta associada uma garrafa disputada por dois losofos Um losofo so bebe de garrafas associadas a arestas incidentes Um losofo tranquilo pode ser acometido de sede por um coquetel (conjunto de garrafas) especco Um losofo pode desejar diferentes coqueteis para cada drinque De posse das garrafas que comp~oem o coquetel desejado, o losofo pode comecar a beber Apos um tempo nito bebendo, o losofo volta a car tranquilo Um losofo permanece tranquilo por um tempo arbitrario 1 Anis 3 2 Martini 4 Whiskey Campari Caipira 6 5 Grafo de Conflitos Condic~oes para uma soluc~ao Equidade : nenhum losofo morre de sede Simetria : mesmos direitos e deveres Concorr^encia : drinques simult^aneos de garrafas distintas n~ao devem ser proibidos Economia : um losofo troca um numero nito de mensagens para uma transic~ao de estado Limites : o numero e o tamanho das mensagens em tr^ansito e limitado ~ DISTRIBUIDA CAPITULO 8. PROGRAMACAO 136 O Problema dos Filosofos Famintos Caso especial em que o coquetel desejado e sempre composto por todas as garrafas acessveis Estados de um losofo: pensando, faminto, comendo Garrafas passam a ser chamadas de garfos Dois vizinhos nunca comem simultaneamente 1 3 2 4 6 5 Grafo de Conflitos Se o grafo de conitos for completo, temos a exclus~ao mutua global Soluc~ao \higi^enica": ideia basica Soluc~ao \higi^enica": { um garfo esta sujo ou limpo { garfos (so) sujam-se ao serem usados para comer { garfos (so) s~ao limpos ao serem enviados { um losofo comendo so responde a requisic~oes ao acabar de comer { um losofo faminto: atende a requisic~oes por garfos sujos adia respostas a requisic~oes por garfos limpos Base da soluc~ao: representac~ao distribuda do grafo H u precede v em H (u ! v) sse 1. u possui fuv , o garfo compartilhado com v, limpo, ou 2. v possui fuv , sujo, ou 8.1. THE DRINKING PHILOSOPHERS PROBLEM 137 3. fuv esta em tr^ansito indo de v para u, A situac~ao inicial deve garantir que o grafo formado por estas regras e acclico A Soluc~ao Higi^enica: Preliminares Entre u e v circula tambem um request token So o possuidor de um token pode envia-lo Se um processo possui o garfo e o token que compartilha com um vizinho, e porque existe uma requisic~ao pendente deste vizinho forku (f ) u detem o garfo f reqf ( f ) u detem o token associado ao garfo f Variaveis: dirtyuu (f ) u detem f , sujo thinkingu=hungryu=eatingu u esta thinking=hungry=eating A Soluc~ao Higi^enica: Comandos Guardados R1 Requisitando um garfo f : hungry, reqf (f ), :fork(f ) ! send request token for fork f ; reqf (f ) := false R2 Entregando um garfo f : :eating, reqf (f ), dirty(f ) ! send fork f ; dirty(f ) := false; fork(f ) := false R3 Recebendo um request token por f : receive(ReqToken(f )) ! reqf (f ) := true R4 Recebendo um garfo f : receive(fork(f )) ! fork(f ) := true R5 Comecando a comer : hungry, all forks ! hungry := false; eating := true; dirty(f ) := true for all f R6 Acabando de comer : eating ! eating := false; thinking := true; ~ DISTRIBUIDA CAPITULO 8. PROGRAMACAO 138 Observac~oes R1 corresponde a n comandos guardados, sendo um para cada processo (losofo) Os ndices dos processos s~ao omitidos para simplicar a notac~ao A Soluc~ao Higi^enica: Condic~oes Iniciais 1. Todos os garfos est~ao sujos : 8f; dirtyu(f ) _ dirtyv (f ) 2. Todo garfo f e o request token associado est~ao com vizinhos distintos forku (f ); reqfv (f ); :forkv (f ); :reqfu (f ) _ forkv (f ); reqfu (f ); :forku (f ); :reqfv (f ) 3. Os garfos est~ao dispostos de maneira a fazer H acclico Filosofos Sedentos: o Grafo de Preced^encias A soluc~ao higi^enica e uma implementaca~o distribuda do grafo de preced^encias Olhando-se apenas o estado de um losofo u, so e possvel determinar a relac~ao de preced^encia entre u e um vizinho v se u detem fuv Garfos s~ao utilizados como artefatos para implementac~ao de H Estado de um losofo: par (estado faminto, estado sedento) Transic~oes de um losofo faminto: D1 Um losofo com sede e pensando ca com fome D2 Um losofo comendo mas sem sede passa a pensar Regra para resoluc~ao de conitos: D3 O losofo u envia uma garrafa em resposta a uma requisic~ao de v sse { u n~ao precisa da garrafa, ou { u n~ao esta bebendo e n~ao detem fuv A notar que a decis~ao do envio da garrafa e baseada na percepc~ao local de preced^encia ~ MUTUA 8.2. EXCLUSAO DISTRIBUIDA 139 Algoritmo para Filosofos Sedentos R1 Requisitando uma garrafa : thirsty, need(b), reqb(b), :bot(b) ! send request token for bottle b; reqb(b) := false R2 Enviando uma garrafa : reqb(b), bot(b), :[need(b) ^ (drinking _ fork(f ))] ! send bottle b; bot(b) := false R3 Recebendo uma requisic~ao por garrafa : receive(request(b)) ! reqb(b) := true R4 Recebendo uma garrafa : receive(bottle(b)) ! bot(b) := true Conclus~ao Um problema de sincronizac~ao complexo, com a diculdade adicional dos ambientes distribudos, foi resolvido porque: estrutura de prova de correc~ao foi concebida junto com o algoritmo 8.2 Exclus~ao Mutua Distribuda Ambiente { diversos stios com memoria e processamento local; { comunicac~ao exclusivamente por trocas de mensagens { sem memoria e sem relogio comuns; Problema { atingir a exclus~ao mutua Restric~oes { aus^encia de bloqueios { aus^encia de inanic~ao { simetria: mesmos direitos, mesmos deveres Porque exclus~ao mutua? { Enunciado simples 140 ~ DISTRIBUIDA CAPITULO 8. PROGRAMACAO { Diversas soluco~es exemplicando tecnicas de programac~ao { Alguma utilizaca~o pratica (pouca) 8.2.1 Relogios Logicos e Timestamps Relogios logicos [Lamport, 1978] s~ao inteiros monotonamente crescen tes conservados pelos stios; usados para etiquetar mensagens e para estabelecer prioridades ; o valor da etiqueta de uma mensagem { seu timestamp { e o valor corrente do relogio logico no momento do envio; a prioridade de uma requisic~ao e o valor corrente do relogio logico no momento da operac~ao; empates s~ao costumeiramente resolvidos pelo numero unico do stio; podem ser incrementados a qualquer momento; devem ser atualizados para permanecerem maiores que a etiqueta de qualquer mensagem recebida ; devem ser incrementados entre certos eventos; Algoritmo de Lamport (1978) o meio de comunicac~ao conserva a ordem de envio na entrega das mensagens; cada stio possui uma la de requisic~oes ; inicialmente o stio 0 possui o recurso crtico; todas as las de requisic~oes possuem a mensagem < T;1 : 0; request > onde T;1 e menor que o valor inicial de qualquer relogio logico; O algoritmo e denido pelas seguintes regras: 1. Requisic~ao : o stio i manda a mensagem < Ti : i; request > para todos os outros stios, e tambem coloca esta mensagem em sua la de requisic~oes; 2. Recepc~ao de < Tj : j; request > : a requisica~o e colocada na la, e uma mensagem < Ti : i; ack > e enviada ao stio j ; 3. Liberac~ao : o stio i remove toda mensagem < Ti : i; request > de sua propria la de requisic~oes, e manda < Ti : i; release > para todos os outros stios; ~ MUTUA 8.2. EXCLUSAO DISTRIBUIDA 141 4. Recepc~ao de < Tj : j; release > : o stio i remove toda mensagem < Tj : j; request > de sua la de requisic~oes; 5. Incio de sec~ao crtica : o stio i pode comecar a utilizar o recurso quando: (a) a requisic~ao mais prioritaria em sua la e da forma < Ti : i; request >, e (b) o stio i ja recebeu mensagens com prioridade menor que (Ti ; i) de todos os outros stios; Algoritmo de Lamport - Seguranca \Prova" de exclus~ao mutua: suponha a exist^encia de uma computac~ao em que dois stios i e j est~ao em suas sec~oes crticas simult^aneamente; sem perda de generalidade, suponhamos que (Ti : i) tenha prioridade sobre (Tj : j ); na operac~ao de requisic~ao, i enviou < Ti : i; request > para j ; no momento da entrada de j em sua sec~ao crtica, { se j n~ao tinha recebido este request, para entrar em sua sec~ao crtica j teria recebido outra mensagem de i, com prioridade menor que (Tj : j ), o que e impossvel pois o meio de transmiss~ao conserva a ordem de envio, e as mensagens enviadas por i recebem timestamps crescentes; { se j ja tinha recebido este request, para entrar em sua sec~ao crtica j teria que t^e-lo eliminado de sua la, o que so aconteceria com a chegada do < release > correspondente, o que contradiz a hipotese de simultaneidade das sec~oes crticas; portanto esta computaca~o n~ao pode existir Algoritmo de Lamport - Progresso Suponhamos a exist^encia de uma computac~ao innita em que o stio i requisitou a sec~ao crtica num instante Ti , e nunca conseguiu. Se i n~ao conseguiu, e porque existe um stio j que nesta computac~ao: { enviou um < Tj : j; request > para i, com prioridade superior a < Ti : i; request >, e que nunca saiu da la de i, { isto nao aconteceu, mas j nunca enviou uma mensagem para i com prioridade inferior a < Ti : i; request > ~ DISTRIBUIDA CAPITULO 8. PROGRAMACAO 142 No segundo caso, j teria recebido o < Ti : i; request >, e n~ao teria enviado para i o < Tj : j; ack > correspondente, o que n~ao pode acontecer; No primeiro caso, j n~ao enviou < Tj0 : j; release >, e como todo stio entrega o recurso crtico apos um tempo nito, o unico motivo para j ter morrido de fome e a exist^encia de j 0 , mais prioritario que j , que tambem morreu de fome, por culpa de j 00 , mais prioritario que j 0 , e assim por diante. Como n~ao existe uma sequ^encia innita como esta, a computac~ao com inanic~ao n~ao pode existir, c.q.d. Exemplo 0 0 1 1 0 1 1 1 1 2 2 2 2 2 2 3 3 3 4 4 4 ~ MUTUA 8.2. EXCLUSAO DISTRIBUIDA 143 Algoritmo de Lamport: Complexidade Em cada sec~ao crtica, temos: n ; 1 requests n ; 1 acks n ; 1 releases com um total de 3(n ; 1) mensagens por sec~ao crtica 8.2.2 Algoritmo de Ricart e Agrawala (1981) Evoluc~ao do algoritmo de Lamport, em que releases so s~ao enviados quando o no requisitante tem prioridade Fonte: \An optimal algorithm for mutual exclusion in computer networks"[Ricart and Agrawala, 1981]. CONST i = /* this node unique number */ VAR OSN, /* Our Sequence Number */ HSN, /* Highest Sequence Number */ ORC /* Outstanding Reply Count */ : CARDINAL; Hungry: BOOLEAN; Deferred: ARRAY[1..n] OF BOOLEAN; PROCEDURE Request(); BEGIN lock(mutex); Hungry := TRUE; OSN := HSN + 1; send(request(OSN, me), j) to every j <> i; ORC := n-1; WHILE ORC > 0 DO Sleep(B, mutex); unlock(mutex); END Request; Algoritmo de Ricart e Agrawala - 2 PROCEDURE Release(); BEGIN lock(mutex); Hungry := FALSE; FOR j := 1 TO n DO IF Deferred[j] THEN 144 ~ DISTRIBUIDA CAPITULO 8. PROGRAMACAO send(reply, j); Deferred[j] := FALSE; END; END; unlock(mutex); END Release; PROCEDURE ReceiveRequest(Tk, k: CARDINAL;); BEGIN lock(mutex); HSN := MAX(HSN, Tk); IF Hungry AND ((Tk > OSN) OR (Tk = OSN) AND (k > i)) THEN Deferred[k] := TRUE ELSE send(reply, k); END; unlock(mutex); END ReceiveRequest; PROCEDURE ReceiveReply(); BEGIN lock(mutex) ORC := ORC - 1; IF ORC = 0 THEN WakeUp(B, mutex); unlock(mutex); END; ~ MUTUA 8.2. EXCLUSAO DISTRIBUIDA 145 Exemplo 0 0 1 1 2 0 1 1 1 1 2 2 3 4 4 5 5 Ricart e Agrawala: Complexidade Em cada sec~ao crtica: n requests n releases com um total de 2(n ; 1) mensagens por sec~ao crtica 8.2.3 Carvalho e Roucairol (1983) { 1 GENIAL [Carvalho and Roucairol, 1983] evoluc~ao do algoritmo de Ricart e Agrawala, que tira proveito da validade de uma autorizaca~o para \comer" mais de uma vez sem enviar novas requisic~oes. 146 ~ DISTRIBUIDA CAPITULO 8. PROGRAMACAO CONST i = /* this node unique number */ VAR OSN, /* Our Sequence Number */ HSN /* Highest Sequence Number */: CARDINAL; A, Deferred: ARRAY [1..n] OF BOOLEAN; Eating, Hungry: BOOLEAN; PROCEDURE Request(); VAR j: CARDINAL; BEGIN lock(mutex); Hungry := TRUE; OSN := HSN + 1; FOR j := 1 TO n DO IF (j <> i) AND (NOT A[j]) THEN send(request(OSN, i),j); WHILE NOT (A[j] = TRUE for all j <> i) DO Sleep(B,mutex); Hungry := FALSE; Eating := TRUE; unlock(mutex); END Request; Carvalho e Roucairol (1983) { 2 PROCEDURE Release(); VAR j: CARDINAL; BEGIN lock(mutex); Eating := FALSE; Thinking := TRUE; FOR j := 1 TO n DO IF Deferred[j] THEN A[j] := FALSE; Deferred[j] := FALSE; send(release(i),j); unlock(mutex); END Release; PROCEDURE ReceiveRequest(Tk, k: CARDINAL); VAR OurPriority: BOOLEAN; BEGIN lock(mutex); HSN := MAX(HSN, Tk); OurPriority := (Tk > OSN) OR ((Tk = OSN) AND (k > i)); IF Eating OR (Hungry AND OurPriority) THEN Deferred[j] := TRUE; ELSE /* Eating = FALSE, Hungry => NOT OurPriority */ send(release(i),j); ~ MUTUA 8.2. EXCLUSAO DISTRIBUIDA 147 IF Hungry AND A[j] THEN send(request(OSN,i),j); A[j] := FALSE; END; unlock(mutex); END ReceiveRequest); PROCEDURE ReceiveRelease(k: CARDINAL); BEGIN lock(mutex); A[k] := TRUE; WakeUp(B, mutex); unlock(mutex); END ReceiveRelease; Exemplo 3 4 0 0 0 4 5 1 1 2 1 1 5 6 1 6 4 1 7 2 7 2 3 4 6 7 7 7 4 7 5 5 8 9 10 Carvalho e Roucairol: Complexidade Por sec~ao crtica: entre 0 e n ; 1 requests entre 0 e n ; 1 releases com um total variando entre 0 e 2(n ; 1) mensagens por seca~o crtica. 8.2.4 O Algoritmo de Maekawa (1985) Para n nos, { o algoritmo de Lamport usa 3:(n ; 1) mensagens por seca~o crtica, ~ DISTRIBUIDA CAPITULO 8. PROGRAMACAO 148 { Ricart e Agrawala usa 2:(n ; 1), e { Carvalho e Roucairol usa entre 0 e 2:(n ; 1) mensagens por sec~ao crtica. Nesta aula vamos explorar o uso de esquemas de comunicac~ao para diminuir a ordem de complexidade na troca de mensagens. Os dois algoritmos { Maekawa (1985) e Carvalho e Campos (1987) utilizam um esquema de comunicaca~o denido por um grafo especial, chamado de plano projetivo nito Usando planos projetivos nitos, a complexidade e reduzida para O(pn), ao custo de uma indirec~ao que penaliza a transfer^encia do recurso crtico em um tempo de transmiss~ao de mensagens. Princpios Basicos O n umero de mensagens trocadas por tomada de sec~ao crtica e da ordem p de (n); Isto e obtido ao custo de um maior tempo de transfer^encia do recurso; O algoritmo e simetrico: mesmos deveres e direitos para todos os stios; E melhor compreendido se separarmos os papeis de arbitro e de cliente de cada stio; utiliza planos projetivos nitos para restringir as comunicac~oes; conitos s~ao resolvidos por meio de relogios logicos , mas a ordem FCFS nem sempre e observada[Maekawa, 1985]. Planos Projetivos Finitos { Denic~ao 1 2 3 4 5 6 ~ MUTUA 8.2. EXCLUSAO DISTRIBUIDA 149 Colec~oes de pontos e linhas , onde linhas s~ao conjuntos de pontos, tais que[Lakshman and Agrawala, 1986]: 1. Dois pontos distintos pertencem sempre a uma unica linha ; 2. Duas linhas distintas possuem sempre um unico ponto comum ; Linhas: L1 L2 L3 L4 L5 L6 L7 = = = = = = = f1; 2; 5g f2; 4; 7g f2; 3; 6g f3; 4; 5g f5; 6; 7g f1; 4; 6g f1; 3; 7g Reparem que foi possvel enumerar as linhas mantendo o ponto i na linha Li Planos Projetivos Finitos { Teoremas 1. Todo ponto pertence ao mesmo numero de linhas 2. todas as linhas possuem o mesmo numero de pontos 3. O numero de linhas a que um ponto pertence e igual ao numero de pontos que uma linha possui ; 4. Sempre e possvel enumerar as linhas mantendo o ponto i per- tencente a linha Li 5. Um ppf com m +1 pontos em cada linha (e m +1 linhas passando por cada ponto) possui m2 + m + 1 pontos e m2 + m + 1 linhas; o numero m e a ordem do ppf ; 6. Se m = pk , onde p e um numero primo e k e um inteiro positivo, ent~ao existe um ppf de ordem m . Observac~ao: a prova da conjectura de que planos projetivos nitos existem somente para ordens m = pk , onde p e um numero primo e k e um inteiro positivo, e um dos mais importantes problemas abertos em Combinatoria1 1 http://www.astro.virginia.edu/~eww6n/math/ProjectivePlane.html ~ DISTRIBUIDA CAPITULO 8. PROGRAMACAO 150 Esquema de Comunicac~ao por ppfs Pontos ! clientes Linhas ! arbitros Associac~ao: Arbitros 1 2 3 4 5 6 7 1 2 3 4 5 6 7 Clientes todo cliente pede autorizac~ao ao mesmo numero de arbitros ; { (Todo ponto pertence ao mesmo numero de linhas) todo arbitro coordena a autorizac~ao para o mesmo numero de clientes ; { (Todas as linhas possuem o mesmo numero de pontos) dois clientes possuem sempre um unico arbitro comum ; { (Dois pontos distintos pertencem sempre a uma unica linha) O cliente i utiliza o arbitro i, para economia de mensagens . { Sempre e possvel enumerar as linhas mantendo o ponto i pertencente a linha Li Para qualquer numero de stios n, e possvel encontrar n0 n tal que 2 + m + 1 pontos, onde m = pk para p primo e exista um ppf com n0 = m p k positivo, onde m = O( n) Algoritmo de Maekawa { Mensagens Tipos de mensagens: request usada pelos clientes para pedir aos seus arbitros sua autorizac~ao; release usada pelos clientes para devolver aos arbitros a autorizac~ao; locked usada pelos arbitros para conceder a autorizac~ao; ~ MUTUA 8.2. EXCLUSAO DISTRIBUIDA 151 inquire usada pelos arbitros para indagar da possibilidade de obtenc~ao de todas as autorizac~oes de um cliente; failed usada pelos arbitros para dizer a um cliente que a autorizac~ao encontrase com outro cliente mais prioritario; relinquish usada por um cliente para devolver a autorizac~ao a um arbitro, apos ter recebido failed de outro arbitro. Algoritmo de Maekawa { Cliente i 1. Requisic~ao : o cliente envia request(OSN, i) para todos os seus arbitros j , e faz Failed[j] := Inquiring[j] := FALSE; 2. Recepc~ao de locked(Tk, k) : o cliente faz A[k] := TRUE Failed[k] := FALSE; 3. Comeca a comer : se utilizar o recurso; A[j] = TRUE e para todos os arbitros, passa a 4. Recepc~ao de inquire(Tk, k) : se Failed[j] = TRUE para algum arbitro j , o cliente envia relinquish(Ti,i) para o arbitro k; sen~ao, faz Inquiring[k] := TRUE; 5. Recepc~ao de failed(Tk, k) : o cliente faz Failed[k] := relinquish(Ti, i) para todo arbitro j tal que Inquiring[j] o envio, faz Inquiring[j] := FALSE; TRUE, e envia = TRUE; ap os 6. Liberac~ao : o cliente envia release(Ti, i) para todos os seus arbitros; Algoritmo de Maekawa { A rbitro j 1. Recepc~ao de request(Ti, i) : se Owner = NIL, o arbitro faz Owner := i, OwnerTi := Ti, e envia locked(Tj,j) para o cliente i; sen~ ao, o arbitro coloca o pedido numa la. Neste caso, se (Ti, i) for mais prioritario que (OwnerTi, Owner), o arbitro envia inquire(Tj,j) para o cliente Owner; 2. Recepc~ao de release(Ti,i) : se a la estiver vazia, o arbitro faz Owner := NIL; sen~ ao, o arbitro envia locked(Tj, j) para o cliente k mais prioritario, faz Owner := k e OwnerTi := Tk, e retira k da la; 3. Recepc~ao de relinquish(Ti,i) : a reac~ao e id^entica a da recepc~ao de release, mas (Ti, i) e colocado na la; 152 ~ DISTRIBUIDA CAPITULO 8. PROGRAMACAO 8.2.5 Algoritmo de Carvalho e Campos (1991) { 1 GENIAL [de Aguiar Campos and Carvalho, 1988] fus~ao das ideias de Maekawa e de Chandy e Misra; O esquema de comunicac~ao e restrito por um plano projetivo nito ; Conitos s~ao resolvidos pela profundidade dos nos num grafo acclico ; O numero de mensagens trocadas por sec~ao crtica varia entre 0 e um p maximo que e O( (n)); Stios agem como clientes e como arbitros; Cada arbitro usa um permission token como autorizac~ao unica disputada por seus clientes; Para comer um cliente precisa da permiss~ao de todos os seus arbitros; Entre um cliente e um arbitro circula um request token, usado por ambos para pedir a permiss~ao; permiss~oes podem estar sujas ou limpas ; ao comer, um cliente suja todas as suas permiss~oes ; um cliente envia de volta a permiss~ao de um arbitro no estado em que ela se encontra, suja ou limpa; um arbitro so envia permiss~oes limpas; cada arbitro mantem uma lista de prioridades que ordena totalmente seus clientes; esta lista so e modicada pelo arbitro na recepc~ao de uma permiss~ao suja, quando o cliente que a devolveu e movido para a ultima posic~ao . Carvalho e Campos: Grafo de Preced^encias Sejam c e d dois clientes, e seja a seu arbitro comum; O cliente c tem preced^encia sobre d se e somente se : 1. d possui a permiss~ao Pa , suja , ou 2. Pa esta em tr^ansito indo de d para a, e esta suja , ou 3. nenhuma das condico~es acima aplica-se a d ou a c, e Prioritya (c) > Prioritya (d) . ~ MUTUA 8.2. EXCLUSAO DISTRIBUIDA 153 Suponha que a permiss~ao de a foi dada a c, e que foi pedida por um outro cliente d: se Prioritya (d) > Prioritya (c), o arbitro a sabe que d tem preced^encia so- bre c, e pede sua permiss~ao de volta usando uma mensagem StrongRequest; se Prioritya (d) < Prioritya (c), a relac~ao de preced^encia entre c e d e determinada pelo estado (suja ou limpa) de Pa , e esta informac~ao e local ao cliente c. Carvalho e Campos: mensagens e variaveis Mensagens de um arbitro para um cliente: CleanPermission, request e StrongRequest ; Mensagens de um cliente para um arbitro: request, CleanPermission e DirtyPermission . Variaveis de um cliente c: Permissionc(a) Reqc(a) : Strongreqc (a) : Dirtyc (a) : Thinkingc : Hungryc : Eatingc : c tem a permiss~ao do arbitro a; c tem a requisic~ao que compartilha com o arbitro a; c recebeu um StrongRequest do arbitro a; c possui a permiss~ao de a, que esta suja; c esta pensando; c esta com fome; c esta comendo. Variaveis de um arbitro a: Ownera cliente que esta com a permiss~ao de a; igual a NIL se Pa estiver com a; Reqa (c) a possui a requisica~o que compartilha com o cliente c; StrongReqSenta a ja enviou um StrongRequest para o cliente que esta com sua permiss~ao. Carvalho e Campos: Algoritmo do Cliente { 1 R 1 Becoming hungry: Thinking ! Hungry := TRUE R 2 Starting to eat: Hungry; 8a 2 S Permission(a) ! Eating := TRUE 8a 2 S; Dirty(a) := TRUE ~ DISTRIBUIDA CAPITULO 8. PROGRAMACAO 154 R 3 Starting to think: Eating ! Thinking := TRUE R 4 Requesting a permission: Hungry; a 2 S; Permission(a); Req(a) ! send(a; Request) Req(a) := FALSE R 5 Sending a dirty permission: Eating; a 2 S; Permission(a); Req(a); Dirty(a) ! send(a; DirtyPermission) Permission(a) := FALSE Strongreq(a) := FALSE Carvalho e Campos: Algoritmo do Cliente { 2 R 6 Sending a clean permission: a 2 S; Permission(a); Strongreq(a); Dirty(a) ! send(a; CleanPermission) Permission(a) := FALSE Req(a) := FALSE Strongreq(a) := FALSE R 7 Receiving a permission: receive(a; CleanPermission) ! Permission(a) := TRUE Dirty(a) := FALSE R 8 Receiving a request: receive(a; Request) ! Req(a) := TRUE ~ MUTUA 8.2. EXCLUSAO DISTRIBUIDA R 9 Receiving a strong request: receive(a; StrongRequest) ! Permission(a) ! Req(P ) := TRUE Strongreq(P ) := TRUE Permission(a) ! SKIP Carvalho e Campos: Algoritmo do A rbitro { 1 R 10 Receiving a request: receive(c; Request) ! Req(c) := TRUE R 11 Receiving a dirty permission: receive(c; DirtyPermission) ! Owner := NIL GiveLeastPriorityTo(c) R 12 Receiving a clean permission: receive(c; CleanPermission) ! Owner := NIL Req(c) := TRUE R 13 Sending the permission: Req(c); c = HighestPriorityRequest; Owner = NIL ! send(c; CleanPermission) Owner := c StrongReqSent := FALSE Carvalho e Campos: Algoritmo do A rbitro { 2 R 14 Asking back the permission, kindly: 9c; c 6= Owner; Owner 6= NIL; Req(c); Req(Owner); Priority(Owner) > Priority(c) ! 155 ~ DISTRIBUIDA CAPITULO 8. PROGRAMACAO 156 send(Owner; Request) Req(Owner) := FALSE R 15 Asking back the permission, rmly: 9c; c 6= Owner; Owner 6= NIL; Req(c); StrongReqSent; Priority(Owner) < Priority(c) ! send(Owner; StrongRequest) StrongReqSent := TRUE Req(Owner) := FALSE Condic~oes iniciais : permiss~oes com os arbitros; requisic~oes com os clientes; listas de prioridades determinando um grafo acclico. Carvalho e Campos: Exemplo { 1 priority tables arbiters clients '$ '$ '$ &% &% &% '$ '$ '$ &% &% &% 1 3 1 2 2 3 1 r HH 2 HH;H; ; ; H ; HH ; ; ; HH;H ; ; r ; HH H r r Figura 8.1: Initial conguration 3 r r ~ MUTUA 8.2. EXCLUSAO DISTRIBUIDA priority tables arbiters clients '$ '$ '$ &% &% &% '$ '$ '$ &% &% &% 1 3 1 2 1 r r 2 HHHH HjH;; ; ; H ; HHH ; ; ; HH; ; HHH ; ; r H r r 157 2 3 3 r _ Figura 8.2: Client 3 got hungry Carvalho e Campos: Exemplo { 2 Carvalho e Campos: Exemplo { 3 Carvalho e Campos: Exemplo { 4 8.2.6 O Algoritmo de Naimi-Trehel Nesta sec~ao vamos apresentar o algoritmo de Naimi-Trehel[Naimi and Trehel, 1987], redescoberto de forma independente por Guilherme Padua em 1994, ent~ao aluno de iniciac~ao cientca. E a vers~ao do Guilherme, com contribuic~oes do Marco Aurelio, do Kemio e minhas que e apresentada aqui utiliza um token com informaca~o suciente para manter uma la de requisic~oes, reduz a complexidade media para O(log n) mensagens, cujo tamanho entretanto e da ordem de n um stio i com fome envia um unico request para o stio nexti , que ele julga deter o token; um stio j que recebe um request do stio i, { se estiver com fome ou comendo armazena a requisic~ao em uma la local; 158 priority tables arbiters clients ~ DISTRIBUIDA CAPITULO 8. PROGRAMACAO '$ '$ '$ &% &% &% '$ '$ '$ &% &% &% 1 3 1 2 2 3 2 3 _ _ 1r r r HHHHj r HH;; ; ; r ; HHHH ; ? HH;; ;; H ; ; HHH r Figura 8.3: Client 2 got hungry { ao terminar de comer, j envia o token para o stio mais prioritario em sua la local; a la e enviada junto com o token, e esvaziada em seguida. nextj passa a apontar para o stio menos prioritario nesta la. { se estiver pensando e n~ao estiver de posse do token, o request e retransmitido para o stio nextj ; nextj passa a apontar para o stio que originou o request recebido (que pode ser diferente do stio que enviou o request) State Variables and Initial Values Each site keeps the following variables in its private memory: ~ MUTUA 8.2. EXCLUSAO DISTRIBUIDA priority tables arbiters clients '$ '$ '$ &% &% &% '$ '$ '$ &% &% &% 1 3 1 r r 1 2 2 3 2 3 r _ _ HHHH r HjH;; r ; ;; H ; ; HHH ; ; ; HH; ; HHH ; ; H r 159 Figura 8.4: Client 3 was forced to render a clean permission Variable Description s may be thinking, hungry or eating HasToken boolean indicating the presence of the token ReqList FIFO list of stored requests Next integer between 1 and N, or nil T R Initial Value s = thinking TRUE for node 1; FALSE for all other nodes ReqList = EMPTY for node 1, Next = nil; for all other nodes, Next = 1 a record corresponding to a to- ken message, with a single eld, T.ReqList, which is a FIFO list of requests a record corresponding to a re- quest message, with a single eld, R.origin, indicating the site who sent the message Auxiliary Variables The array V [1 : : : N] (for V isited) is used only for proof purposes. V [k] is a sequence of node numbers. It contains the nodes already \visited" by a request originated at node k. As an auxiliary variable, V [k] may be read or written by any node; Initially V [k] = EMPTY for all k. 160 priority tables arbiters clients ~ DISTRIBUIDA CAPITULO 8. PROGRAMACAO '$ '$ '$ &% &% &% '$ '$ '$ &% &% &% 1 3 1 2 2 3 1 r HrHHHj r r HH;; r ; ;; H ; ; HHH ; ; ; ? HH; ; H ; ; HHH 2 3 r _ _ _ Figura 8.5: Client 1 got hungry Rules s := hungry s = eating, ReqList 6= EMPTY ! ! ! ! Rule 5: requesting the token s = hungry, Next 6= nil ! Receive(R), s 6= thinking Receive(R), s = thinking, Next = nil ! ! send(Next, Request(Myself)); Next := nil; V [Myself] := EMPTY; Rule 8: forwarding a request Receive(R), s = thinking, Next 6= nil ! Rule 9: receiving the token ! Rule 1: getting hungry s = thinking Rule 2: starting to eat s = hungry, Hastoken s = eating, ReqList = ; Rule 3: stoping to eat, keeping the token Rule 4: stoping to eat, sending the token Rule 6: storing a request Rule 7: releasing the token Receive(T) s := eating s := thinking s:= thinking ; Next := last(ReqList); T.ReqList := tail(ReqList); send(head(ReqList), T); HasToken := FALSE; ReqList := EMPTY; for all k 2 T.ReqList do V [k]:= V [k] cat Myself; put(ReqList, R.origin) T.ReqList := EMPTY; send(R.origin, T); HasToken := FALSE; Next := R.origin send(Next, R); Next := R.origin; V [R.origin] := V [R.origin] cat Myself; Hastoken := TRUE; ReqList := T.ReqList cat ReqList V [Myself] := EMPTY; ~ MUTUA 8.2. EXCLUSAO DISTRIBUIDA priority tables arbiters clients '$ '$ '$ &% &% &% '$ '$ '$ &% &% &% 1 3 1 r HH 1 2 2 3 2 r 3 r _ _ HH;; ; ; H ; HHH ; ; ; HH; ; HHH ; r r; H r ^ 161 Figura 8.6: Client 1 eats and dirties his permissions Proof of Mutual Exclusion Predicate A1 bellow is an inductive invariant2. 8 8Vi; eating[i] =) HasToken[i] > > > > > > > > 8Vi; j 2 [1 : : : N ]; i 6= j =) (:HasToken[i] _ :HasToken[j ]) > > > > < A1 > 8Vi; HasToken[i] =) 8j; T 62 IN [j ] > > > 2 > > 8j 6= i; T 62 IN [j ] > > > > 4 ^ 8 i; T 2 IN [ i ] = ) > > : 8j; :HasToken[j ] (8.1) Progress: the ! Graph Let D be a relation dened between nodes of the network by the following rules: Node i ! j if and only if one of the following conditions is veried: D1: Next[i] = j D2: R [i] 2 IN[j] D3: Position(ReqList[j],i) = 1 D4: 9 k, Position(ReqList[k],j) = Position(ReqList[k],i) + 1 2 A1 is valid initially, and for every rule R with guard G and action S , fA1 ^ Gg S fA1g. 162 priority tables arbiters clients ~ DISTRIBUIDA CAPITULO 8. PROGRAMACAO '$ '$ '$ &% &% &% '$ '$ '$ &% &% &% 3 1 1r r HH 2 1 2 3 2 r 3 r HH;; ; ; H ; HHH ; ; ; HH; ; HHH ; ; H r r ^ _ Figura 8.7: Client 2 eats D5: T 2 IN[j], Position(T.ReqList,i) = 1 D6: 9 k, T 2 IN[k], Position(T.ReqList, j) = Position(T.ReqList,i) + 1 Theorem 1 The root node: Let root be a state predicate such that root(i) = TRUE i 6 9 no other node j such that i ! j. THEOREM 1 A2 There is always one and only one root node, i.e., a node i such that root(i) = TRUE; A3 The relation ! always dene one and only one acyclic path from any non-root node to the root. We must add A4 and A5 bellow in order to get an inductive invariant: A4 (HasToken[i] _ T 2 IN[i]) , root(i) A5 For any non-root node i, there is one and just one node j such that i ! j, and one and just one condition among D1, . . . , D6 holds between i and j. Theorem 1: Initial Conditions A2 ^ A3 ^ A4 ^ A5 hold initially: ~ MUTUA 8.2. EXCLUSAO DISTRIBUIDA '$ '$ '$ &% &% &% '$ '$ '$ &% &% &% 1 3 priority tables arbiters 1 r r clients HH 1 2 3 2 2 r 3 r HH;; ; ; H ; HHH ; ; ; HH; ; HHH ; ; H r r 163 ^ Figura 8.8: Client 3 nally eats Node 1 is the only node that has HasToken = TRUE, and it is the unique root. Every node j, j 6= i, ! node 1, since { all input channels and ReqLists are initially empty, and thus conditions D2 to D6 do not apply to any node; { Next[1] = nil, and Next[2] = Next[3] = : : : Next[N] = 1 initially. Theorem 1: Scanning the Rules Now we will show that for every rule R, if A2 ^ A3 ^ A4 ^ A5 and the enabling predicate hold, then Theorem 1 will hold after the rule's action. Rules 1 to 3 do not change relation ! , so they trivially verify the induction step. The following conventions are used to represent graphically the ! graph: ~ DISTRIBUIDA CAPITULO 8. PROGRAMACAO 164 a D2 a a b Node a depends on node b by D2 b There is a path from a to b a is the root node Theorem 1: \Proof" for Rule 4 x x i i D3 D1 y a y a D5 D4 z b z b D4 w c D6 w i’s ReqList Before Rule 4 c token ReqList After Rule 4 Stopping to eat, sending the token ~ MUTUA 8.2. EXCLUSAO DISTRIBUIDA 165 Theorem 1: \Proof" for Rule 5 i D1 Next root Before Rule 5 x i y D2 Next z root After Rule 5 x y Requesting the token; z ~ DISTRIBUIDA CAPITULO 8. PROGRAMACAO 166 Theorem 1: \Proof" for Rule 6 x i root D3 y D2 a R b w Before Rule 6 D4 z x i root a R D3 y D4 z After Rule 6 D4 b w Storing a request (Rule 6) Theorem 1: \Proof" for Rule 7 x R x R D2 D1 y i Before Rule 7 Releasing the token upon request y i After Rule 7 ~ MUTUA 8.2. EXCLUSAO DISTRIBUIDA 167 Theorem 1: \Proof" for Rule 8 D2 D1 R Next i root Before Rule 8 x y z w Next root D2 D1 R i After Rule 8 x z y w Forwarding a request Theorem 1: \Proof" for Rule 9 token ReqList D6 c b x y i’s ReqList D6 a D5 i D3 r D4 z s s Before Rule 9 After Rule 9 D4 c b D4 D4 a D3 i r D4 s i’s ReqList x y z s ~ DISTRIBUIDA CAPITULO 8. PROGRAMACAO 168 Receiving the token Progresso Queremos mostrar que s[i] = hungry leads-to s[i] = eating Vamos comecar mostrando que P 1 s[i] = hungry ^ root(i) leads;to s[i] = eating: Pelo invariante A4, root(i) =) Hastoken[i] _T 2 IN [i]. E facil ver que (por hipotese de equidade fraca na execuc~ao da Regra 9) hungry ^ T 2 IN [i] leads;to Hastoken[i] Como hungry ^ Hastoken[i] leads;to s[i] = eating; temos o resultado que queramos. Consideremos agora uma computac~ao innita em que um stio i0 estava com fome em um certo instante t0 , e permaneceu com fome ate o m dos tempos. Pelo que acabamos de demonstrar, sabemos que no instante t0 tnhamos root(i0 ) = F , e portanto que para todo instante t t0 , 9j; i0 ! j . Se no instante t0 tnhamos Next[i] 6= NIL, a regra 5 certamente tera sido executada em um instante t1 > t0 . Um Request tera sido enviado por i0 . Este Request n~ao pode ter visitado um numero ilimitado de stios, como veremos a seguir. Portanto, teremos um stio i1 onde o Request de i tera se estabilizado a partir de um instante t2 . Por D3 ou D4 , teremos i ! i1 para todo instante t t2 Isto so e possvel se i1 tambem nunca chegou a comer na computac~ao , a partir do instante t2 . Pelo mesmo raciocnio, existe um stio i2 que a partir de um certo instante reteve o Request de i1 , e que nunca chegou a comer. Portanto, a unica possibilidade de exist^encia de e a exist^encia de uma sequ^encia innita de stios i0; i1 ; i2 ; : : : tal que, a partir de um certo instante, o Request de ik esteja retido em ik+1 . Mas como ik ! ik+1 , e como D e um grafo acclico, esta sequ^encia n~ao pode conter repetic~oes, e n~ao pode existir. Um Request visita um numero nito de stios Para provar que um Request n~ao pode visitar um numero ilimitado de stios, vamos demonstrar o seguinte invariante: ~ MUTUA 8.2. EXCLUSAO DISTRIBUIDA 169 A7 if V [i] = v1 v2 : : : vm , then for j; k 2 [1 : : : m], vj 6= i, and j 6= k =) vj = 6 vk . Em outras palavras, todos os elementos em V [i] s~ao distintos, e seu comprimento n~ao pode crescer indenidamente. A7 n~ao e indutivo, e precisamos reforca-lo com o seguinte predicado: A8 = j 2 V[i] =) j !+ i onde j !+ i signica que existe uma rota de j para i de comprimento maior ou igual a 1 no grafo ! . Como para todo i V[i] e inicialmente vazio, A8 e valido inicialmente. Precisamos agora varrer todas as regras do algoritmo, demonstrando que se A8 for valido antes da execuc~ao da regra, A8 sera valido tambem apos a sua execuc~ao. A8 corre o risco de ser invalidado por regras que modiquem o valor de algum V[i] ou que eliminem relac~oes j !+ i para algum par de stios. Exerccios Exerccio 8.2.1 (Defasagem de Relogios) Dois computadores que se comu- nicam por mensagens querem manter seus relogios logicos com uma defasagem que nunca exceda um certo valor . Faca um algoritmo distribudo para o controle desdes dois relogios, procurando minimizar o trafego de mensagens e o tamanho de cada mensagem. Considere que o meio de comunicac~ao nunca inverte a ordem de envio das mensagens. Exerccio 8.2.2 (Transmiss~ao de Sequ^encias) Programe um algoritmo pa- ra transmiss~ao de uma sequ^encia de itens de um computador para outro. Cada mensagem deve transportar no maximo um item por vez. A sequ^encia de itens recebidos deve permanecer igual a sequ^encia de itens enviados, ou ter apenas um item a menos. Considere que o meio de comunicac~ao nunca inverte a ordem de envio das mensagens, mas que pode perder algumas delas. Exerccio 8.2.3 (Conta Bancaria) Uma conta bancaria deve ser mantida por dois computadores; qualquer um deles deve permitir depositos e retiradas. Programe estas operac~oes de forma a n~ao permitir que o saldo global seja negativo, e que procurando minimizar o trafego de mensagens. ~ DISTRIBUIDA CAPITULO 8. PROGRAMACAO 170 8.3 2o Trabalho Pratico Distribuic~ao: 17/11/98 Entrega: 21/12/98 Parte 1: Leia a parte de sockets no tutorial da Sun. Parte 2: Implemente os algoritmos de Maekawa, Carvalho e Campos e Naimi e Trehel usando o sistema Voyager3. O sistema deve inicializar em cada uma das n maquinas um servidor Voyager. Para cada algoritmo, um agente com a implementac~ao do algoritmo deve ser enviado para cada maquina participante. Procure fazer uma interface que permita monitorar a instalac~ao e execuc~ao dos algoritmos. Informac~oes sobre a instalac~ao Voyager do DCC podem ser obtidas contactandose o Yuri4 no CRC. 8.3.1 Voyager Voyager5 e um sistema moderno para programac~ao distribuda em Java que oferece um tipo simples de agentes. Programas Voyager Um programa Voyager que executa Voyager.startup(7000); Store store = new Store() associa-se ao porto 7000, e automaticamente dispara threads que prov^em servicos de temporizaca~o fazem coleta de lixo distribuda aceitam trafego na rede Classes Remotas e Refer^encias Virtuais Classes Remotas podem ser geradas a partir de classes comuns, utilizando-se o utilitario vcc, que aplicado sobre Store.java ou Store.class gera a classe remota VStore.class 3 4 5 htt;://www.objectspace.com mailto:[email protected] http://www.objectspace.com 8.3. 2O TRABALHO PRATICO 171 Inst^ancias de Classes Remotas podem ser criadas fora do espaco de enderecamento de um programa podem receber mensagens como se fossem locais. Uma refer^encia virtual e usada para isto. Se uma classe e remota, pode-se construir inst^ancias remotas , mesmo se o codigo da classe n~ao existe na maquina remota Voyager.startup(7000); VStore vstore = new VStore("dallas:8000/Loja"); // Loja \'{e} um alias enviar mensagens para inst^ancias remotas vstore.stock("widget",43); estabelecer conex~oes com inst^ancias remotas de outros programas // conecta usando alias Voyager.startup(9000); VStore vstore2 = (VStore) VObject.forObjectAt("dallas:8000/Loja"); int price = vstore2.buy("widget"); mover objetos para outros programas vstore.moveTo("tokyo:9000"); O objeto espera o processamento das mensagens pendentes e muda-se, deixando o novo endereco para os que n~ao sabem de sua mudanca tornar o objeto persistente . Agentes Voyager Agentes Voyager s~ao objetos que podem autonomamente decidir sua propria mudanca para outras maquinas public void travel() { moveTo("tokyo:9000", "atTokio"); } 172 ~ DISTRIBUIDA CAPITULO 8. PROGRAMACAO \atTokio" e um metodo que sera chamado assim que o agente chegar em \tokyo:9000". para outros objetos public void buyFrom(VStore vstore) { moveTo(vstore,"shop"); } o agente se move para a maquina onde esta vstore; ao chegar la, o procedimento shop e chamado. Tipos de Mensagens Mensagens, ou chamadas de metodos, podem ter diversos tipos: sncronas assncronas (One-Way ) circulares assncronas futuras Parte IV Balanco e Conclus~oes 173 Bibliograa [Andrews and Schneider, 1983] Andrews, G. R. and Schneider, F. B. (1983). Concepts and notations for concurrent programming. Computing Surveys, 15:3{43. [Bal et al., 1989] Bal, H. E., Steiner, J. G., and Tanembaum, A. S. (1989). Programming languages for distributed computing systems. ACM Computing Surveys, 21(3). [Barbara and Garcia-Molina, 1986] Barbara, D. and Garcia-Molina, H. (1986). Mutual exclusion in partitioned distributed systems. Distributed Computing, 1(1):119{132. [Bernstein et al., 1987] Bernstein, P. A., Hadzilacos, V., and Goodman, N. (1987). Concurrency Control and Recovery in Database Systems. AddisonWesley, Reading. [Bitton et al., 1984] Bitton, D., DeWitt, D. J., Hsiao, D. K., and Menon, J. (1984). A taxonomy of parallel sorting. ACM Computing Surveys, 16(3):287{ 318. [Campbell and Habermann, 1974] Campbell, R. H. and Habermann, A. N. (1974). The specication of process synchronization by path expressions, volume 16 of Lecture Notes in Computer Science. Springer Verlag. [Carvalho and Roucairol, 1983] Carvalho, O. S. F. and Roucairol, G. (1983). On mutual exclusion in computer networks. Communications of the ACM, 26(2). [Chandy and Misra, 1984] Chandy, K. M. and Misra, J. (1984). The drinking philosophers problem. ACM Transactions on Programming Languages and Systems, 6(4). [de Aguiar Campos and Carvalho, 1988] de Aguiar Campos, S. V. and Carvalho, O. S. F. (1988). Um algoritmo distribudo para exclus~ao mutua com o(pn) mensagens. In Anais do 6o Simposio Brasileiro de Redes de Computadores, PortoAlegre. Sociedade Brasileira de Computac~ao. 175 176 BIBLIOGRAFIA [Dijkstra, 1965a] Dijkstra, E. W. (1965a). Co-operating Sequential Processes. Academic Press, London. [Dijkstra, 1965b] Dijkstra, E. W. (1965b). Solution of a problem in concurrent programming control. Communications of the ACM, 8(9):569. [Dijkstra, 1975] Dijkstra, E. W. (1975). Guarded commands, nondeterminacy and formal derivation of programs. Communications of the ACM, 18(8):453{ 457. [Dinning, 1989] Dinning, A. (1989). A survey of synchronization methods for parallel computers. Computer, 22(7):66{77. [Dubois et al., 1988] Dubois, M., Scheurich, C., and Briggs, F. A. (1988). Synchronization, coherence, and event ordering in multiprocessors. Computer, 21(2):9{21. [Eswaran et al., 1976] Eswaran, K. P., Gray, J. N., Lorie, R. A., and Traiger, I. L. (1976). The notions of consistency and predicate locks in a database system. Communications of the ACM, 19(11). [Floyd, 1967] Floyd, R. W. (1967). Assigning meaning to programs. In Schwartz, J. T., editor, Mathematical aspects of computer science: Proc. American Mathematics Soc. symposia, volume 19, pages 19{31, Providence RI. American Mathematical Society. [Gray and Reuter, 1993a] Gray, J. and Reuter, A. (1993a). Transaction Processing: Concepts and Techniques. Morgan Kaufmann Publishers, San Mateo, CA. [Gray and Reuter, 1993b] Gray, J. and Reuter, A. (1993b). Transaction Processing: concepts and techniques. Morgan Kaufmann Publishers, Inc. [Hansen, 1972] Hansen, P. B. (1972). Concurrent programming concepts. ACM Computing Surveys, 5:233{245. [Hoare, 1969] Hoare, C. A. R. (1969). An axiomatic basis for computer programming. Communications of the ACM, 12(10):576{580. [Hoare, 1974] Hoare, C. A. R. (1974). Monitors, an operating system structuring concept. Communications of the ACM, 17(10):549{557. [Hoare, 1978] Hoare, C. A. R. (1978). Communicating sequential processes. Communications of the ACM, 21(8):666{677. [Johnson and Shasha, 1993] Johnson, T. and Shasha, D. (1993). The performance of concurrent B-Tree algorithms. ACM Transactions on Database Systems, 18(1):51{101. [Keller, 1976] Keller, R. M. (1976). Formal verication of parallel programs. Communications of the ACM, 19(7). BIBLIOGRAFIA 177 [Knuth, 1966] Knuth, D. E. (1966). Additional comments on a problem in concurrent programming control. Communications of the ACM, 9(5):321{ 322. Letter to the editor. [Kohler, 1981] Kohler, W. H. (1981). A survey of techniques for synchronization and recovery in decentralized computer systems. ACM Computing Surveys, 13(2). [Lakshman and Agrawala, 1986] Lakshman, T. V. and Agrawala, A. K. (1986). Ecient decentralized consensus protocols. IEEE Transactions on Software Engineering, SE-12(5):600{607. [Lamport, 1978] Lamport, L. (1978). Time, clocks, and the ordering of events in a distributed system. Communications of the ACM, 21(7). p [Maekawa, 1985] Maekawa, M. (1985). A n algorithm for mutual exclusion in decentralized systems. ACM Transactions on Computing Systems, 3(2). [Manna and Pnueli, 1981] Manna, Z. and Pnueli, A. (1981). Verication of concurrent programs: Temporal proof principles. In Kahn, G., editor, Logics of Programs, volume 131 of LNCS, pages 200{252. Springer-Verlag , Berlin, Germany. [Milner, 1984] Milner, R. (1984). Lectures on a Calculus for Communicating Systems, volume 197 of LNCS. Springer-Verlag, New York, NY. [Naimi and Trehel, 1987] Naimi, M. and Trehel, M. (1987). An improvement of the log N distributed algorithm for mutual exclusion. In 7th International Conference on Distributed Computing Systems, pages 371{377, Washington, D.C., USA. IEEE Computer Society Press. [Owick and Gries, 1976] Owick, S. and Gries, D. (1976). Verifying properties of parallel programs: an axiomatic approach. Communications of the ACM, 19(5). [Pnueli, 1979] Pnueli, A. (1979). The temporal semantics of concurrent programs. In Kahn, G., editor, Semantics of Concurrent Computations, volume 70 of LNCS, pages 1{20, Evian, France. Springer-Verlag , Berlin, Germany. [Ricart and Agrawala, 1981] Ricart, G. and Agrawala, A. K. (1981). An optimal algorithm for mutual exclusion in computer networks. Communications of the ACM, 24(1). [Shankar, 1993] Shankar, A. U. (1993). An introduction to assertional reasoning for concurrent systems. ACM Computing Surveys, 25(3):225{262. [Tanenbaum, 1987] Tanenbaum, A. S. (1987). Operating Systems Design Implemtations. Prentice-Hall. Indice Remissivo Avaliac~ao, 14 cobegin, 18 coend, 18 Conitos, 21 Ementa, 12 Grafo de Preced^encias, 19 Lamport, 103 Links, 15 Modelo de Programac~ao, 16 Modo de execuc~ao, 16 Programac~ao Paralela Assncrona, 16 Sem^antica de Programas Paralelos, 21 Sem^antica n~ao-determinstica, 22 Sem^antica sequencial, 22 178