Correcção do teste

Exame - 17 de Julho de 2000

OC98-99 (testes)


Condições gerais da prova

Índice


Questões

1. Desenvolva um módulo Erlang que implemente um sistema distribuído respeitante ao diagrma seguinte. Implemente os processos inicio/?, multi/?, final/?, g/? e f/?.

Resposta:

-module(pergunta_1).
-export([start/0,inicio/0,multi/1,g_f/1,u/1,final/0]).
start() ->
	spawn(?MODULE,inicio,[]).
inicio() ->
	P_final=spawn(?MODULE,final,[]),
	P_u=spawn(?MODULE,u,[P_final]),
	P_multi=spawn(?MODULE,multi,[P_final]),
	loop(P_u,P_multi).
loop(P_u,P_multi) ->
	receive
		{A,B,C} -> ok
	end,
	P_multi ! {A,C},
	P_u ! {B},
	loop(P_u,P_multi).
%
% apenas para que nao gere erros de compilacao
%
u(A) ->
	receive
		B -> B
	end,
	u(A).
multi(P_final) ->
	receive
		{A,C} -> ok
	end,
	Lista_sub=lanca_subs(length(A)),
	envia_valores_sub(A,C,Lista_sub),
	G=recebe_valores_sub(Lista_sub),
	P_final!G.
final() ->
	receive
		G -> ok
	end,
	receive
		U -> ok
	end,
	Lista_sub=lanca_subs(length(G)),
	envia_valores_sub(G,U,Lista_sub),
	F=recebe_valores_sub(Lista_sub),
	io:format("Resultado final: ~w.~n",[F]).
lanca_subs(N) ->
	lanca_subs(N,[]).
lanca_subs(0,Lista) ->
	Lista;
lanca_subs(N,Lista) ->
	Pid_novo=spawn(?MODULE,g_f,[self()]),
	lanca_subs(N-1,[Pid_novo|Lista]).
envia_valores_sub([],_Multi_2,[]) ->
	ok;
envia_valores_sub([Multi_1|Lista],Multi_2,[Pid|Lista_sub]) ->
	Pid ! {Multi_1,Multi_2},
	envia_valores_sub(Lista,Multi_2,Lista_sub).
recebe_valores_sub(Lista_sub) ->
	recebe_valores_sub(Lista_sub,[]).
recebe_valores_sub([],Lista_res) ->
	lists:reverse(Lista_res);
recebe_valores_sub([Pid|Lista_sub],Lista_res) ->
	receive
		{Pid,Res} -> ok
	end,
	recebe_valores_sub(Lista_sub,[Res|Lista_res]).
g_f(Parent) ->
	receive
		{Mult_1,Mult_2} -> ok
	end,
	Parent ! Mult_1*Mult_2.
Índice

2. Imagine um sistema composto por vários processos, cada qual monitoriza um sensor. Estes sensores devem estar sempre operacionais, mas por vezes ocorrem falhas que devem ser prontamente resolvidas, pelo que os processos que os monitorizam devem estar constantemente operacionais. Sempre que o sensor avaria, emite uma mensagem de erro: {‘EXIT’,Sensor_pid,Razao}, mas quando a razão é ‘fatal’, a intervenção humana é necessária, pelo que o processo monitor deve reportar o facto (ao seu superior) e ele próprio terminar. Nos outros casos, o monitor deverá reinicializar o sensor, enviado-lhe uma mensagem {‘CONTROL’,reset}.
Pretende-se desenvolver um sistema tolerante a falhas que salvo a excepção mencionada, garanta que os sensores e os monitores estão sempre activos.

a) Apresente um diagrama referente ao contexto descrito;
b) Implemente o correspondente módulo Erlang.

Resposta:

a)

b)

-module(pergunta_2).
-export([master/0,monitor/2]).
master() ->
	process_flag(trap_exit,true),
	loop([]).
loop(Monitores) ->
	receive
		{monitor,Sensor} ->
			Monitores_novo=lanca_monitor(Monitores,Sensor),
			loop(Monitores_novo);
		{'EXIT',Pid,fatal} ->
			Monitores_novo=trata_razao_fatal(Monitores,Pid),
			loop(Monitores_novo);
		{'EXIT',Pid,_Razao} ->
			Monitores_novo=trata_razao_outra(Monitores,Pid),
			loop(Monitores_novo)
	end.
monitor(Sensor,Master) ->
	receive
		{'EXIT',Sensor,fatal} ->
			exit(fatal);
		{'EXIT',Sensor,_Razao} ->
			Sensor ! {'CONTROL',reset}
	end,
	monitor(Sensor,Master).
trata_razao_fatal(Monitores,Pid) ->
	case [X || X<-Monitores,element(1,X)==Pid] of
		[{Pid,Sensor}] ->
			io:format("Intervenção humana necessária no sensor ~w.~n",[Sensor]),
			retira_da_lista(Monitores,Pid);
		_ ->
			Monitores
	end.
trata_razao_outra(Monitores,Pid) ->
	case [X || X<-Monitores,element(1,X)==Pid] of
		[{Pid,Sensor}] ->
			Monitores_aux=retira_da_lista(Monitores,Pid),
			lanca_monitor(Monitores_aux,Sensor);
		_ ->
			Monitores
	end.
lanca_monitor(Monitores,Sensor) ->
	Pid=spawn_link(?MODULE,monitor,[Sensor,self()]),
	[{Pid,Sensor}|Monitores].
retira_da_lista(Monitores,Pid) ->
	[X || X<-Monitores,X=/=Pid].

Índice  

3. Implemente um servidor de reservatórios de água. O servidor de estar registado com o nome servidor. O servidor deve receber as seguintes mensagens:

{PID,depositar, Nº_de_Litros} à devolve {ok,deposito,Nº_de_Litros}
{PID,levantar, Nº_de_Litros} à devolve {ok,levantamento,Nº_de_Litros_Levantados}

Quando recebe a mensagem de depósito deve criar processos “reservatório” com uma capacidade constante (200 L).
Um processo reservatório recebe as seguintes mensagens com o seguinte comportamento:

{por,N_litros} à devolve {por,N_Litros_a_Mais_que_Não_cabem_no Reservatório}
{tirar,N_Litros} à devolve {tirar,N_Litros_Tirados_Efectivamente}
{ver} à devolve {ver,N_Litros_existentes}
{fim} à devolve {ok} e termina o porcesso.

Um reservatório vazio não tem razão de existir, e o seu processo deve ser terminado.

Resposta:

-module(pergunta_3).
-export([start/0,servidor/0,reservatorio/0]).
-define(CAPACIDADE,200).
start() ->
	spawn(?MODULE,servidor,[]).
servidor() ->
	register(servidor,self()),
	loop([]).
loop(Reservatorios) ->
	receive
		{PID,depositar,N_de_Litros} ->
			Reservatorios_novo=deposita(Reservatorios,N_de_Litros),
			PID ! {ok,deposito,N_de_Litros},
			loop(Reservatorios_novo);
		{PID,levantar,N_de_Litros} ->
			{Litros_nao_levantados,Reservatorios_novo}=levanta(Reservatorios,N_de_Litros),
			N_de_Litros_Levantados=N_de_Litros-Litros_nao_levantados,
			PID ! {ok,levantamento,N_de_Litros_Levantados},
			loop(Reservatorios_novo);
		Msg ->
			io:format("Msg. received: ~w.~n",[Msg]),
			loop(Reservatorios)
	end.
levanta([],N_de_Litros) ->
	{N_de_Litros,[]};
levanta(Reservatorios,0) ->
	{0,Reservatorios};
levanta([{PID,Litros}|Reservatorios],N_de_Litros) ->
	case envia_levantamento(PID,N_de_Litros) of
		Litros ->
			termina_reservatorio(PID),
			levanta(Reservatorios,N_de_Litros-Litros);
		N_de_Litros ->
			levanta([{PID,Litros-N_de_Litros}|Reservatorios],0);
		_Else ->
			exit(erro_reservatorio)
	end.
envia_levantamento(PID,Litros) ->
	PID ! {tirar,Litros},
	receive
		{tirar,Litros_retirados} ->
			Litros_retirados
	end.
deposita(Reservatorios,N_de_Litros) ->
	case [X||X<-Reservatorios,element(2,X)<?CAPACIDADE] of
		[{PID,Litros}] ->
			{Resto,Reservatorios_aux}=completa_reservatorio(PID,Litros,Reservatorios,N_de_Litros);
		[] ->
			Resto=N_de_Litros,
			Reservatorios_aux = Reservatorios
	end,
	cria_reservatorios(Reservatorios_aux,Resto).
completa_reservatorio(PID,Litros,Reservatorios,N_de_Litros) ->
	Falta=?CAPACIDADE-Litros,
	if
		Falta > N_de_Litros ->
			A_Depositar = N_de_Litros;
		true ->
			A_Depositar = Falta
	end,
	Resto=N_de_Litros-A_Depositar,
	envia_deposito(PID,A_Depositar),
	Lista_completos=[X||X<-Reservatorios,element(2,X)==?CAPACIDADE],
	Reservatorio_actualizado={PID,Litros+A_Depositar},
	{Resto,[Reservatorio_actualizado|Lista_completos]}.
cria_reservatorios(Reservatorios,Litros) when Litros=<0 ->
	Reservatorios;
cria_reservatorios(Reservatorios,Litros) when Litros=<?CAPACIDADE ->
	PID=spawn(?MODULE,reservatorio,[]),
	envia_deposito(PID,Litros),
	[{PID,Litros}|Reservatorios];
cria_reservatorios(Reservatorios,Litros) ->
	PID=spawn(?MODULE,reservatorio,[]),
	envia_deposito(PID,?CAPACIDADE),
	cria_reservatorios([{PID,?CAPACIDADE}|Reservatorios],Litros-?CAPACIDADE).
envia_deposito(PID,Litros) ->
	PID ! {por,Litros},
	receive
		{por,0} ->
			ok;
		{por,N_Litros_a_Mais_que_Nao_cabem_no_Reservatorio} ->
			io:format("Erro: reservatorio ~w deveria permitir este deposito: ~w.~n",[PID,?CAPACIDADE]),
			exit(erro_reservatorio)
	end.
termina_reservatorio(PID) ->
	PID ! {fim},
	receive
		{ok} -> ok
	end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% RESERVATORIO
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
reservatorio() ->
	loop_reservatorio(0).
loop_reservatorio(Litros) ->
	receive
		{por,Litros_a_depositar} ->
			Litros_novo=deposita_litros(Litros,Litros_a_depositar),
			loop_reservatorio(Litros_novo);
		{tirar,Litros_a_retirar} ->
			Litros_novo=retira_litros(Litros,Litros_a_retirar),
			loop_reservatorio(Litros_novo);
		{ver} ->
			servidor!{ver,Litros},
			loop_reservatorio(Litros);
		{fim} ->
			servidor ! {ok}
	end.
deposita_litros(Litros,Litros_a_depositar) ->
	Litros_livres=?CAPACIDADE-Litros,
	if
		Litros_livres>=Litros_a_depositar ->
			Litros_depositados=Litros_a_depositar;
		true ->
			Litros_depositados=Litros_livres
	end,
	Litros_nao_depositados=Litros_a_depositar-Litros_depositados,
	servidor ! {por,Litros_nao_depositados},
	Litros_depositados+Litros.
retira_litros(Litros,Litros_a_retirar) when Litros>=Litros_a_retirar ->
	servidor ! {tirar,Litros_a_retirar},
	Litros-Litros_a_retirar;
retira_litros(Litros,Litros_a_retirar) when Litros<Litros_a_retirar ->
	servidor ! {tirar,Litros},
	0.
Índice

Última actualização: 07-12-2004

OC98-99 (testes)