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:
calcular(Operando1,Operacao,Operando2)
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:
Pretende-se que implemente um sistema robusto.