Jusqu’ici, nous avons passé un état d’une fonction à la suivante. Comment un programme peut-il conserver cet état et répondre à plusieurs interlocuteurs ? Nous allons donner notre compteur à un processus et lui envoyer des messages.
Avant de venir
Suivez la page Pré cours 6 : Elixir dans le terminal, Mix et
l’éditeur doivent fonctionner avant la séance. Gardez également Livebook :
le carnet ap6-processus.livemd reste notre support principal aujourd’hui.
Un processus attend des messages
Un processus de la BEAM est une unité d’exécution gérée par la machine
virtuelle d’Erlang, sur laquelle tourne aussi Elixir. Il a un identifiant, le
PID, qui sert d’adresse pour lui écrire, et une boîte aux lettres, c-à-d.
la file des messages qu’il a reçus et pas encore lus. Ce n’est pas un
processus du système d’exploitation : la VM en fait tourner des milliers.
Nous commencerons par faire un aller-retour entre deux processus, avec
spawn, qui lance une fonction dans un nouveau processus, send, qui dépose
un message dans une boîte, et receive, qui en retire un de la nôtre.
Nous changerons ensuite les motifs de receive : un message qui ne correspond
pas reste dans la boîte aux lettres. Enfin, une référence créée par make_ref
permettra d’associer chaque réponse à sa requête, comme sur le schéma
ci-dessous.
Sur le schéma, le client, à gauche, crée une référence avec make_ref() puis
envoie au compteur, à droite, le message {:get, client, ref} : il y met son
propre PID, pour que le compteur sache à qui répondre, et la référence, pour
reconnaître la réponse. Le compteur, dont l’état vaut 3, reçoit ce message,
répond {:result, ref, 3} et se remet en attente avec loop. Le client,
lui, n’accepte que le message qui porte sa référence, grâce au motif ^ref :
une autre réponse arrivée entre-temps resterait dans sa boîte. Notez enfin que
send n’a pas attendu la réponse ; c’est le receive du client qui attend.
Dans Livebook, les expériences d’envoi et de réception restent dans la même
cellule pour viser le bon processus.
L’état est l’argument de la prochaine boucle
Notre compteur attend un message, calcule une nouvelle valeur, puis se rappelle avec cette valeur. Les données restent immuables ; c’est l’exécution du processus qui continue, exactement comme un accumulateur dont la prochaine donnée viendrait de la boîte aux lettres. Vous écrirez les transitions et vérifierez que deux compteurs restent indépendants.
Que reste-t-il quand le processus s’arrête ? Nous observerons sa fin avec un
monitor, c-à-d. une observation qui nous envoie un message :DOWN quand le
processus se termine, puis découvrirons le rôle d’un superviseur, un
processus chargé de démarrer d’autres processus et de les relancer quand ils
tombent. Pouvez-vous deviner quelle valeur affiche un compteur relancé ? Un
processus redémarré repart de son état initial : redémarrer ne restaure pas
une mémoire perdue.
Sortir de Livebook en fin de séance
Nous créerons process_lab avec mix new process_lab --sup, puis ouvrirons le
dossier dans l’éditeur. Le carnet donne le petit enfant Agent à placer sous
le superviseur et les commandes pour le retrouver dans iex -S mix.
Cette application est écrite en Elixir et utilise le modèle d’application Erlang/OTP. Mix organise les fichiers, compile et lance les tests ; OTP organise le démarrage et la supervision. En AP7, nous écrirons notre propre serveur dans ce même cadre.
Pour relire une primitive, cf. le guide des processus et la documentation de Supervisor. Le chapitre 9 du poly reprend processus et messages quand un bout de syntaxe vous manque.
Ouvrir les ateliers
Processus, messages et état
Faites dialoguer des processus, écrivez un compteur avec une boucle récursive et observez ce qui disparaît quand son processus s’arrête.
Slides 1 à 34Elixir : spawn, self, send, receive, make_ref, Process.monitor · poly, chap. 9 · supervision et Mix en fin de carnet.
