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.

Le client retrouve sa réponse grâce à la référence de sa requête

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.

Run in LivebookTélécharger le .livemd