Une connaissance de l'Erlang est désormais indispensable : en effet, cette partie du tutoriel est en majorité dédiée à la mise en place du système de hot code swapping.
Notre application va cette fois être un peu plus élaborée. En effet, on va utiliser le hot code swapping pour charger, recharger, et quitter des modules.
Le core
Voyons d'abord le code :
Code : Erlang 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 | -module(core).
-export([start/0]).
start() ->
io:format("Lancement du core ...~n"),
register(core, spawn(fun() -> loop([]) end)).
loop(Modules) ->
receive
{core, load, {Module, Args}} ->
io:format("Chargement de ~s ...~n", [Module]),
Pid = spawn(fun() -> Module:init(Args) end),
loop([{Module, Pid}|Modules]);
{core, reload, {Module, NewArgs}} ->
io:format("Fermetume de ~s ...~n", [Module]),
{Module, Pid} = lists:keysearch(Module, 1, Modules),
Pid ! quit,
io:format("Chargement de ~s ...~n", [Pid]),
NewPid = spawn(fun() -> Module:init(NewArgs) end),
NewList = lists:keyreplace(Module, 1, Modules, {Module, NewPid}),
loop(NewList);
{core, unload, {Module}} ->
io:format("Fermeture de ~s ...~n", [Module]),
{Module, Pid} = lists:keysearch(Module, 1, Modules),
Pid ! quit,
NewList = lists:keydelete(Module, 1, Modules),
loop(NewList);
{core, quit} ->
%% ici, plutot lists:foreach, car on utilise pas le resultat
lists:foreach(fun({_, Pid}) -> Pid ! quit end, Modules),
io:format("Modules fermés.~n");
{_From, show, String} ->
io:format("~s~n", [String]),
loop(Modules);
{_From, Else, _Args} ->
io:format("Unknow request: ~p.~n", [Else]),
loop(Modules)
end.
|
Celui-ci, bien qu'un peu plus complexe que le précédent reste
très basique. Néanmoins, il illustre bien le principe détaillé plus haut, à savoir : pouvoir charger et recharger des modules à volonté sans avoir à relancer tout le programme.
Vous aurez pu remarquer que les 4 premiers messages, ou du moins les 4 premiers messages
recherchés, sont ceux mettant en jeu une intervention humaine. Parmi ceux-ci, les 2 premiers ont pour but de charger ou recharger des modules, alors que le deux suivants sont destinés à fermer 1 ou la totalité des modules chargés.
Les deux messages suivants servent à la réception des messages des modules, le premier doit tout simplement afficher à l'écran le message envoyé alors que le second... eh bien il doit gérer tous les autres types de requête, mais comme on ne sait pas les traiter, ou plutôt qu'on ne veut pas les traiter dans le cadre de ce tutoriel (la première requête étant là à titre d'exemple), on affiche la requête puis on relance la boucle.
Les Modules
La structure
Si vous avez regardé le code précédent, vous avez pu remarquer une ligne ressemblant à :
Code : Erlang1 | Pid = spawn(fun() -> Module:init(Args) end)
|
Vous connaissez tous (du moins, je l'espère) la fonction
spawn, inutile donc de s'y attarder. Ce qui peut par contre être intéressant, c'est la fonction utilisée pour initialiser le processus :
Module:init ; pour ceux qui auraient la mémoire courte :
Module est la variable contenant le nom du module à charger, on lance donc le process avec la fonction
init du module (comme prévu), en lui passant la liste d'arguments fournis par l'utilisateur.
Vos modules devront donc
obligatoirement contenir une fonction :
init/1. Après il faudra bien entendu une fonction principale récursant jusqu'à ce qu'on ferme le module, cette fonction devra servir à recevoir les messages du core.
Ensuite, libre à vous de créer les fonctions que vous voulez, c'est votre programme après tout.
La communication
Là aussi, un protocole strict devrait être respecté, du moins, ça permet d'éviter les problèmes. Ce qu'il faut, c'est que tous les messages envoyés par ou aux modules passent par le core. Pour faciliter cela, vous avez pu voir dans le code du core qu'à la création du process nous avons utilisé la fonction :
register/2. Pour ceux qui ne connaîtraient pas encore cette fonction, elle associe un atome à un pid. Le premier argument est l'atome, le second un Pid ; ici, vous pouvez voir une fonction,
spawn mais que renvoie cette fonction ? Eh oui : un Pid ! Une fois ceci fait, lorsque vous écrirez cet atome, cela fera référence au Pid qui lui est associé ainsi :
core ! X enverra le contenu de X au core.
Ici le core est très basique, il n'envoie pas de message à ses modules à l'exception de
quit ; pour que vos modules lui envoient des messages, il faudra donc qu'il fasse les choses par eux-mêmes, sans attendre les instructions du core. En réalité, il est très rare que cela se passe ainsi, le core envoie la plupart du temps des messages à ses modules, il arrive néanmoins que les modules doivent (comme ici) entreprendre des actions sans que le core le leur dise, la méthode la plus simple pour cela est d'utiliser un timer.