• Aucun résultat trouvé

Paradigmes de programmation appliqués à la réconciliation

conciliation

Cette section regroupe quelques notions de programmation utilisées pour l’écriture du lo- giciel de réconciliation. Quelques-unes de ces notions sont peu usuelles, mais sont essentielles au bon fonctionnement de la réconciliation et à la résolution des contraintes introduites par l’utilisation d’un canal de communication. Cette section permet d’attirer le lecteur du code sur les points clé de la structure du programme.

Abstraction de classes

L’abstraction de classes est une caractéristique essentielle de la programmation orientée objet. Elle permet de regrouper plusieurs objets dont les interfaces ou les caractéristiques sont similaires. Par exemple, tous les objets du programme de réconci- liation qui nécessitent un dialogue à travers le canal classique (gestion des blocs à réconcilier, formatage des blocs ou algorithme d’encodage-décodage) ont en commun la nécessité de dis- poser de fonctions capables d’envoyer ou de recevoir des données sur ce canal. Comme il serait inefficace de coder ces mêmes fonctions pour chacun des objets, nous définissons un objet abs-

trait (appelé classe dans les langages C++ ou java), implémentant ces fonctions communes, et

dont dérivent les objets qu’on désire regrouper. On dit que les fonctions de la classe abstraite sont héritées par les classes dérivées. Dans l’exemple de l’utilisation du canal classique, la classe de base se nomme Application .

15.2 Paradigmes de programmation appliqués à la réconciliation 181

Cette structure de classes héritées présente de multiples avantages. Comme nous l’avons vu, elle permet de factoriser du code (c’est-à-dire de l’écrire une fois pour toutes, en vue de diverses utilisations), mais aussi de présenter une interface unifiée d’un ensemble de classes avec le reste du programme. Ainsi, la classe qui gère le canal classique (appelée Node ) sait qu’elle aura affaire à des classes de type Application , sans faire de suppositions sur sa fonc- tion particulière. À l’inverse, la classe Node est elle-même une classe abstraite dont dérivent les classes qui implémentent les diverses variétés de canaux classiques disponibles (voir sec- tion 15.4). L’abstraction de classes permet donc de générer du code plus modulaire, dont les effets de dépendances internes (et donc les bogues) sont limités.

Singletons

Un singleton est une classe unique, universelle à l’ensemble du programme. En programmation orientée objet, une classe définit un type de variable, au même titre que les types entier ou flottant. De cette façon, on peut déclarer plusieurs objets, c’est-à-dire plusieurs instanciations de cette classe. Par exemple, deux objets de type "décodeur LDPC" et deux objets de type "révélation totale" seront nécessaires pour le décodage du code multi-niveaux (section 12.7). Au contraire, certaines classes ont pour vocation de n’être instanciées qu’une seule fois. Par exemple, un seul objet de type Node est nécessaire au bon fonctionnement du programme. Ces classes uniques sont appelées singletons. Elles ont l’avantage de pouvoir être utilisées depuis tout endroit du programme, alors que les objets usuels (nos décodeurs) ne peuvent être utilisés que par leur propriétaire (ici l’objet "décodeur multi-niveaux").

Boucle principale

La boucle principale permet à un programme de patienter en attendant un évé- nement. Un programme classique s’exécute de façon séquentielle, de la première à la der- nière instruction. Après cette dernière, le programme est définitivement interrompu. Notre programme de réconciliation se comporte différemment, en ce qu’il est obligé d’attendre la ré- ponse de son partenaire (un événement réseau) après avoir émis une requête. La partie du code responsable du fait d’attendre en écoutant un événement s’appelle la boucle principale ; son implémentation est de la responsabilité du nœud (classe Node ). Un nœud implémentant le canal classique avec les fonctions standards de programmation client-serveur bas niveau pourra pour ce faire utiliser la fonction réseau read (celle-ci ne répond qu’à réception d’un message). Un nœud utilisant des bibliothèques de programmation de plus haut niveau pourra utiliser la boucle principale fournie. C’est le cas du nœud développé par Cécile Neu, ou des bibliothèques fournies par le projet SECOQC.

Du point de vue de l’application, l’exécution du code se termine quand le programme entre dans se boucle principale, en ce sens qu’il n’y a plus d’instruction à exécuter. L’application sera "réveillée" par l’exécution de sa fonction responsable de recevoir les messages.

Applications

Les applications sont les utilisateurs du canal classique. Nous les avons déjà rencon- trées au cours des exemples des paragraphes précédents. À première vue, une seule application, responsable de l’ensemble du processus de réconciliation semblerait nécessaire. Cependant, il est intéressant de diviser ce processus en plusieurs entités logiques. Ce partage permet d’ob- tenir un programme plus modulaire, et rend le code plus lisible. De plus, la modularisation

182 Chapitre 15 : Distillation d’une clé à travers un réseau classique

permet de dédoubler certaines parties du processus de réconciliation, par exemple pour tirer parti d’architectures matérielles multiprocesseurs. Chacune des applications a donc une tâche définie, et a la possibilité de lancer d’autres applications, appelées applications filles.

Les applications ont en commun les fonctions suivantes, définies dans la classe abstraite Application :

• start() : Cette fonction est accompagnée d’un état, incrémenté à chaque grande réalisation de l’application. Elle est appelée pour démarrer l’application, ainsi qu’en interne à chaque étape de l’application, selon un protocole défini (section 15.3).

• send(message) : Cette fonction envoie un message sur le canal classique.

• recv(message) : Cette fonction est appelée par le nœud lorsqu’un message à destination de l’application est arrivé. Un identifiant unique, associé à chaque application et indiqué dans l’en-tête de chaque message, permet au nœud de diriger le message vers la bonne application.

• notify() : Cette fonction est appelée par chaque application fille pour indiquer que cette dernière a terminé sa tâche.

Maître-esclave

L’attribution des rôles maître-esclave (ou client-serveur) entre les deux partenaires permet d’ordonner la communication. De façon arbitraire et interchangeable, l’un des partenaires sera promu maître (ou encore client) et surnommé "Claude" conformément à la terminologie adoptée dans [7] ; l’autre, Dominique, sera l’esclave ou serveur. Le maître est seul autorisé à initier les échanges. Conformément à un protocole préalablement établi (les protocoles de communication sont détaillés section 15.3), l’esclave devra fournir une réponse au maître.

Immédiatement après avoir émis une requête (resp. une réponse), le maître (resp. l’esclave) attend l’échange suivant (en entrant dans sa boucle principale). Dans ce cas, les deux partenaires n’exécutent jamais d’instructions simultanément. On parle d’exécution asynchrone. Parfois, quand des tâches indépendantes et longues doivent être réalisées par chacun des partenaires (par exemple le mélange aléatoire des symboles), le maître ou l’esclave choisissent de continuer leur exécution après émission d’un message. Les deux programmes sont exécutés de façon simultanée, dite synchrone. Dans ce cas, seul le maître est autorisé à rétablir le dialogue.

Le respect scrupuleux de ces règles hiérarchiques assure le bon déroulement du protocole de réconciliation. L’exécution synchrone de deux programmes est un aspect très délicat de la programmation, et exige de la rigueur. En cas de dysfonctionnement, l’erreur sera en général très difficile à déceler.

Multi-threading

Le multi-threading est une technique permettant d’avoir plusieurs points d’exécu- tion courants, ou fils, dans un même programme. Bien plus que l’exécution parallèle de deux programmes séparés communicants, le multi-threading requiert une discipline intran- sigeante pour qu’une fonction ne soit pas exécutée en même temps par les différents threads, ou qu’une variable ne soit pas modifiée en même temps par différents threads.

Le code pilotant l’expérience et le code gérant la réconciliation sont regroupés dans un même programme, parcouru par deux threads. L’avantage de cette méthode est que les deux threads partagent le même espace mémoire (puisqu’il s’agit d’un seul et même programme. . . ), ce qui évite le recours à de coûteux et contraignants protocoles de communication entre processus.