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

Threads et Teams

Déclarations dans : be/kernel/OS.h sauf indication contraire

Librairie : libroot.so

Un thread est une unité synchrone qui exécute une série d'instructions de programmation. Au lancement d'une application, un thread initial —le thread principal (main thread)—est automatiquement créé (ou engendré (spawned) ) et démarré. Depuis ce thread principal vous pouvez engendrer et démarrer des threads additionnels; depuis chacun de ces threads vous pouvez à nouveau engendrer et démarrer d'autres threads encore; et ainsi de suite...
La collection des threads engendrés par le thread principal—autrement dit, les threads compris dans une application— est appelée une Team. Tous les threads de toutes les teams s'exécutent en parallèle et de manière asynchrone entre eux.

Pour plus d'informations sur les threads et les teams, consultez "Concepts de Thread et de Team".


Fonctions


estimate_max_scheduling_latency()

Déclarée dans : be/kernel/scheduler.h

                                                         
  

bigtime_t estimate_max_scheduling_latency(thread_id thread = -1)

Retourne la lantence (temps d'attente) maximale estimée de préemption, en micro secondes, du thread spécifié. Spécifiez un thread_id de -1 pour obtenir celle du thread courant (appelant).


exit_thread() , kill_thread() , kill_team() , on_exit_thread()

                                                         
  

void exit_thread(status_t return_value)

status_t kill_thread(thread_id thread)

status_t kill_team(team_id team)

status_t on_exit_thread(void (*callback)(void *), void *data)

Ces fonctions commandent l'arrêt d'exécution à un ou plusieurs threads :

Terminer un thread est une opération assez peu risquée puisque, un thread ne pouvant terminer que lui-même, on assume qu'il sait ce qu'il fait. Tuer un autre thread ou une team toute entière est un peu plus violent, le ou les avis de décès pouvant tomber à tout moment. De plus, tuer un thread peut amener une fuite de mémoire, les ressources allouées par le thread ne seront pas forcement libérées. Toutefois, tuer une team toute entière n'entraine pas de fuite, le système récuperant toutes les ressources lorsqu'une team meurt.

 
Gardez à l'esprit que les threads meurent automatiquement (et leurs ressources sont récupérées) si on les laissent se terminer naturellement. Vous devriez n'avoir besoin de tuer un thread qu'uniquement si quelque chose débloque.


CODES DE RETOUR


find_thread()

                                                         
  

thread_id find_thread(const char *name)

Cherche et retourne le thread du nom fourni. Un argument name à NULL retourne le thread appelant.

Un nom de thread est affecté lorsque le thread est engendré. Le nom peut être changé par la suite via la fonction rename_thread() . Ne pas oublier que les noms de threads n'ont pas besoin d'être unique : si deux threads (ou plus) portent le même nom, un appel à find_thread() sur ce nom retourne le premier thread ainsi nommé rencontré. Il n'y a pas de moyen pour énumérer les threads portant un nom identique.

CODES DE RETOUR


get_team_info() , get_next_team_info()

                                                         
  

status_t get_team_info(team_id team, team_info *info)

status_t get_next_team_info(int32 *cookie, team_info *info)

Ces fonctions copient, dans l'argument info, la structure team_info pour une team particulière :

La fonction get_team_info() retourne l'information pour la team identifiée par team. Pour l'information du noyau, utiliser B_SYSTEM_TEAM comme valeur d'argument team.

La version get_next_team_info(), elle, vous permet de parcourir la liste de toutes les teams. L'argument cookie est un marqueur, positionnez-le à 0 lors du premier appel, la fonction s'occupe du reste. Celle-ci retourne B_BAD_VALUE lorsqu'il n'y a plus de team à visiter :

   /* Obtenir le team_info pour chaque team. */
   team_info info;
   int32 cookie = 0;
   
   while (get_next_team_info(0, &cookie, &info) == B_OK)
      ...

Consultez team_info pour une description de cette structure.

CODES DE RETOUR


get_thread_info() , get_next_thread_info()

                                                         
  

status_t get_thread_info(thread_id thread, thread_info *info)

status_t get_next_thread_info(team_id team,
      int32 *cookie,
      thread_info *info)

Ces fonctions copient, dans l'argument info, la structure thread_info pour un thread particulier. La fonction get_thread_info() retourne l'information pour le thread identifié par thread.

La version get_next_thread_info(), elle, vous permet de parcourir la liste de tous les threads d'une team, par appel successifs. L'argument team identifie la team que vous voulez observer; une valeur 0 de team signifie la team du thread appelant. L'argument cookie est un marqueur, positionnez-le à 0 lors du premier appel, la fonction s'occupe du reste. Celle-ci retourne B_BAD_VALUE lorsqu'il n'y a plus de thread à visiter :

   /* Obtenir le thread_info pour chaque thread de cette team. */
   thread_info info;
   int32 cookie = 0;
   
   while (get_next_thread_info(0, &cookie, &info) == B_OK)
      ...

 
L'information d'un thread est fournie principalement comme aide au déboggage. Aucune valeur trouvée dans une structure thread_info n'est garantie d'être valide—l'état du thread, par exemple, aura presque certainement changé lorsque get_thread_info() reviendra.


CODES DE RETOUR


has_data() voir send_data()


rename_thread()

                                                         
  

status_t rename_thread(thread_id thread, const char *name)

Change le nom d'un thread en name. Le nom ne peut pas être plus long que B_OS_NAME_LENGTH (32 caractères).

CODES DE RETOUR


resume_thread()

                                                         
  

status_t resume_thread(thread_id thread)

Demande à un thread, nouveau ou suspendu, de commencer l'exécution de ses instructions. Si le thread vient tout juste d'être engendré, il appelle et exécute la fonction spécifiée lors de l'appel à spawn_thread(). Si le thread était auparavant suspendu (via suspend_thread()), il reprend là où il en était.

Vous ne pouvez pas utiliser cette fonction pour réveiller un thread endormi ni pour débloquer un thread attendant l'acquisition d'un sémaphore ou la fin de l'appel à receive_data(). Cependant, vous pouvez débloquer ces threads en les suspendant puis en les réveillant. Les threads bloquées qui sont réveillées retournent B_INTERRUPTED.

resume_thread() est identique à l'envoi d'un signal SIGCONT au thread.

CODES DE RETOUR


receive_data()

Récupère un message depuis le cache de message du thread. Le message y aura été placé par un précédant appel à send_data(). Si le cache est vide, receive_data() bloque jusqu'à ce qu'un message se présente—elle ne revient jamais les mains vides.

Le thread_id du thread qui a appellé send_data() est retourné par référence dans l'argument sender. Notez qu'il n'y a pas de garantie que l'émetteur sera toujours vivant lorsque vous obtiendrez son ID. Egalement, la valeur de sender a l'entrée de la fonction est ignorée—vous ne pouvez pas demander un message d'un émetteur en particulier.

La fonction send_data() copie deux donnéeacute;es dans le cache de message d'un thread :

Malheureusement, il n'y a pas de moyen pour connaître la quantité de donnée présente dans le cache avant d'appeller receive_data() :

Chaque receive_data() correspond exactement à un send_data(). Sans invocation de sa fonction soeur, receive_data() bloquera jusqu'à ce que send_data() soit appellée. Si vous ne voulez pas bloquer, vous devez appeller has_data() avant d'appeller receive_data() (et procéder au receive_data() uniquement si has_data() retourne true).

CODES DE RETOUR


send_data() , receive_data() , has_data()

                                                         
  

status_t send_data(thread_id thread,
      int32 code,
      void *buffer,
      size_t buffer_size)

int32 receive_data(thread_id *sender,
      void *buffer,
      size_t buffer_size)

bool has_data(thread_id thread)

Chaque thread a un cache de message d'un message de capacité qui lui est associé. Ces fonctions accèdent à ce cache.

send_data() copie un message dans le cache de message du thread thread. Le thread ciblé recupère le message (et vide son cache) en appelant receive_data().

Il y a deux parties dans le message :

En plus de retourner le code directement et de copier les données du message dans son argument buffer, receive_data() positionne la valeur de sender à l'identifiant (id) du thread qui a émis le message.

send_data() bloque s'il y a un message non lu dans le cache du thread ciblé; sinon elle retourne immédiatement (c.a.d. elle n'attend pas que la cible appelle receive_data()). De manière analogue, receive_data() bloque jusqu'à ce qu'il y ait un message à retourner.

Dans l'exemple suivant, le thread principal engendre un thread, lui envoit un message et demande au thread de s'exécuter :

   main()
   {
      thread_id other_thread;
      int32 code = 63;
      char *buf = "Hello";
   
      other_thread = spawn_thread(thread_func, ...);
      send_data(other_thread, code, (void *)buf, strlen(buf));
      resume_thread(other_thread);
      ...
   }

Pour recupérer le message, le thread cible appelle receive_data() :

   int32 thread_func(void *data)
   {
      thread_id sender;
      int32 code;
      char buf[512];
   
      code = receive_data(&sender, (void *)buf, sizeof(buf));
      ...
   }

Gardez à l'esprit que les données du message sont copiées dans le buffer; vous devez allouer l'espace adéquate pour les données. Si le buffer n'est pas assez grand pour contenir toutes les données du message, la partie en trop est perdue. Notez, cependant, qu'il n'y a aucun moyen pour un thread de déterminer combien de données ont été copié dans son cache de message.

has_data() retourne true si le thread thread a un message dans son cache de message. Ostensiblement, vous utiliserez cette fonction avant d'appeller send_data() ou receive_data() pour éviter de bloquer :

   if (!has_data(target_thread)) 
      err = send_data(target_thread, ...);
   
   /* ou */
   
   if (has_data(find_thread(NULL))
      code = receive_data(...);

Cela fonctionne pour receive_data(), mais notez qu'il y a une possible compétition d'appel entre has_data() et send_data(). Un autre thread peut émettre un message à la cible dans l'interim.

CODES DE RETOUR

send_data() retourne :


set_thread_priority() , suggest_thread_priority()

                                                         
  

status_t set_thread_priority(thread_id thread, int32 new_priority)

Déclarée dans : be/kernel/scheduler.h
                                                         
  

int32 suggest_thread_priority(uint32 what = B_DEFAULT_MEDIA_PRIORITY,
      int32 period = 0,
      bigtime_t jitter = 0,
      bigtime_t length = 0)

set_thread_priority() modifie la priorité du thread spécifié à new_priority. La priorité doit être comprise entre 0 et 120. Consultez "Priorité de thread" pour une description du schéma de priorité, et "Valeurs de priorité de thread" pour une liste de constantes pré-définies de priorité.

suggest_thread_priority() prend des informations à propos du thread et suggère en retour une priorité que vous pouvez passer à set_thread_priority() (ou, mieux, à spawn_thread()).

La valeur de what est un masque binaire qui indique le type d'activités que le thread effectuera. Les valeurs possibles sont listées dans "Priorité suggérée de thread".

period est le nombre de fois par seconde que le thread a besoin d'être exécuté (spécifier 0 si il en a besoin continuellement). jitter est une estimation, en micro secondes, de l'ecart-type autour de cette period moyenne.

length est une approximation de la quantité de temps, en micro secondes, que durera typiquement chaque invocation du thread (c.a.d., la quantité de temps qui s'écoulera entre le moment de la reception d'un message, son exploitation et l'attente d'un nouveau message).

Par exemple, si vous engendrez un thread pour gérer le rafraichissement video pour un jeux informatique, et que vous voulez que l'affichage soit mis à jour 30 fois par secondes, vous devriez utiliser un code similaire au suivant ::

   int32 priority;
   priority = suggest_thread_priority(B_LIVE_3D_RENDERING, 30, 1000, 150);
   th = spawn_thread(func, "render_thread", priority, NULL)

Ceci engendre le thread d'affichage avec une priorité appropriée pour un thread d'affichage en temps-réel qui veut être exécuté 30 fois par seconde, avec une variation de seulement 100 micro secondes. Chaque invocation du code du thread est estimée prendre 150 micro secondes. Bien évidement, les valeurs de jitter et de length doivent être peaufinées pour chaque application particulière.

CODES DE RETOUR

set_thread_priority() retourne...


snooze() , snooze_until()

                                                         
  

status_t snooze(bigtime_t microseconds)

status_t snooze_until(bigtime_t microseconds, int timebase)

snooze() bloque le thread appelant pour un certain nombre de micro secondes.

snooze_until() bloque jusqu'à un moment absolu mesuré dans la base temps donnée. Actuellement, la seule valeur autorisée pour la base de temps est B_SYSTEM_TIMEBASE, qui mesure le temps d'après l'horloge système (telle que retournée par system_time()).

CODES DE RETOUR


snooze_until() voir snooze()


spawn_thread()

                                                         
  

thread_id spawn_thread(thread_func func,
      const char *name,
      int32 priority,
      void *data)

Créer un nouveau thread et retourne son identifiant thread_id (un entier positif). Les arguments sont :

Un nouveau thread engendré est dans l'état suspendu (B_THREAD_SUSPENDED). Pour dire au thread de démarrer, vous passez son thread_id à la fonction resume_thread(). Le thread continuera de s'exécuter jusqu'à ce que la fonction du thread finisse, ou jusqu'à ce que le thread soit explicitement tué (via un signal ou un appel à exit_thread(), kill_thread(), ou kill_team()).

CODES DE RETOUR


suggest_thread_priority() see set_thread_priority()


suspend_thread()

                                                         
  

status_t suspend_thread(thread_id thread)

Stoppe l'exécution du thread donné, mais sans le tuer. Le thread reste suspendu (suspend_thread() bloque) jusqu'à il lui soit dit de reprendre par la fonction resume_thread(). Rien ne vous interdit de suspendre votre propre thread, c.a.d.  :

   suspend_thread(find_thread(NULL));

Bien sûr, c'est ingénieux seulement si vous avez un autre thread qui vous réveillera plus tard !

Vous pouvez suspendre n'importe quel thread, quel que soit son état. Mais attention : si le thread est bloqué sur un sémaphore (par exemple), le prochain appel à resume_thread() fera "passer par dessus" l'acquisition du sémaphore.

Les suspensions ne s'imbriquent pas. Un seul resume_thread() réveille un thread, peu importe le nombre d'appel à suspend_thread() il avait reçu.

suspend_thread() est identique à l'envoi d'un signal SIGSTOP au thread.

CODES DE RETOUR


wait_for_thread()

                                                         
  

status_t wait_for_thread(thread_id thread, status_t *exit_value)

Cette fonction fait attendre le thread appelant jusqu'à ce que le thread (le thread "cible") soit mort. Si le thread est suspendu (ou fraîchement engendré), wait_for_thread() le réveille.

Lorsque le thread cible est mort, la valeur retournée par sa fonction de thread (ou celle imposée par exit_thread()) est renvoyée dans exit_value. Si le thread cible a été tué (par kill_thread() ou kill_team()), ou si la fonction de thread ne retourne pas de valeur, la valeur retournée dans exit_value sera non fiable.

 
Vous devez passer un pointeur valide comme second argument à wait_for_thread(). Vous ne devez pas passer NULL même si vous n'etes pas intéressé par la valeur de retour.


CODES DE RETOUR


Structures et Types


team_id , thread_id

typedef int32 team_id ;

typedef int32 thread_id ;

Ces nombres identifient de manière unique une team et un thread, respectivement.


team_info

                                                         
  

typedef struct {
      team_id team;
      int32 thread_count;
      int32 image_count;
      int32 area_count;
      thread_id debugger_nub_thread;
      port_id debugger_nub_port;
      int32 argc;
      char args[64];
      uid_t uid;
      gid_t gid;
      } team_info;

La structure team_info retourne des informations sur une team. Pour obtenir l'une de ces structures, utilisez get_team_info() ou get_next_team_info().

Le premier champ est évident; les trois suivants également : ils donnent le nombre de threads engendrés, d'images chargées et d'areas créées ou clonées dans cette team.

Les champs debugger sont utilisés par, euh, le... debugger ?

Le champ argc est le nombre d'arguments de la ligne de commande qui a été utiliée pour lancer cette team; args est une copie des 64 premiers caractères de la ligne de commande. Si cette team est une application qui a été lancé par l'interface utilisateur (par double-clic ou en acceptant un laché d'icone), alors argc est 1 et args est le nom du fichier exécutable de l'application.

uid et gid identifie l'utilisateur et le groupe "propriétaire" de la team. Vous pouvez utiliser ces valeurs pour jouer avec les permissions.


thread_func

                                                         
  

typedef int32 (*thread_func)(void *data);

thread_func est le prototype d'une fonction de thread pour un thread. Vous spécifiez une fonction de thread en passant une thread_func comme premier argument de spawn_thread(); le dernier argument de spawn_thread() est renvoyé à la fonction de thread comme argument data. Lorsque la fonction du thread se termine, le thread engendré est automatiquement tué. Pour obtenir la valeur de retour d'une thread_func, un autre thread doit attendre par un appel à wait_for_thread().

Notez que spawn_thread() ne copie pas les données pointé par data. Elle transmet simplement le pointeur, littérallement. Ne passer jamais un pointer alloué localement (sur la pile), donc.


thread_info

                                                         
  

typedef struct {
      thread_id thread;
      team_id team;
      char name[B_OS_NAME_LENGTH];
      thread_state state;
      sem_id sem;
      int32 priority;
      bigtime_t user_time;
      bigtime_t kernel_time;
      void *stack_base;
      void *stack_end;
      } thread_info

La structure thread_info contient des informations sur un thread. Pour obtenir l'une de ces structures, utilisez get_thread_info() ou get_next_thread_info().

Les champs thread, team et name contiennent l'information indiquée.

state décrit ce que le thread fait actuellement (consultez thread_state pour une liste des états). Si le thread attend l'acquisition d'un sémaphore, sem est ce sémaphore.

priority est une valeur indiquant le niveau d'attention que le thread reçoit (Consultez "Priorité de thread").

user_time et kernel_time sont la quantité de temps, en micro secondes, que le thread a consommé dans le code user et la quantité de temps que le noyau a fonctionné pour le compte du thread, respectivement.

stack_base et stack_end sont des pointeurs sur le premier octet et le dernier octet dans la pile d'exécution du thread. Actuellement, la taille de la pile est fixée à environ 256k.

 
Les deux pointeurs de pile sont actuellement inversés de façon à ce que stack_base soit plus petit que stack_end. (Dans un monde où la pile grandit par le bas, la base devrait être plus grande que la fin).



Constantes


B_SYSTEM_TEAM

                                                         
  

#define B_SYSTEM_TEAM ...

Utilisez cette constante comme premier argument à get_team_info() pour obtenir les informations sur le noyau.


B_SYSTEM_TIMEBASE

                                                         
  

#define B_SYSTEM_TIMEBASE ...

La constante de base temporelle sur l'horloge système est utilisée comme base de mesure du temps dans la fonction snooze_until(). (Actuellement, c'est la seule base temporelle disponible.)


be_task_flags

Déclaré dans : be/kernel/scheduler.h
                                                         
  

enum be_task_flags { B_DEFAULT_MEDIA_PRIORITY,
      B_OFFLINE_PROCESSING,
      B_STATUS_RENDERING,
      B_USER_INPUT_HANDLING,
      ...
      };

Constante Signification
B_DEFAULT_MEDIA_PRIORITY Le thread ne fait rien de particulierement spécialisé.
B_OFFLINE_PROCESSING Le thread effectue des calculs non temps-réel.
B_STATUS_RENDERING Le thread affiche un status ou une prévisualisation.
B_USER_INPUT_HANDLING Le thread gère l'intéraction avec l'utilisateur.
B_LIVE_VIDEO_MANIPULATION Le thread manipule de la video en temps réel (filtrage, compréssion, decompréssion, etc.).
B_VIDEO_PLAYBACK Le thread joue de la video en provenance d'un périphérique physique.
B_VIDEO_RECORDING Le thread enregistre de la video en provenance d'un périphérique physique.
B_LIVE_AUDIO_MANIPULATION Le thread manipule de l'audio en temps réel (filtrage, compréssion, decompréssion, etc.).
B_AUDIO_PLAYBACK Le thread joue de l'audio en provenance d'un périphérique physique.
B_AUDIO_RECORDING Le thread enregistre de l'audio en provenance d'un périphérique physique.
B_LIVE_3D_RENDERING Le thread réalise de la synthèse 3D en temps réel.
B_NUMBER_CRUNCHING Le thread effectue du calcul numérique intensif.

Ces constantes décrivent le type de tâche pour laquelle le thread est conçu. Vous utilisez ces constantes lors d'une demande de suggestion de priorité (cf. suggest_thread_priority()).

 
Ces constantes ne doivent pas être utilisées comme valeur réelle de priorité—ne passez pas l'une de ces valeurs comme argument priority de spawn_thread().



Valeurs de priorité de thread

Priorité en temps partagé (Time-Sharing) Valeur
B_LOW_PRIORITY 5
B_NORMAL_PRIORITY 10
B_DISPLAY_PRIORITY 15
B_URGENT_DISPLAY_PRIORIT Y 20

Priorité en temps réel (Real-Time) Valeur
B_REAL_TIME_DISPLAY_PRIORITY 100
B_URGENT_PRIORITY 110
B_REAL_TIME_PRIORITY 120

Les valeurs de priorité de thread sont utilisée pour indiquer l'urgence d'un thread. Bien que vous pouvez repositionner la priorité d'un thread via set_thread_priority(), la priorité est initiallement—et quasi uniquement—positionnée dans spawn_thread(). Comme montré ici, il y deux types de priorités [texte manquant].


thread_state

                                                         
  

enum { ... } thread_state

Etat Signification
B_THREAD_RUNNING Le thread reçoit actuellement l'attention d'un CPU.
B_THREAD_READY Le thread attend son tour pour recevoir l'attention d'un CPU.
B_THREAD_SUSPENDED Le thread a été suspendu ou vient juste d'être engendré et attend de démarrer.
B_THREAD_WAITING Le thread attend l'aquisition d'un sémaphore. Le champ sem de la structure thread_info du thread vous indiquera quel sémaphore.
B_THREAD_RECEIVING Le thread est dans un appel à la fonction receive_data().
B_THREAD_ASLEEP Le thread is dans un appel à la fonction snooze().

Un état de thread vous indique ce qu'il est en train de faire. Pour obtenir l'état, consultez le champ state de la structure thread_info (obtenue par get_thread_info()).


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