Tratamento de erros em tempo de execução

» Slides sobre tratamento de erros

Erros de sintaxe, bem como alguns erros de semântica, podem ser detectados pelo compilador, mas os programas podem ainda conter erros lógicos. Muitos destes erros apenas são detectados durante a execução. Por exemplo:

O Erlang fornece um mecanismo de detecção e tratamento de erros, oferecendo aos programadores primitivas para a construção de sistemas robustos e tolerantes a falhas.

As primitivas catch e throw fornecem um mecanismo para tratamento de excepções na avaliação de uma expressão. Podem ser usadas para:

A primitiva catch é usada da seguinte forma:

catch Expressão

Se não existir nenhuma falha durante a avaliação da expressão é retornado o valor da expressão, tal como se não fosse usado o catch. No entanto, se ocorrer algum erro na avaliação da expressão é retornado o tuplo {'EXIT',Razão}, onde Razão é um átomo que indica o que correu mal.

Exemplo 1

> catch 1+3.
> 4

> catch 1+a.
> {'EXIT',{badarith,[{erl_eval,eval_op,3},
{erl_eval,expr,3},
{erl_eval,exprs,4},
{shell,eval_loop,2}]}}

Nota: Para observar a diferença entre usar ou não o catch num processo execute os seguintes passos na shell do Erlang

1> self().
<0.30.0>
2> 1 + a.

=ERROR REPORT==== 30-Mar-2006::13:10:33 ===
Error in process <0.30.0> with exit value: {badarith,[{erl_eval,eval_op,3},{shell,exprs,6},{shell,eval_loop,3}]}

** exited: {badarith,[{erl_eval,eval_op,3},
                      {shell,exprs,6},
                      {shell,eval_loop,3}]} **
3> self().
<0.33.0>

Como pode verificar o processo da shell terminou devido ao erro gerado por tentar avaliar "1 + a". No entanto, continua a existir interacção com o utilizador porque o processo foi automaticamente recriado. Abordaremos este assunto nas próximas aulas.

4> catch 1 + a.
{'EXIT',{badarith,[{erl_eval,eval_op,3},
                   {erl_eval,expr,5},
                   {shell,exprs,6},
                   {shell,eval_loop,3}]}}
5> self().
<0.33.0>

Agora o processo não terminou, uma vez que o erro foi correctamente detectado e tratado.

Exemplo 2

somar(A,B) -> verifica(catch A + B).

verifica({'EXIT',Why}) -> {erro,Why};
verifica(X) -> X.


A primitiva throw fornece um mecanismo para retornar um valor a um catch associado. A excepção propaga-se até ser encontrado um catch, mesmo que seja lançada vários níveis abaixo. Sempre que é usado um throw tem de existir um catch, caso contrário ocorre um erro de execução (que é precisamente o que pretendemos evitar!!!).

A primitiva throw é usada da seguinte forma:

throw( Excepção )

Exemplo 3

test() ->
    catch test1(),
    'processo nao morreu'.

test1() ->
    io:format("in test1...~n"),
    test2(),
    .....
    .....

test2() ->
    io:format("in test2...~n"),
    throw('erro grave').

O processo não termina mesmo que tenha sido lançada uma excepção, uma vez que existe um catch associado. Experimente retirar o catch e observe as diferenças.

Exemplo 4

somar(A,B) ->
    catch calc_soma(A,B).

calc_soma(A,B) when number(A),number(B) ->
    A + B;
calc_soma(A,B) ->
    throw({erro,'operandos invalidos'}).

Se tentarmos somar outros elementos que não dois números é lançada a excepção {erro,'operandos invalidos'} e não um erro de execução.


Exercícios

1) Crie um processo que simule uma máquina de calcular que permita executar as operações somar, subtrair, multiplicar e dividir. Este processo deve devolver uma das respostas:

O cliente deverá usar a função:

Esta função envia para o processo servidor os parâmetros de entrada e aguarda uma resposta. Se o servidor não responder dentre de 5 segundos a função deve terminar, avisando o utilizador da ocorrência.


2) Implemente um sistema composto por um servidor e n processos que simulam sensores de temperatura em diversos pontos de um edifício. De 5 em 5 segundos cada um dos sensores envia ao servidor o valor da temperatura obtida ou a indicação 'erro de leitura' (exemplo de código para geração de valores aleatórios).

O servidor deve manter uma lista actualizada das temperaturas de cada um dos sensores.

Implemente um cliente que possa efectuar os seguintes pedidos ao servidor:

Sugestão: usar as funções do módulo lists, lists:nth(N,Lista) e lists:sum(Lista).

Pretende-se que implemente um sistema robusto.