Por atuar diretamente sobre ambientes de natureza distribuída -- tipicamente uma rede local de estações de trabalho -- as aplicações DPC++ tornam-se bastante sucetíveis à ocorrência de falhas, devido, principalmente, ao uso intenso do meio de comunicação. Este fato motivou, no projeto, o surgimento do estudo de técnicas de Tolerância a Falhas.
Em face de algumas particularidades oriundas do modelo funcional de DPC++, as técnicas tradicionais de Tolerância a Falhas, que, de forma geral, não levam em conta as particularidades das aplicações, podem ser remodeladas de maneira a tirar benefícios dessas particularidades, tornando o estudo de Tolerância a Falhas em DPC++ especialmente interessante.
Assim sendo, o presente trabalho visa apresentar como os mecanismos tradicionais de Tolerância a Falhas vêm sendo aplicados em DPC++ e quais as perspectivas que se têm em função das novas tendências do projeto.
Utilizando o paradigma de orientação a objetos, DPC++ baseia-se no conceito de objetos distribuídos. Estes nada mais são que objetos convencionais, com todas as características já conhecidas, porém com a capacidade de serem instanciados em outro nó do sistema. Isto faz com que haja execução concorrente entre dois objetos em nós distintos, ocasionando o ganho em performance.
A figura 1 mostra uma visão geral
de uma aplicação DPC++
,
com os objetos trocando mensagens exatamente como ocorre nas aplicações
orientadas a objetos que se conhece. A única diferença é
que, neste caso, as mensagens são, na prática, trocadas através
do meio de comunicação, e não diretamente pela memória
RAM.

Como sugere a figura, cada objeto distribuído é, na prática, um processo.
Embora haja o paralelismo de execução entre objetos distribuídos, internamente um objeto é seqüencial, de forma que ele se comunica com apenas um outro objeto de cada vez. Essas duas características mantém o mesmo comportamento de um programa C++ convencional, facilitando a tarefa do programador.
Um elemento extra está, ainda, presente nas aplicações DPC++. Esse elemento, chamado de Diretório, é também um objeto distribuído, mas com tarefas especiais. É ele o responsável pela criação dos objetos distribuídos, além de manter um conjunto de informações sobre cada objeto (por exemplo, em que nó ele está instanciado) e sobre o sistema.
Ao executar um comando new, por exemplo, para criar um novo objeto distribuído, um objeto envia, na verdade, uma mensagem ao objeto Diretório solicitando a criação. Este, por sua vez, escolhe um nodo apropriado (de acordo com técnicas de balanceamento de carga) e instancia o objeto. Este ``endereço'' é, então, retornado ao objeto original, que agora pode comunicar-se com o novo objeto. Cabe lembrar que este procedimento é totalmente transparente ao programador da aplicação.
A figura 2 ilustra uma aplicação
DPC++ durante o processo de criação de um novo objeto distribuído.

A figura 3 ilustra a criação de pontos de recuperação quando da troca bem sucedida de uma mensagem entre dois objetos distribuídos, bem como as mensagens de confirmação definidas pelo protocolo estabelecido:

O objeto
envia uma mensagem ao objeto
e cria seu ponto de recuperação. Ao receber a mensagem,
cria o seu checkpoint e envia uma mensagem de confirmação.
Ao receber esta última,
envia nova confirmação, determinando o estabelecimento de
um estado consistente entre os dois objetos, visto que a mensagem original
foi bem recebida e os pontos de recuperação corretamente
criados.
O protocolo que rege esse mecanismo garante que, na ocorrência de uma situação de falha em um dos objetos, seja possível restaurar o sistema a um estado global consistente. Esta característica advém de uma série de particularidades das aplicações DPC++.
Por exemplo, a figura 4 mostra uma situação
de falha no recebimento de uma mensagem por parte de
:

Duas características particulares da comunicação entre objetos em DPC++ justificam a eficácia do mecanismo de tolerância a falhas utilizado:
Uma outra situação de falha que se poderia imaginar seria
um objeto (como
,
por exemplo) receber mais de uma mensagem ao mesmo tempo. Novamente, as
características de DPC++ garantem o funcionamento do mecanismo.
Pelo fato de os objetos em DPC++ serem seqüenciais, ou seja, não
existe paralelismo internamente a um objeto, as requisições
de execução de métodos são tratadas em ordem
FIFO, de modo que a segunda requisição será armazenada
e só será tratada depois que todo o processo de recebimento
e efetivação da primeira tiver sido completado.
O mecanismo de tolerância a falhas de DPC++, por sua vez, requer uma quantidade menor de mensagens quando da criação de um checkpoint, e portanto esta operação pode, isoladamente, ser executada mais eficientemente do que aquela introduzida por Koo e Toueg. Porém, a influência global do mecanismo de checkpoints distribuídos apresentado depende do grau de comunicação entre os objetos distribuídos, sendo diretamente influenciado pela estrutura da aplicação. Como os pontos de recuperação são criados a cada troca de mensagens, quanto mais freqüentemente os objetos se comunicarem, maior será a influência do mecanismo de tolerância a falhas no desempenho final da aplicação.
Tendo em vista a natureza distribuída de DPC++, onde o ``grão'' de paralelismo é maior, pode-se considerar o mecanismo proposto como adequado, pois a estrutura de aplicações DPC++ tende a apresentar um padrão de comunicação relativamente baixo. Como a comunicação entre nodos distintos tem um alto custo em relação ao processamento, é normal que um objeto distribuído comunique-se com pouca freqüência, realizando alguma tarefa computacional durante a maior parte de sua vida. Na prática, a utilização do mecanismo descrito conseguiu efetivamente introduzir um nível de tolerância a falhas nos objetos distribuídos de DPC++, conseqüentemente aumentando sua confiabilidade [PIL97].
A função do objeto Diretório pode ser dividida em três partes distintas, conforme mostra a figura 5. O autômato finito definido na figura descreve a primeira função do objeto Diretório como sendo a criação do seu objeto backup. Na primeira vez que o Diretório é criado, o ambiente DPC++ deverá também criar o mesmo objeto Diretório em outro nodo da rede, que funcionará como um objeto Diretório backup.

A segunda parte é caracterizada pelo fato do Diretório primário estar funcionando perfeitamente, por isso este estado é considerado como estado final do autômato finito. A função descrita na terceira parte é a restauração do Diretório primário. No momento da execução, a situação de falha é assumida pelo objeto distribuído quando este faz uma requisição ao Diretório primário e não obtém resposta dentro de um tempo limite. A próxima ação a ser realizada pelo objeto distribuído é fazer a mesma requisição ao Diretório backup, que, concluindo que o primário falhou, assume a função de primário e cria um novo Diretório backup em outro nodo da rede.
Uma vez assumindo como Diretório primário, uma mensagem é enviada para todos os objetos distribuídos contendo os novos identificadores do Diretório primário e do Diretório backup. O modelo final de replicação do Diretório a ser utilizado no DPC++ é ilustrado na figura 6.

No modelo apresentado na figura 6, podem
ser distinguidos três momentos nos quais uma falha pode ser detectada.
Estes momentos estão referenciados como
,
e
.
A falha definida em
ocorre antes do Diretório primário enviar a mensagem de atualização
ao Diretório backup. A falha
aparece depois que (ou enquanto) a mensagem de atualização
foi enviada e antes que o Diretório primário receba uma confirmação.
A falha expressa em
ocorre após o objeto distribuído receber a resposta da requisição
feita por ele ao Diretório primário.
Nos dois primeiros casos, o objeto distribuído não receberá a resposta de sua requisição e detectará a falha do Diretório primário. Em conseqüência, ele se comunicará com o Diretório backup, iniciando, assim, o processo de recuperação do Diretório primário. No terceiro caso, a falha é transparente ao objeto distribuído, já que este recebeu a resposta de sua requisição. Esta falha somente será detectada quando algum objeto distribuído tiver que se comunicar com o Diretório primário.
Uma das maneiras de se implementar concorrência entre métodos seria fazer com que cada requisição fosse atendida em um novo thread. Existiria, então, um thread responsável pelo atendimento das requisições, o qual criaria um novo thread para atender cada requisição, liberando-se imediatamente para poder atender outras.
Isto, por si só, já inviabiliza o atual modelo de checkpoints de DPC++, pois ele baseia-se no fato de que cada objeto atende apenas um método de cada vez e, portanto, só haverá troca de mensagens entre o objeto que requisitou o serviço e o objeto que o está atendendo, até que a transação seja concluida, onde então, o objeto que atendeu voltará a estar disponivel e o que requisitou continuará sua execução. Pode-se garantir que, se um objeto falhar, apenas o objeto que o estava requisitando será afetado, ou apenas o que o estava atendendo. Os outros não serão comprometidos, mesmo que venham a requisitar métodos a um objeto que falhou. Neste caso, como visto, este objeto pedirá o seu status ao Diretório, e este lhe retornará o endereço do novo objeto.
Quando se possibilita atender várias requisições ao mesmo tempo, não se tem mais esta propriedade, pois existiriam objetos trocando mensagens com vários outros, através de seus threads, e se este falhar, pode-se ter vários objetos afetados.
Com as modificações propostas para o DPC++, o atual modelo de tolerância a falhas através de checkpoints deve ser cuidadosamente revisto. Adaptá-lo para suportar estas alterações talvez seja inviável, sendo possivelmente melhor partir para estudos de outras técnicas de TF.
O trabalho expôs também as dificuldades, em relação à tolerância a falhas, originadas pelas novas características a serem introduzidas no projeto. Buscando um melhor desempenho global para as aplicações DPC++, o projeto está sendo incorporado de técnicas modernas de programação paralela, porém trazendo como efeito colateral a invalidação do modelo de tolerância a falhas atualmente suportado.
As perspectivas do projeto incluem a completa incorporação do modelo de replicação do objeto Diretório e um estudo aprofundado de viabilidade de alteração no esquema de criação de checkpoints distribuídos, de forma a suportar corretamente o novo modelo funcional baseado em multithreading.