Archive

Posts Tagged ‘pong’

Ping – Pong example in Erlang 1

September 10, 2009 Leave a comment

Una delle prime cose che ho studiato in erlang è stato un semplice caso di ping-pong tra client e server. Cercherò di analizzare con voi il problema e di proporre un paio di soluzioni (in questo post la prima soluzione).

Riassumo brevemente il problema di partenza: realizzare un server, che ricevuto in ingresso un messaggio di ping e un intero N da un client, risponda a tale client con N messaggi di pong.

Ecco come potrebbe essere realizzato il nostro server:

-module(server).
-export([start/0]).

start() ->
    register(?MODULE, spawn_link(fun() -> loop() end)).

loop() ->
    receive
	{_From, ping, 0} ->
	    loop();
	{From, ping, N} ->
	    From ! {self(), pong},
	    self() ! {From, ping, N-1},
	    loop();
	_ ->
	    io:format("Unknown message", []),
	    loop()
    end.

Ok, vediamo come è strutturato il nostro modulo per il server.

Il server ha una funzione si start che  non ha parametri di ingresso. Tale funzione usa le BIF register/2 e spawn_link/1.

Iniziamo spiegando cosa fa spawn_link/1: tale funzione prende in ingresso una funzione, la e la esegue. Inoltre lega tale funzione al processo in cui è stata invocata, in questo modo se tale funzione (che presumibilmente è un processo) termina (con ragione normale o anomala) il processo padre ne viene avvisato è può decidere di adottare una strategia di recupero (cosa che noi non facciamo in questo esempio :) )

La BIF register/2, invece viene utilizzata per assegnare ad un pid (process identifier) un nome simbolico. Nel nostro esempio utilizziamo una combinazione di queste due BIF per eseguire la funzione loop, legarla al processo padre ed assegnare al pid ad essa associato lo stesso nome del modulo (server).

La funzione loop/0 implementa il vero è proprio cuore del nostro server. Tale funzione può essere vista come una sorta di listener che si basa sul costrutto receive.

Ogni processo in erlang ha una sorta di mailbox, dove vengono immagazinati tutti i messaggi che sono stati indirizzati a tale processo. Il costrutto receive permette di recuperare dalla mailbox un messaggio. Una volta che tale messaggio è stato recuperato dalla mailbox, eseguiamo un pattern matching tra tale messaggio e alcuni valori. Se uno di questi valori combacia eseguiamo le istruzioni al suo interno, altrimenti passiamo oltre.

Come potete notare nel codice del metodo loop/0 alla fine di ogni pattern matching il metodo loop viene rieseguito, consentendo una costante lettura dei messaggi nella mailbox.

Credo sia meglio dire un paio di cosette:

1) nonostante il metodo loop venca richiamato, non viene occupata nessuna nuova porzione di memoria, questo e’ uno dei motivi per cui erlang soffre pochissimo (o nulla) memory leaks.

2) come potete notare il nostro receive specifica 3 differenti valori che vengono utilizzati per eseguire il pattern matching con il messaggio ricevuto. Il primo e il secondo sono delle tuple che incapsulano una struttura di messaggio ben precisa: il pid da cui il messaggio e’ stato spedito (che ci permette di capire a chi spedire la risposta), il messaggio vero e proprio e un valore intero (che ci permette di avere un conteggio sui messaggi gia’ inviati). Il terzo valore e’ un “don’t care” ovvero un “va bene tutto”.  In pratica se il  messaggio ricevuto non combacia ne con la prima, ne con la seconda opzione si prova a farlo combaciare con il don’t care, tale operazione restituira’ true per qualsiasi messaggio e quindi le istruzioni al suo interno verranno eseguite. Nonostante tale sistema permetta di evitare errori di pattern matching (in quando se un messaggio non combacia con un almeno un valore un errore e’ generato) e’ altamente sconsigliato utilizzare il don’t care, in quando si suppone che i messaggi spediti dovrebbero essere nella giusta forma, ed eventuali crash in fase di testing permettono di risolvere errori di typing che altrimenti verrebbero ignorati.

Vediamo come eseguire tale codice:

pegaso:Desktop bellerofonte$ erl
Erlang (BEAM) emulator version 5.6.5  [async-threads:0]
[kernel-poll:false]

Eshell V5.6.5  (abort with ^G)
1> server:start().
true
2> server ! {self(), ping, 2}.
{<0.31.0>,ping,2}
3> flush().
Shell got {<0.33.0>,pong}
Shell got {<0.33.0>,pong}
ok
4>

Dopo aver mandato in esecuzione una erlang shell nella cartella dove il file beam del server è presente, eseguiamo il comando server:start() che ci da come risultato true (risultato dell’operazione di registrazione del processo col nome specificato).

In erlang e’ possibile mandare un messaggio ad uno specificio processo utilizzando il simbolo ! .  Se ad esempio vogliamo mandare al server il messaggio {self(), ping, 2} mettiamo alla sinistra del punto esclamativo  l’atom rappresentante il nome del processo a cui vogliamo inviare il messaggio, mentre a destra del punto esclamativo mettiamo il messaggio vero e proprio. Per chi non lo sapesse il metodo self() restituisce il pid del processo in cui tale comando viene eseguito.

A questo punto il server ci dovrebbe aver spedito dei messaggi di risposta, che possiamo recuperare dalla mailbox utilizzando il comando flush(). Potete notare nelle linee seguenti all’invocazione del comando flush() che la shell ha ricevuto due messaggi di pong, esattamente cio’ che ci aspettavamo.

Questo post comincia ad esser un po’ troppo lungo, quindi mi permetto di finire qui per oggi e di proseguire nel prossimo!

Categories: Erlang Tags: , ,
Follow

Get every new post delivered to your Inbox.

Join 27 other followers