• Aucun résultat trouvé

3.2 Exemples d’utilisation des fonctions crochets

3.2.5 Autoriser une action après un délai

Lors d’une action sur la porte INSERER, un cadenas va ainsi récupérer la clé issue de l’environnement dans sa variable “cle” utilisée en réception en deuxième offre.

3.2.5 Autoriser une action après un délai

La capacité à limiter les actions possibles peut être employée pour retarder la réalisation d’une action, et ainsi offrir un mécanisme de délai (timeout) dans l’implémentation générée. Nous présentons comment mettre en place une action retardée sur la porte DELAI de notre exemple.

Le crochet de pré-négociation est utilisé pour interdire une action sur la porte DELAI pendant une certaine durée, ici trois secondes. Pour cela, le crochet stocke la date de début d’attente de chaque cadenas dans un tableau en variable globale, et autorise le lancement d’une négociation uniquement lorsque le cadenas concerné attend depuis au moins trois secondes. Cependant, un cadenas peut être prêt sur la porte DELAI, puis effectuer une autre action et être prêt à nouveau sur cette porte, le tout en moins de trois secondes. C’est ici que l’information sur les réceptions de messages “ready” par la porte est utile : le crochet peut savoir si un cadenas s’est à nouveau manifesté comme prêt, auquel cas la date de début d’attente est mise à jour.

Étant donné qu’une action sur la porte DELAI ne concerne qu’un seul cadenas à la fois, le vecteur de synchronisation ne contient qu’un seul identifiant de tâche, qui correspond au cadenas visé par la négociation. Le crochet de pré-négociation récupère ainsi cet identifiant de tâche via le vecteur de synchronisation, met à jour le début d’attente si ce cadenas est nouvellement prêt, et autorise le démarrage d’une négociation si le cadenas attend depuis plus de trois secondes.

#define DELAI_ATTENTE 3

// Dates de début d’attente, 0 == jamais commencé d’attendre time_t debut[DLC_SPEC_MAX_TASK];

bool DLC_DELAI_PRE_NEGOTIATION (DLC_ACTION *action, DLC_HOOK_INFO *hookinfo) {

DLC_NAT cadenas; // identifiant de tâche du cadenas concerné par l’action time_t duree_attente;

3.2. Exemples d’utilisation des fonctions crochets 95 // correspond au cadenas visé pour une action sur la porte DELAI

assert (hookinfo->vectsize == 1); cadenas = hookinfo->vect[0];

// Mise à jour date de début d’attente if (hookinfo->newrdy[cadenas]) {

debut[cadenas] = time(); } else {

// un cadenas qui ne vient pas d’envoyer un message "ready" // est forcément déjà en train d’attendre

assert (debut[cadenas] != 0); }

// Autorise une négociation si le cadenas attend depuis suffisamment longtemps duree_attente = time() - debut[cadenas];

return duree_attente >= DELAI_ATTENTE; }

Entre le moment où le crochet de pré-négociation autorise le démarrage d’une négociation pour un cadenas et le moment où la demande de confirmation de cette négociation atteint la porte, il se peut qu’un nouveau message “ready” de la part de ce cadenas soit reçu par la porte. En conséquence, le crochet de post-négociation doit lui aussi vérifier que le cadenas concerné par l’action ne s’est pas manifesté comme de nouveau prêt durant la négociation. En d’autres termes, le comportement du crochet de post-négociation est en fait identique à celui de pré-négociation.

bool DLC_DELAI_POST_NEGOTIATION (DLC_ACTION *action, DLC_HOOK_INFO *hookinfo) {

return DLC_DELAI_PRE_NEGOTIATION (action, hookinfo); }

Les fonctions crochets peuvent ainsi être utilisées pour implémenter un mécanisme de délai de réalisation d’action. Cependant, une fois le délai passé, il n’est pas garanti que l’action sur la porte DELAI ait lieu, car le cadenas a encore une chance de participer à une action sur la porte OUVRIR. De plus, le mécanisme de délai de réalisation d’action ne permet pas d’être précis, et son utilisation est inappropriée pour des systèmes avec de fortes contraintes temporelles. En effet, la mesure de durée est effectuée au niveau de la porte, et cette mesure démarre à partir du moment où la porte reçoit un message “ready” de la part du cadenas. Les délais résultants des transmissions de messages sur le réseau impactent donc la durée d’attente réelle.

Remarque 3-2

Lorsqu’un crochet de pré-négociation retourne faux, la porte relève d’éventuels nouveaux messages en réception, puis consulte les tâches prêtes pour détecter une action possible et appelle à nouveau la fonction crochet de pré-négociation pour la nouvelle action visée. Le mécanisme de délai mis en place au niveau du crochet de pré-négociation revient donc à une forme d’attente active, ce qui n’est pas une solution optimale.

De manière générale, nous pensons que le mécanisme de délai devrait être implémenté direc-tement au niveau de la tâche. Nous avons envisagé d’ajouter un crochet pré-négociation au niveau de la tâche, puis nous nous sommes ravisés afin de minimiser le nombre de fonctions crochets, mais nous ne considérons pas la question des délais comme définitivement réglée. Pour alimenter de futurs développements sur ce point, on note qu’il existe des propositions d’ajout de notions de temps directement au sein du langage de spécification, notamment

Chapitre 4

Génération automatique

d’implémentation distribuée

Eventually, I decided that thinking was not getting me very far and it was time to try building.

Rob Pike

The Text Editor sam

Ce chapitre présente la manière dont nous procédons en pratique pour générer automati-quement une implémentation distribuée à partir d’une spécification LNT. En partant de la composition parallèle des processus tâches en LNT et des fonctions crochets en C, on génère plusieurs programmes C qui implémentent le comportement des tâches, le protocole de synchronisation et les appels aux fonctions crochets.

La figure 4.1 offre une vue d’ensemble de la génération d’implémentation, qui comprend plusieurs étapes réalisées automatiquement par DLC, en faisant plusieurs fois appel à des outils ou des bibliothèques de CADP. Sur le haut de la figure, on retrouve les entrées de DLC, c’est-à-dire la spécification LNT d’un système distribué formé d’une composition parallèle de tâches, et d’éventuelles fonctions crochets. Au bas de la figure est représentée l’implémentation distribuée, constituée de programmes tâches, de programmes portes, et d’un programme “nœud central” en charge de déployer le système. Au centre, les différentes phases de génération de code sont représentées, ainsi que la section dans laquelle chacune est traitée.

On commence par décrire la bibliothèque de support pour la programmation distribuée. Le flot de compilation d’une tâche est ensuite exposé, suivi par l’implémentation du protocole. On présente ensuite la bibliothèque qui représente les attributs de la spécification. Enfin, une dernière section couvre les limitations actuelles de DLC.

C LNT LNT C C C C C C

EXE EXE EXE

implémentation distribuée extraction

attributs

DLC

compilateur C compilateur C compilateur C fonctions spécification LNT tâche porte manager NETWORK CÆSAR_ porte central nœud section 4.3 section 4.1 section 4.3 section 4.1

DLC_ SPECIFICATION section 4.4 tâche génération interface manager section 4.2 || ... || tâche tâche bibliothèque générique bibliothèque produite à partir de la spécification

centralnœud EXEC/CÆSAR

crochet

Figure 4.1 – Vue d’ensemble de la génération d’implémentation distribuée

4.1 La bibliothèque CÆSAR_NETWORK

DLC génère des implémentations qui utilisent la bibliothèque C “CÆSAR_NETWORK”, développée au sein de l’équipe CONVECS. Cette bibliothèque offre notamment des pri-mitives de communication, de déploiement et un mécanisme d’arrêt d’urgence qui sont très utiles pour le développement de programmes distribués. Elle est employée au sein des outils distribués de l’équipe, comme par exemple le générateur d’espace d’états DIS-TRIBUTOR [GMS13]. On donne ici une vue d’ensemble des primitives de la bibliothèque utilisées dans le cadre de DLC.

Nœuds. Un système distribué est composé de plusieurs programmes, qui sont appelés desnœuds. Chaque nœud possède un identifiant unique, qui est un nombre naturel. Le premier programme à être lancé est le nœudcentral, dont l’identifiant est toujours égal à zéro.

Déploiement. La bibliothèque permet d’automatiser le déploiement des nœuds sur une ou plusieurs machines. A son démarrage, le nœud central lit un fichier de confi-guration qui indique sur quelles machines le système doit être déployé, chaque ma-chine pouvant héberger un ou plusieurs nœuds. Le nœud central se charge alors de copier les différents exécutables du système sur les machines indiquées et de lancer leur exécution. Il suffit donc de démarrer le nœud central pour déployer le système distribué sur une grille de machines. La bibliothèque utilise des utilitaires classiques

4.1. La bibliothèque CÆSAR_NETWORK 99 tels que rsh ou ssh pour accéder aux machines distantes, qui sont identifiées par leur nom DNS. Le fichier de configuration est en texte brut avec une syntaxe simple, ce qui permet de l’éditer facilement ou de le générer automatiquement. La configura-tion de déploiement peut donc être facilement modifiée entre différentes exécuconfigura-tions, sans avoir à recompiler le système.

Communication. La bibliothèque offre des primitives de communication entre nœuds par passage de messages asynchrones. En pratique, ces communications utilisent des sockets POSIX avec le protocole TCP pour garantir que les messages sont délivrés et qu’ils restent dans le même ordre entre un émetteur et un récepteur. La procédure d’initialisation de la bibliothèque établit une connexion entre toutes les paires de nœuds du système, chaque nœud est donc en mesure de communiquer avec n’importe quel autre.

La bibliothèque CÆSAR_NETWORK offre aussi un service de nommage : chaque nœud est désigné par son identifiant, qui correspond à sa place dans la liste des nœuds du fichier de configuration. Il est donc possible de connaître l’identifiant de chaque nœud en contrôlant l’ordre d’apparition des nœuds dans le fichier de configuration. Cela permet de nommer les nœuds indépendamment des machines sur lesquelles ils seront effectivement déployés. Ce nommage facilite la programmation du système distribué, en décorrélant l’identification des processus distants de la topologie de déploiement effectivement utilisée lors d’une exécution.

Arrêt d’urgence. Un mécanisme d’arrêt d’urgence permet à n’importe quel nœud de demander l’arrêt de tous les nœuds du système. En pratique, une requête d’arrêt d’urgence est transmise au nœud central, qui se charge ensuite de stopper tous les nœuds du système. Lorsqu’un nœud reçoit un signal d’arrêt d’urgence, il appelle une fonction définie par l’utilisateur avant de terminer ; cette fonction permet de préparer l’arrêt du nœud. Le mécanisme d’arrêt d’urgence est notamment utilisé pour stopper tous les nœuds quand l’un d’entre eux lève une erreur à l’exécution.

Outre la simplification de la programmation distribuée, la bibliothèque CÆSAR_NETWORK permet aussi d’isoler la couche de communication dans l’implémentation générée. Nous avons fait le choix de nous concentrer sur le protocole de synchronisation plutôt que sur les techniques d’implémentation efficace de passage de messages entre processus, qui sont un sujet de recherche à part entière. Le passage de messages repose sur les sockets POSIX ; si elles ne sont pas forcément les primitives les plus performantes, notamment entre deux processus déployés sur une même machine, elles ont l’avantage d’être disponibles par défaut sur tous les systèmes d’exploitation couverts par CADP, c’est-à-dire Solaris, Linux, OSX et Windows. Si nécessaire, l’isolement au sein de la bibliothèque CÆSAR_NETWORK de l’implémentation du passage de messages devrait permettre d’améliorer cette implémenta-tion dans le futur sans avoir à modifier le code généré par DLC.