Table des matières du Be Book     Index du kit du Noyau

Concepts d'un port

Un port est un dépot de messages global au système dans lequel tout thread peut y copier un buffer de données, et dont tout thread peut en obtenir le buffer. Ce dépot est implémenté comme une file FIFO (premier entré/premier sorti—first-in/first-out) : un port stocke ses messages dans l'ordre de leur réception, et il les libère dans le même ordre. Chaque port possède sa propre file.


Créer et détruire un port

La fonction create_port() crée un nouveau port et lui affecte un unique port_id, global au système. Bien que les ports soient accessibles par tous les threads, les port_id ne sont pas diffusés par le système d'exploitation—il n'y a pas de fonction "find port". Si vous créez un port et voulez qu'un autre thread soit capable d'y écrire ou d'y lire, vous devez diffuser le port_id à ce thread.

Un port est la propriété de la team qui l'a créé. Lorsqu'une team meurt (lorsque tous ses threads sont morts), les ports qui appartenaient à cette team sont détruits. Une team peut céder la propriété de ses ports à une autre team via la fonction set_port_owner().

Si vous voulez explicitement vous débarasser d'un port, vous appellez delete_port(). Vous pouvez détruire n'importe quel port, pas seulement ceux possèdés par la team du thread appelant. Lorsque vous détruisez un port, tous ses messages non-lus sont perdus. Si vous voulez lire ces messages, mais voulez qu'aucun nouveaux messages n'arrivent durant l'opération, vous devez appeller close_port() avant la destruction du port. Notez que vous ne pouvez pas réouvrir un port fermé; après avoir fini de lire les messages du port, vous êtes supposé détruire le port.


La file de messages : lecture et écriture d'un port

La taille de la file de messages d'un port—le nombre de messages qu'il peut contenir au même instant—est fixée lorsque le port est créé.

Les fonctions write_port() et read_port() manipulent la file de messages du port : write_port() place un message à la fin de la file de messages du port; read_port() retire le message en tête de file et le retourne à l'appelant.write_port() bloque si la file est pleine; elle revient lorsque de la place est faite dans la file par une invocation de read_port(). De la même manière, si la file est vide, read_port() bloque jusquà ce que write_port() soit appellée.

Vous pouvez fournir un timeout pour vos opérations de lecture et d'écriture de port en utilisant les fonctions "gonflées" write_port_etc() et read_port_etc(). En fournissant un timeout, vous vous assurez que vos opération sur votre port ne bloqueront pas à vie.

Bien que chaque port possède sa propre file de message, tous les ports partagent un pool global d'emplacements—Il ne peut donc y avoir que ce nombre d'emplacements d'utilisés par tous les ports au même moment. Si trop de files de port se remplissent, le pool d'emplacements va s'épuiser, ce qui bloquera des appels write_port() sur des ports non-pleins. Pour éviter cette situation, vous devriez vous assurer que vos appels write_port() et read_port() sont raisonnablement équilibrés.

Les fonctions write_port() et read_port() sont le seul moyen d'accès à la file de messages d'un port. Il n'y a pas de notion de "coup d'oeil" aux messages non-lus de la file, ni de suppression de messages dans la file.


Les messages d'un port

Un message dans un port—les donnés envoyées au port—consiste en un "code de message" et un "buffer". L'un ou l'autre de ces éléments peut être utilisé comme bon vous semble, mais ils sont sensés remplir ces objectifs :

Le message que vous passez à write_port() est copié dans le port. Aprè le retour de write_port(), vous pouvez libérer les données du message sans affecter la copie que le port conserve.

Lorsque vous lisez un port, vous devez fournir un buffer dans lequel le port puisse copier le message. Si le buffer que vous fournissez n'est pas assez grand pour recevoir le message, la partie non-lue sera perdue—le prochain appel à read_port() ne finira pas la lecture du message.

Typiquement, vous allouerez le buffer que vous passez à read_port() en appelant d'abord port_buffer_size(), comme montré ci-dessous :

   char *buf = NULL;
   ssize_t size;
   int32 code;
   
   /* On assumera que my_port est valide. 
    * port_buffer_size() bloquera jusqu'à l'arrivée d'un message.
    */
   if ((size = port_buffer_size(my_port)) < B_OK) 
      /* Gérer l'erreur */
   
   if (size > 0)
      buf = (char *)malloc(size);
   
   if (buf) {
      /* Maintenant on peut lire le buffer. */
      if (read_port(my_port, &code, (void *)buf, size) < B_OK)
      /* Gérer l'erreur */

Evidement, il y a une possible compétition d'exécution (dans l'exemple) entre port_buffer_size() et l'appel consécutif read_port()—un autre thread peut lire le port entre-temps. Si vous utilisez port_buffer_size() comme montré dans l'exemple, vous ne devez pas avoir plus d'un thread lisant le port en même temps.

Comme indiqué dans l'exemple, port_buffer_size() bloque jusqu'à l'arrivée d'un message. Si vous ne voulez pas (potentiellement) bloquer à vie, vous devez utiliser la version port_buffer_size_etc() de cette fonction. Comme avec les autres fonctions ...etc(), port_buffer_size_etc() fournit une option de timeout.


Table des matières du Be Book     Index du kit du Noyau


Le Be Book,
...en superbe HTML...
pour BeOS Release 5.

Copyright © 2000 Be, Inc. Tous droits réservés.
Traduit en Français par Philippe Houdoin