AnyEvent et POE

AnyEvent et POE sont deux modules Perl permettant de gérer de manière transparente les problématiques de boucles d’événements et de s’affranchir du système d’exploitation utilisé.

Il fournissent une interface uniforme quelle que soit la boucle d’événements sous-jacente. Les boucles d’événements pouvant être utilisées sous POE sont dans le namespace POE::Loop:: tandis que celles pouvant l’être sous AnyEvent sont dans le namespace AnyEvent::Impl::. On peut d’ailleurs voir que AnyEvent peut utiliser POE via AnyEvent::Impl::POE, mais c’est une autre histoire…

Si ces deux modules reconnaissent les événements de type entrées/sorties, les timers et les signaux, ils ont une approche différente de leur gestion :

  • AnyEvent permet d’associer un callback (uniquement une référence sur une fonction) à chaque événement. Point ;
  • POE, quant à lui, ajoute une couche dans laquelle les événements vont être nommés avant d’être distribués. Un callback (référence sur une fonction ou un couple instance / méthode) est associé à un état (un nom), puis chaque événement va être associé à un état. Lorsqu’un événement arrive, il déclenche un état et le callback associé à l’état est appelé. Une sorte d’indirection, en fait. Ces états sont regroupés par session. Chaque session est indépendante, mais peut communiquer avec une autre via des événements que nous qualifierons de « logiques », puisqu’ils n’ont pas le système pour origine (voir les méthodes post() et call() de POE::Kernel). Ce cloisonnement permet d’isoler des composants du programme les uns des autres et ainsi d’éviter que les noms d’état ne se trouvent tous mélangés, avec le risque de collision de nom que cela pourrait entraîner.

Bien entendu AnyEvent comme POE permettent de réaliser n’importe quelle application client/serveur. Pour tous les protocoles orientés ligne, cela ne pose aucun problème.

Là où le bât blesse, c’est lorsque le dialogue utilise des données sérialisées. Le problème n’est pas tant comment les données sont sérialisées, mais plutôt comment ces données sérialisées sont envoyées. Faut-il transmettre la longueur des données sérialisées en tête ? Si oui sous quelle forme ? Si non y-a-t’il une séquence de fin ? Autant d’options qui rendent l’interopérabilité parfois impossible…

AnyEvent va avoir une attitude différente selon la méthode de sérialisation utilisée, partant du principe que certaines méthodes de désérialisation savent détecter toutes seules la fin du flux (JSON ou Data::MessagePack, par exemple). Pour les méthodes n’incluant pas cette possibilité (comme Storable), la longueur va être transmise en tête avec un pack("w", LONGUEUR) par exemple.

POE gère les choses de manière générique. À l’aide du module POE::Filter::Reference, quelle que soit la méthode de sérialisation, la longueur des données sérialisées est passée en tête sous une forme « humaine » suivie d’un \0 comme "128\0..." pour une longueur de 128 octets par exemple.

Donc, ce n’est pas compatible :-(

C’est là qu’intervient le module AnyEvent::POE_Reference. Ce module permet de sérialiser et de désérialiser des données à la mode POE depuis AnyEvent sans nécessiter la présence du module POE.

Merci Perl, merci Ijenko !