Applikationen ska ha ett grafiskt interface. För detta använder ni
Erlangs portabla grafiksystem gs
som finns
dokumenterat i online-manualerna. Interfacet behöver inte vara mer
avancerat än ni själva vill ha det.
Kapitel 11.6 i Erlang-boken beskriver ett "fuskigt" sätt att implementera processgrupper, där total meddelandeordning (abcast) garanteras genom att alla meddelanden går genom gruppledaren. (Nackdelen är förstås att man får en flaskhals i systemet.) Erlangs system med processlänkar och trap_exit används för att detektera fel hos andra processer. Ni ska använda denna metod, med vissa modifieringar, enligt följande:
groups
, som ska
exportera följande funktioner:
create() -> Proxy
Proxy = pid()
Startar en ny grupp och returnerar ett process-id
Proxy
som fungerar som "handle" för
medlemskapet i gruppen. (Se nedan om proxies.)
join(Pid) -> Proxy
Pid = pid()
Proxy = pid()
Går med i gruppen som representeras av Pid
,
och returnerar en ny "handle"; jämför med create/0
.
(Pid
fås från ett tidigare anrop till
create/0
eller join/1
.)
leave(Pid) -> ok
Pid = pid()
Pid
lämnar gruppen den är med i och
terminerar. Funktionen returnerar alltid ok
.
När alla deltagare har lämnat en grupp ska gruppen försvinna
av sig själv. (Det ska inte finnas några kvarglömda
extraprocesser som lever vidare i evighet.)
cast(Pid, Message) -> ok
Pid = pid()
Message = term()
Multicastar meddelandet Message
till gruppen
som representeras av Pid
. Funktionen returnerar
alltid ok
. Meddelandet skickas endast ut om
processen som anropar cast
är densamma som
skapade Pid
.
create
eller join
) ska den inte
behöva hantera gruppens interna meddelanden själv, utan den får
automatiskt en proxy-process (på samma nod som den själv) som
sköter om "snacket med gruppen" och bara skickar vidare de
faktiska meddelandena som sänts med multicasts.
Exempel:
Proxyns Pid fungerar alltså både som "medlemsnummer" och som "handle" till gruppen. Proxyn ska vara länkad till applikationsprocessen så att om den senare kraschar ska också proxyn göra det. Den minns förstås också Pid:en för sin förälder, så den kan leverera meddelandena från gruppen.
Det första en ny proxy måste göra då den startats (vid
join
) är att vänta på ett meddelande från
ledarprocessen som säger ungefär "välkommen - det är jag som är
gruppledaren, och det här är medlemslistan". Om inget sådant
meddelande kommer inom en viss tid (maximalt ett par sekunder),
så ska proxyn självdö genom att göra
exit(disconnected)
.
Om det kan hända att proxyn får reda på att den har blivit
utesluten ur gruppen (av någon anledning) så ska den också göra
exit(disconnected)
.
Applikationsprocessen ska fånga felsignaler, och om den ser att dess proxy har dött, kan den agera på lämpligt sätt (till exempel rapportera detta till användaren).
För att gå med i en existerande grupp gäller det alltså att
på ett eller annat sätt hitta en sådan proxy som man kan prata
med. Notera att man kan göra join
genom vilken
proxy-Pid som helst, men man kan bara göra cast
genom
sin egen.
Observera! Erlang-boken påstår här att man inte kan jämföra Pid:ar på olika noder, utan måste jämföra nodnamnen för att vara säker på att ordningen blir densamma hos alla. Detta stämmer inte! (Det kan ha stämt en gång i tiden, men inte längre.) Ansvaret för att fixa en ny gruppledare kan alltså läggas på den proxy vars Pid är störst eller minst av alla i gruppen. (Då får man inte heller några problem att ha flera gruppmedlemmar på en och samma nod.)
För att klara av problemet med meddelanden som inte har gått ut till alla på grund av att gruppledaren har kraschat mitt i en utsändning, så ska alla medlemmar (deras proxies) buffra de senaste 10 (ca.) meddelandena, och om en ny gruppledare väljs ska alla först av allt skicka kopior av samtliga buffrade meddelanden till den nya gruppledaren, så att den är så mycket up-to-date som möjligt. När den nya ledaren har fått denna information kan den i sin tur hantera andra medlemmars frågor om saknade meddelanden.
register/4
lägger till en ny Pid till mängden
för ett visst namn, om det inte redan fanns med. (Om det
redan fanns, så ignoreras
Description
-parametern.)
lookup/2
returnerar {ok, PidList}
unregister/2
tar bort hela namnet och listan,
medan unregister(Server, Name, Pid)
är en ny
funktion som bara tar bort en viss Pid. Om mängden Pid:ar
för ett visst namn blir tom, så tas namnet bort
automatiskt.
Ni kan sedan använda denna namnserver från era chat-applikationer på följande vis:
join
. Sedan
registrerar ni er nya proxy-Pid under samma gruppnamn.