Provided by: manpages-fr-dev_4.23.1-1_all bug

NOM

       futex – Verrouillage rapide en mode utilisateur

BIBLIOTHÈQUE

       Bibliothèque C standard (libc, -lc)

SYNOPSIS

       #include <linux/futex.h>   /* Définition des constantes FUTEX_* */
       #include <sys/syscall.h>   /* Définition des constantes SYS_* */
       #include <unistd.h>

       long syscall(SYS_futex, uint32_t *uaddr, int futex_op, uint32_t val,
                    const struct timespec *timeout,   /* ou : uint32_t val2 */
                    uint32_t *uaddr2, uint32_t val3);

       Remarque :   la   glibc  ne  fournit  pas  de  fonction  autour  de  futex(),  nécessitant
       l'utilisation de syscall(2).

DESCRIPTION

       L'appel système futex() offre une méthode pour attendre qu'une condition  soit  vraie.  On
       l'utilise  en général comme construction de blocage dans le contexte de la synchronisation
       de la mémoire partagée. Quand  on  utilise  des  futex,  la  majorité  des  opérations  de
       synchronisation s'effectue dans l'espace utilisateur. Un programme de l'espace utilisateur
       n'utilise l'appel système futex() que lorsqu'il est probable qu'il doive se  bloquer  plus
       longtemps  avant  que la condition ne soit vraie. D'autres opérations futex() peuvent être
       utilisées pour réveiller des processus ou des  threads  qui  attendent  une  condition  en
       particulier.

       Un  futex est une valeur 32 bits — désignée ci-dessous comme « mot futex » —dont l'adresse
       est fournie à l'appel système futex() (les futex ont une taille de 32 bits sur toutes  les
       plateformes,  y  compris  les systèmes 64 bits). Toutes les opérations futex sont pilotées
       par cette valeur. Afin de partager un futex entre des processus, le futex est  placé  dans
       une  zone  de  la  mémoire  partagée  créée en utilisant (par exemple) mmap(2) ou shmat(2)
       (ainsi, le mot futex peut avoir plusieurs adresses virtuelles dans  différents  processus,
       mais  ces  adresses se rapportent toutes au même emplacement de la mémoire physique). Dans
       un programme multithreadé, il suffit de mettre le mot  futex  dans  une  variable  globale
       partagée par tous les threads.

       Lors  de l'exécution d'une opération futex qui demande le blocage d'un thread, le noyau ne
       le bloquera que si le mot futex a une valeur fournie par le thread appelant (en tant qu'un
       des  paramètres  de  l'appel  futex())  correspondant  à  celle  prévue  du  mot futex. Le
       chargement de la valeur du mot futex, la comparaison de cette valeur avec  celle  attendue
       et  le  blocage  s'effectueront  de  manière  atomique et seront entièrement organisés par
       rapport aux opérations qui sont effectuées en parallèle par d'autres threads sur  le  même
       mot  futex.  Ainsi,  le  mot  futex est utilisé pour relier la synchronisation de l'espace
       utilisateur et l'implémentation  du  blocage  par  le  noyau.  Tout  comme  une  opération
       compare-and-exchange  atomique qui modifie potentiellement la mémoire partagée, le blocage
       par futex est une opération compare-and-block atomique.

       Une  utilisation  des  futex  consiste  à  implémenter  des  verrous.  L'état  du   verrou
       (c'est-à-dire  acquis  ou  non acquis) peut se représenter comme un drapeau auquel on a un
       accès atomique en mémoire partagée. En absence de conflit (uncontended  case),  un  thread
       peut  accéder et modifier l'état du verrou avec des instructions atomiques, par exemple le
       passer de manière atomique de l'état non acquis à acquis,  en  utilisant  une  instruction
       compare-and-exchange  atomique  (de  telles  instructions  s'effectuent  entièrement  dans
       l'espace utilisateur et le noyau ne conserve aucune information  sur  l'état  du  verrou).
       D'un  autre  côté, un thread peut être incapable d'acquérir un verrou parce qu'il est déjà
       acquis par un autre thread. Il peut alors passer l'attribut du  verrou  en  tant  que  mot
       futex,  et  la  valeur  représentant  l'état  acquis  en  tant  que  valeur  attendue pour
       l'opération d'attente de futex(). Cette opération futex() bloquera si et seulement  si  le
       verrou  est  encore  acquis  (c'est-à-dire si la valeur du mot futex correspond toujours à
       « l'état acquis »). Lorsque le verrou est relâché, le thread  doit  d'abord  réinitialiser
       l'état du verrou sur non acquis puis exécuter une opération futex qui réveille les threads
       bloqués par le drapeau de verrou utilisé en tant que  mot  futex  (cela  peut  être  mieux
       optimisé  pour  éviter  les  réveils  inutiles). Voir futex(7) pour plus de détails sur la
       manière d'utiliser les futex.

       Outre la fonctionnalité de base du futex consistant à attendre et  à  réveiller,  d'autres
       opérations futex visent à gérer des cas d'utilisation plus complexes.

       Remarquez qu'aucune initialisation ou destruction explicite n'est nécessaire pour utiliser
       les futex ; le noyau ne garde un futex (c'est-à-dire un artefact d'implémentation  interne
       au  noyau)  que  pendant  que  les  opérations  telles que FUTEX_WAIT, décrite ci-dessous,
       s'effectuent sur un mot futex en particulier.

   Argument
       Le paramètre uaddr pointe vers un mot futex. Sur toutes les plateformes,  les  futex  sont
       des  entiers  de  quatre  octets qui doivent être alignés sur une limite de quatre octets.
       L'opération à effectuer sur le futex est indiquée dans le paramètre de futex_op ; val  est
       une valeur dont la signification et l'objectif dépendent de futex_op.

       Les  autres  paramètres  (timeout,  uaddr2 et val3) ne sont nécessaires que pour certaines
       opérations futex décrites ci-dessous. Si un de ces arguments n'est pas nécessaire, il  est
       ignoré.

       Pour  plusieurs  opérations  de  blocage,  le  paramètre  timeout est un pointeur vers une
       structure timespec qui indique la durée maximale de l'opération. Toutefois,  contrairement
       au  prototype  décrit  ci-dessus,  pour  certaines opérations, les quatre octets les moins
       significatifs de ce paramètre sont utilisés comme un  entier  dont  la  signification  est
       déterminée  par  l'opération.  Pour  ces  opérations,  le  noyau diffuse la valeur timeout
       d'abord à unsigned long, puis à uint32_t, et dans le reste de cette page, ce paramètre est
       désigné par val2 quand il est interprété de cette manière.

       Lorsqu'il  est  nécessaire, le paramètre uaddr2 est un pointeur vers un deuxième mot futex
       utilisé par l'opération.

       L'interprétation du paramètre de l'entier final, val3, dépend de l'opération.

   Opérations futex
       Le paramètre futex_op est en  deux  parties :  une  commande  qui  indique  l'opération  à
       effectuer  et  un  bit ORed avec zéro ou plusieurs options qui changent le comportement de
       l'opération. Les options qui peuvent être incluses dans futex_op sont les suivantes :

       FUTEX_PRIVATE_FLAG (depuis Linux 2.6.22)
              Ce bit d'option peut être utilisé avec toutes les opérations futex. Il dit au noyau
              que   le  futex  est  un  processus  privé  non  partagé  avec  d'autres  processus
              (c'est-à-dire qu'il n'est utilisé que pour la synchronisation entre les threads  du
              même  processus). Cela permet au noyau d'effectuer des optimisations de performance
              supplémentaires.

              Par commodité, <linux/futex.h> définit un ensemble de constantes  dont  le  suffixe
              est  _PRIVATE  et  qui sont équivalentes à toutes les opérations listées ci-dessous
              mais avec l'attribut FUTEX_PRIVATE_FLAG ORed dans la valeur  de  la  constante.  On
              trouve ainsi FUTEX_WAIT_PRIVATE, FUTEX_WAKE_PRIVATE et ainsi de suite.

       FUTEX_CLOCK_REALTIME (depuis Linux 2.6.28)
              Ce  bit  d'option  ne  peut  être utilisé qu'avec les opérations FUTEX_WAIT_BITSET,
              FUTEX_WAIT_REQUEUE_PI  (depuis  Linux  4.5),  FUTEX_WAIT  (depuis  Linux  4.5)   et
              FUTEX_LOCK_PI2 (depuis Linux 5.14).

              Si cette option est positionnée, le noyau mesure le timeout par rapport à l'horloge
              CLOCK_REALTIME.

              Si cette option n'est pas positionnée, le noyau mesure le  timeout  par  rapport  à
              l'horloge CLOCK_MONOTONIC.

       L'opération indiquée dans futex_op prend une de ces valeurs :

       FUTEX_WAIT (depuis Linux 2.6.0)
              Cette  option teste que la valeur du mot futex vers laquelle pointe l'adresse uaddr
              contient toujours la valeur val attendue, et si  tel  est  le  cas,  elle  s'endort
              jusqu'à  une  opération  FUTEX_WAKE sur le mot futex. Le chargement de la valeur du
              mot futex est  un  accès  en  mémoire  atomique  (c'est-à-dire  qu'il  utilise  des
              instructions  machine  atomiques  de  l'architecture  concernée). Ce chargement, la
              comparaison avec la valeur attendue et la mise en sommeil s'effectuent  de  manière
              atomique et sont totalement organisés selon les autres opérations futex sur le même
              mot futex. Si le thread commence à dormir, il est considéré comme en attente de  ce
              mot futex. Si la valeur futex ne correspond pas à val, l'appel échoue immédiatement
              avec l'erreur EAGAIN.

              Le but de la comparaison avec la valeur attendue est d'empêcher des réveils perdus.
              Si  un autre thread a changé la valeur du mot futex après que le thread a décidé de
              se bloquer en se fondant sur la valeur d'avant, et si l'autre thread a effectué une
              opération  FUTEX_WAKE (ou un réveil équivalent) après le changement de cette valeur
              et avant cette opération FUTEX_WAIT, le thread appelant observera cette  valeur  et
              ne commencera pas à dormir.

              Si le timeout n'est pas NULL, la structure vers laquelle il pointe indique un délai
              d'attente (cet intervalle sera arrondi à  la  valeur  supérieure  à  partir  de  la
              granularité de l'horloge système et il est garanti de ne pas expirer en avance). Le
              délai est mesuré par défaut par rapport à  l'horloge  CLOCK_MONOTONIC  mais  depuis
              Linux    4.5,   l'horloge   CLOCK_REALTIME   peut   être   choisie   en   indiquant
              FUTEX_CLOCK_REALTIME dans futex_op. Si le  timeout  est  NULL,  l'appel  se  bloque
              indéfiniment.

              Remarque :  pour  FUTEX_WAIT,  le timeout est interprété comme une valeur relative.
              Cela diffère des autres opérations futex où le timeout  est  interprété  comme  une
              valeur  absolue.  Pour  obtenir  l'équivalent  de FUTEX_WAIT, avec un délai absolu,
              utilisez FUTEX_WAIT_BITSET en indiquant val3 comme FUTEX_BITSET_MATCH_ANY.

              Les paramètres uaddr2 et val3 sont ignorés.

       FUTEX_WAKE (depuis Linux 2.6.0)
              Cette opération réveille jusqu'à val éléments en attente  (comme  dans  FUTEX_WAIT)
              sur  le  mot  futex  à  l'adresse uaddr. Généralement, val est indiqué soit sous la
              forme de 1 (réveil d'un seul élément en attente) soit avec INT_MAX (réveil de  tous
              les  éléments  en attente). Vous n'avez aucune garantie quant aux éléments qui sont
              réveillés (par exemple un élément en  attente  dont  la  priorité  d'ordonnancement
              élevée n'est pas garanti de se réveiller avant un autre d'une priorité plus basse).

              Les paramètres timeout, uaddr2 et val3 sont ignorés.

       FUTEX_FD (de Linux 2.6.0 jusqu'à Linux 2.6.25 inclus)
              Cette  opération  crée  un  descripteur  de  fichier  associé  au  futex sur uaddr.
              L'appelant doit fermer le descripteur de fichier  renvoyé  après  l'avoir  utilisé.
              Quand  un  autre  processus  ou  un  autre thread effectue un FUTEX_WAKE sur le mot
              futex, le descripteur de fichier indique  qu'il  est  accessible  en  lecture  avec
              select(2), poll(2), et epoll(7)

              Le   descripteur  de  fichier  peut  être  utilisé  pour  avoir  des  notifications
              asynchrones, si val n'est pas nul, puis, quand  un  autre  processus  ou  un  autre
              thread exécute FUTEX_WAKE, l'appelant recevra le numéro du signal passé à val.

              Les paramètres timeout, uaddr2 et val3 sont ignorés.

              Parce  qu'il  était  de  façon  inhérente  sujet  à  des situations de concurrence,
              FUTEX_FD a été supprimé de Linux 2.6.26 et les suivants.

       FUTEX_REQUEUE (depuis Linux 2.6.0)
              Cette opération effectue la même chose  que  FUTEX_CMP_REQUEUE  (voir  ci-dessous),
              sauf  qu'elle  ne  vérifie rien en utilisant la valeur dans val3 (le paramètre val3
              est ignoré).

       FUTEX_CMP_REQUEUE (depuis Linux 2.6.7)
              Cette opération vérifie d'abord si l'emplacement uaddr contient toujours la  valeur
              val3.  Si tel n'est pas le cas, l'opération échoue avec l'erreur EAGAIN. Si tel est
              le cas, l'opération réveille un maximum de val éléments en  attente  du  futex  sur
              uaddr.  S'il  y  a plus de val éléments en attente, les autres sont supprimés de la
              file d'attente du futex source sur uaddr et ajoutés à la file  d'attente  du  futex
              cible  sur  uaddr2.  Le  paramètre  val2  indique  une  limite supérieure du nombre
              d'éléments remis en attente dans le futex sur uaddr2.

              Le chargement à partir de uaddr est un  accès  atomique  en  mémoire  (c'est-à-dire
              qu'il  utilise  les instructions machine atomiques de l'architecture concernée). Ce
              chargement,  la  comparaison  avec  val3  et  la  remise  en   attente   d'éléments
              s'effectuent  de  manière  atomique  et  sont totalement organisées par rapport aux
              autres opérations sur le même mot futex.

              Les valeurs classiques qu'on indique à val sont 0 ou 1 (indiquer INT_MAX n'est  pas
              utile car cela rendrait l'opération FUTEX_CMP_REQUEUE équivalente à FUTEX_WAKE). La
              valeur limite indiquée avec val2 est généralement  1  ou  INT_MAX  (indiquer  0  en
              paramètre   n'est   pas  utile  car  cela  rendrait  l'opération  FUTEX_CMP_REQUEUE
              équivalente à FUTEX_WAIT).

              L'opération   FUTEX_CMP_REQUEUE   a   été   ajoutée   pour   remplacer   l'ancienne
              FUTEX_REQUEUE.  La  différence  est que la vérification de la valeur sur uaddr peut
              être utilisée pour s'assurer que la remise  en  attente  ne  se  produit  que  sous
              certaines  conditions,  ce qui évite les conflits de mémoire (race conditions) dans
              certains cas d'utilisation.

              FUTEX_REQUEUE et FUTEX_CMP_REQUEUE peuvent être utilisées pour éviter  des  réveils
              en troupeau (thundering herd) qui peuvent survenir quand on utilise FUTEX_WAKE dans
              des cas où tous les éléments en attente qu'on réveille doivent  acquérir  un  autre
              futex.  Imaginons le scénario suivant où plusieurs threads attendent en B, une file
              d'attente implémentée en utilisant un futex :

                  lock(A)
                  while (!check_value(V)) {
                      unlock(A);
                      block_on(B);
                      lock(A);
                  };
                  unlock(A);

              Si un thread qui se réveille utilisait FUTEX_WAKE, tous les éléments attendant en B
              se réveilleraient et essaieraient d'acquérir le verrou A. Cependant, réveiller tous
              ces threads de cette manière  serait  vain  car  tous  les  threads,  sauf  un,  se
              bloqueraient immédiatement à nouveau via le verrou A. Au contraire, une remise dans
              la file d'attente ne réveille qu'un élément et déplace les autres sur le  verrou  A
              et quand celui réveillé déverrouille A, le suivant peut continuer.

       FUTEX_WAKE_OP (depuis Linux 2.6.14)
              Cette  opération a été ajoutée pour prendre en charge certains cas d'utilisation de
              l'espace utilisateur où plus d'un futex à la fois doit être géré. L'exemple le plus
              frappant   est   l'implémentation  de  pthread_cond_signal(3),  qui  nécessite  des
              opérations sur deux futex, une pour implémenter le  mutex,  l'autre  pour  utiliser
              dans  l'implémentation  de la file d'attente associée à la variable conditionnelle.
              FUTEX_WAKE_OP permet d'implémenter de tels cas sans augmenter le nombre de conflits
              et de changement de contexte.

              L'opération  FUTEX_  WAKE_OP revient à exécuter le code suivant de manière atomique
              et complètement organisé en fonction des opérations futex  sur  un  des  deux  mots
              futex fournis :

                  uint32_t oldval = *(uint32_t *) uaddr2;
                  *(uint32_t *) uaddr2 = oldval op oparg;
                  futex(uaddr, FUTEX_WAKE, val, 0, 0, 0);
                  if (oldval cmp cmparg)
                      futex(uaddr2, FUTEX_WAKE, val2, 0, 0, 0);

              En d'autres termes, FUTEX_WAKE_OP fait ce qui suit :

              -  sauvegarde la valeur d'origine du mot futex sur uaddr2 et effectue une opération
                 pour modifier la valeur du futex sur uaddr2 ; il s'agit d'un  accès  en  mémoire
                 read-modify-write  atomique  (c'est-à-dire  d'une  utilisation  des instructions
                 machine atomiques liées à l'architecture concernée)

              -  réveille un maximum de val éléments en attente sur le futex pour  le  mot  futex
                 sur uaddr ;

              -  et selon les résultats d'un test de la valeur d'origine du mot futex sur uaddr2,
                 réveille un maximum de val2 éléments en attente du mot futex sur  le  futex  sur
                 uaddr2.

              L'opération  et  la  comparaison qui doivent être effectuées sont encodées dans les
              bits du paramètre val3. Visuellement, l'encodage est :

                  +---+---+-----------+-----------+
                  |op |cmp|   oparg   |  cmparg   |
                  +---+---+-----------+-----------+
                    4   4       12          12    <== # of bits

              Exprimé en code, l'encodage est :

                  #define FUTEX_OP(op, oparg, cmp, cmparg) \
                                  (((op & 0xf) << 28) | \
                                  ((cmp & 0xf) << 24) | \
                                  ((oparg & 0xfff) << 12) | \
                                  (cmparg & 0xfff))

              Dans ce qui précède, op et  cmp  sont  chacun  des  codes  listés  ci-dessous.  Les
              composants  oparg  et  cmparg  sont  des  valeurs  numériques  littérales, sauf les
              remarques ci-dessous.

              Le composant op prend une de ces valeurs :

                  FUTEX_OP_SET        0  /* uaddr2 = oparg; */
                  FUTEX_OP_ADD        1  /* uaddr2 += oparg; */
                  FUTEX_OP_OR         2  /* uaddr2 |= oparg; */
                  FUTEX_OP_ANDN       3  /* uaddr2 &= ~oparg; */
                  FUTEX_OP_XOR        4  /* uaddr2 ^= oparg; */

              En outre, comparer bit à bit (ORing) la valeur suivante dans op a pour  conséquence
              que (1 << oparg) sera utilisé en tant qu'opérande :

                  FUTEX_OP_ARG_SHIFT  8  /* Utiliser (1 << oparg) comme opérande */

              Le champ cmp prend une de ces valeurs :

                  FUTEX_OP_CMP_EQ     0  /* si (oldval == cmparg) réveiller */
                  FUTEX_OP_CMP_NE     1  /* si (oldval != cmparg) réveiller */
                  FUTEX_OP_CMP_LT     2  /* si (oldval < cmparg) réveiller */
                  FUTEX_OP_CMP_LE     3  /* si (oldval <= cmparg) réveiller */
                  FUTEX_OP_CMP_GT     4  /* si (oldval > cmparg) réveiller */
                  FUTEX_OP_CMP_GE     5  /* si (oldval >= cmparg) réveiller */

              Le  code  de  retour  de FUTEX_WAKE_OP est la somme du nombre d'éléments en attente
              réveillés par le futex uaddr et du nombre d'éléments en attente  réveillés  sur  le
              futex uaddr2.

       FUTEX_WAIT_BITSET (depuis Linux 2.6.25)
              Cette  opération  est  équivalente  à  FUTEX_WAIT,  sauf  que val3 est utilisé pour
              fournir un masque de bit de 32 bits au noyau. Ce masque, où au moins  un  bit  doit
              être  positionné,  est  stocké  dans  la  partie  interne  du noyau de l'élément en
              attente. Voir la description de FUTEX_WAKE_BITSET pour plus de détails.

              Si timeout n'est pas NULL, la structure vers laquelle il pointe  indique  un  délai
              absolu  de  l'opération d'attente. Si timeout est NULL, l'opération peut se bloquer
              indéfiniment.

              L'argument uaddr2 est ignoré.

       FUTEX_WAKE_BITSET (depuis Linux 2.6.25)
              Cette opération est identique à FUTEX_WAKE, sauf que le paramètre val3 est  utilisé
              pour  fournir  un  masque de bit de 32 bits au noyau. Ce masque, où au moins un bit
              doit être positionné, est utilisé pour choisir les éléments en attente qui  doivent
              être réveillés. Le choix se fait par une comparaison bit à bit AND du masque de bit
              « wait » (à savoir la valeur de val3) et par un masque de bit stocké dans la partie
              interne  de l'élément en attente (le masque de bit « wait » positionné en utilisant
              FUTEX_WAIT_BITSET). Tous les éléments en attente pour lesquels le AND  est  positif
              sont réveillés ; les autres restent endormis.

              L'effet  de  FUTEX_WAIT_BITSET  et  de FUTEX_WAKE_BITSET est de permettre un réveil
              sélectif parmi les éléments en  attente  bloqués  sur  le  même  futex.  Cependant,
              remarquez  que  selon le cas, l'utilisation de cette fonction de mélange de masques
              de bit sur un futex peut être moins efficace que le fait d'avoir  plusieurs  futex,
              car elle a besoin que le noyau vérifie tous les éléments en attente sur un futex, y
              compris ceux non concernés par  le  réveil  (à  savoir  qu'ils  n'ont  pas  de  bit
              pertinent positionné dans leur masque de bit « wait »).

              La  constante  FUTEX_BITSET_MATCH_ANY,  qui  correspond  à tous les positionnements
              32 bits du masque, peut être utilisé en tant que val3 de  FUTEX_WAIT_BITSET  et  de
              FUTEX_WAKE_BITSET.  En dehors des différences dans la gestion du paramètre timeout,
              l'opération FUTEX_WAIT est équivalente à FUTEX_WAIT_BITSETval3 est  indiqué  en
              tant  que  FUTEX_BITSET_MATCH_ANY ;  c'est-à-dire permettre le réveil par n'importe
              quel   élément   en   attente).   L’opération   FUTEX_WAKE   est   équivalente    à
              FUTEX_WAKE_BITSETval3  est  indiqué  en  tant  que  FUTEX_BITSET_MATCH_ANY ;
              c'est-à-dire, réveiller n’importe quel élément en attente.

              Les arguments uaddr2 et timeout sont ignorés.

   Futex et héritage de priorité
       Linux prend en charge l'héritage de priorité (priority inheritance, PI) des futex, afin de
       gérer des problèmes d'inversion des priorités qu'on peut rencontrer avec des verrous futex
       normaux. L'inversion des priorités est un problème qui survient quand une tâche  de  haute
       priorité  est  bloquée  en  attente  d'acquérir  un  verrou que possède une tâche de basse
       priorité issue du processeur. Du coup, la tâche de priorité basse ne va  pas  relâcher  le
       verrou et celle de haute priorité reste bloquée.

       L'héritage  de priorité est un mécanisme pour gérer le problème d'inversion des priorités.
       Avec ce mécanisme, quand une tâche à haute priorité est bloquée par un verrou possédé  par
       une  tâche  à  basse priorité, la priorité de la seconde est temporairement amenée au même
       niveau que celle à haute priorité, de sorte qu'elle ne soit pas doublée par une  tâche  de
       niveau  intermédiaire  et  qu'elle  puisse  ainsi  avancer  pour  relâcher le verrou. Pour
       fonctionner, l'héritage de priorité doit être transitif, ce qui signifie que si une  tâche
       à  haute  priorité  bloque sur le verrou d'une tâche à priorité intermédiaire (et ainsi de
       suite sur des chaînes de la taille de votre choix), les deux tâches (ou plus  généralement
       toutes les tâches de la chaîne de verrous) voient leur niveau de priorité amené à celui de
       la tâche à haute priorité.

       Du point de vue de l'espace utilisateur, le futex a conscience d'un PI  en  acceptant  une
       réglementation  (décrite  ci-dessous) entre l'espace utilisateur et le noyau sur la valeur
       du  mot  futex,  couplé  à  l'utilisation  d'opérations  futex  PI   décrites   ci-dessous
       (contrairement  aux  autres  opérations  futex  décrites  ci-dessus,  celles PI-futex sont
       conçues pour l'implémentation de mécanismes IPC très spécifiques).

       Les opérations PI-futex décrites ci-dessous diffèrent des autres opérations dans  le  sens
       où elles imposent des règles dans l'utilisation de la valeur du mot futex :

       -  Si le verrou n'est pas acquis, la valeur du mot futex doit être 0.

       -  Si  le  verrou  est acquis, la valeur du mot futex doit être l'ID du thread (TID ; voir
          gettid(2)) du thread propriétaire.

       -  Si le verrou a un propriétaire et s'il y a des threads en concurrence pour  le  verrou,
          le  bit FUTEX_WAITERS doit être positionné dans la valeur du mot futex ; autrement dit,
          cette valeur est :

              FUTEX_WAITERS | TID

          (Remarquez que cela n'est pas possible pour un mot futex PI d'être sans propriétaire ni
          FUTEX_WAITERS défini).

       Avec  cette  règle,  une  application  de l'espace utilisateur peut acquérir un verrou non
       acquis ou en relâcher un en utilisant des instructions atomiques dans l'espace utilisateur
       (comme   une  opération  compare-and-swap  telle  que  cmpxchg  sur  l'architecture  x86).
       L'acquisition d'un verrou consiste simplement dans l'utilisation de compare-and-swap  pour
       positionner  la  valeur  du  mot  futex de manière atomique sur le TID de l'appelant si sa
       valeur précédente était 0. Relâcher  un  verrou  exige  d'utiliser  compare-and-swap  pour
       positionner la valeur du mot futex sur 0 si la valeur précédente était le TID prévu.

       Si  un  futex  est déjà acquis (c'est-à-dire qu'il a une valeur positive), les éléments en
       attente doivent utiliser l'opération FUTEX_LOCK_PI pour acquérir le  verrou.  Si  d'autres
       threads  attendent  le  verrou,  le bit FUTEX_WAITERS est défini dans la valeur du futex ;
       dans ce cas le détenteur du verrou doit utiliser l'opération FUTEX_UNLOCK_PI pour relâcher
       le verrou.

       Dans  le  cas  où  les  appelants  sont bloqués dans le noyau (c'est-à-dire qu'ils doivent
       effectuer un appel futex()), ils traitent directement avec ce qu'on appelle  un  RT-mutex,
       un  mécanisme  de  verrouillage  du  noyau  qui  implémente la sémantique de l'héritage de
       priorité requis. Après que le RT-mutex est acquis, la valeur futex  est  mise  à  jour  en
       fonction, avant que le thread appelant ne renvoie vers l'espace utilisateur.

       Il  est  important de remarquer que le noyau mettra à jour la valeur du mot futex avant de
       renvoyer vers l'espace utilisateur (cela enlève la possibilité pour  la  valeur  d'un  mot
       futex  de  se terminer dans un état non valable, par exemple en ayant un propriétaire mais
       en ayant la valeur 0, ou en ayant des éléments en attente  mais  aucun  bit  FUTEX_WAITERS
       positionné).

       Si  un  futex  a un RT-mutex associé dans le noyau (c'est-à-dire qu'il y a des éléments en
       attente bloqués) et si le propriétaire du futex/RT-mutex meurt de manière  inattendue,  le
       noyau  nettoie le RT-mutex et passe la main au prochain élément en attente. Cela implique,
       en retour, que la valeur dans l'espace utilisateur soit mise à jour en fonction. Pour dire
       que  c'est nécessaire, le noyau positionne le bit FUTEX_OWNER_DIED dans le mot futex ainsi
       que dans l'ID du thread du nouveau propriétaire. L'espace utilisateur peut détecter  cette
       situation  par  la  présence  du  bit  FUTEX_OWNER_DIED  et  il est alors responsable pour
       nettoyer l'espace laissé par le propriétaire mort.

       Les PI futex sont utilisés en indiquant une des valeurs listées ci-dessous dans  futex_op.
       Remarquez  que  les  opérations  de  PI  futex  doivent  être utilisées par paires et sont
       soumises à des exigences supplémentaires :

       -  FUTEX_LOCK_PI, FUTEX_LOCK_PI2 et FUTEX_TRYLOCK_PI vont de  pair  avec  FUTEX_UNLOCK_PI.
          FUTEX_UNLOCK_PI  ne  doit  être appelé que sur un futex appartenant au thread appelant,
          tel que défini par les règles de la valeur, sans quoi on obtient l'erreur EPERM.

       -  FUTEX_WAIT_REQUEUE_PI va de pair avec FUTEX_CMP_REQUEUE_PI. Elles  doivent  s'effectuer
          depuis  un  futex  non-PI  vers  un  PI  futex  distinct (sans quoi on obtient l'erreur
          EINVAL). De plus, val (le nombre d'éléments en attente à  réveiller)  doit  être  de  1
          (sans quoi on obtient l'erreur EINVAL).

       Les opérations PI futex sont comme suit :

       FUTEX_LOCK_PI (depuis Linux 2.6.18)
              Cette opération est utilisée après avoir essayé sans succès d'acquérir un verrou en
              utilisant une instruction atomique en mode utilisateur, car  le  mot  futex  a  une
              valeur  positive  – en  particulier  parce  qu'il  contenait  le  TID (spécifique à
              l’espace de noms PID) du verrou propriétaire.

              L'opération vérifie la valeur du mot futex sur l'adresse uaddr. Si la valeur est de
              0, le noyau essaie de positionner de manière atomique la valeur du futex sur le TID
              de l'appelant. Si la valeur du mot futex  est  positive,  le  noyau  positionne  de
              manière  atomique  le bit FUTEX_WAITERS, qui signale au propriétaire du futex qu'il
              ne peut pas déverrouiller le futex dans l'espace utilisateur de  manière  atomique,
              en positionnant la valeur du futex à 0. Après cela, le noyau :

              (1)  Essaie de trouver le thread associé au TID du propriétaire.

              (2)  Crée  ou réutilise l'état du noyau sur la base du propriétaire (s'il s'agit du
                   premier élément en attente, il n'existe pas d'état du  noyau  pour  ce  futex,
                   donc  il  est  créé  en  verrouillant  le RT-mutex et le propriétaire du futex
                   devient propriétaire du RT-mutex). Si des éléments en attente existent, l'état
                   existant est réutilisé.

              (3)  Rattache  l'élément  en  attente  au futex (c'est-à-dire que l'élément est mis
                   dans la file d'attente du RT-futex).

              S'il existe plus d'un élément en attente, la mise dans la file d'un élément se fait
              par  ordre de priorité descendant (pour des informations sur l'ordre des priorités,
              voir les points sur l'ordonnancement SCHED_DEADLINE, SCHED_FIFO  et  SCHED_RR  dans
              sched(7)).  Le  propriétaire  hérite  soit  de  la  bande passante de processeur de
              l'élément en attente (si l'élément est programmé sous la  règle  SCHED_DEADLINE  ou
              SCHED_FIFO),  soit  de la priorité de l'élément en attente (s'il est programmé sous
              la règle SCHED_RR ou SCHED_FIFO). Cet héritage suit la chaîne de verrous  dans  les
              cas de verrous imbriqués et il effectue la détection des verrous morts (deadlocks).

              Le  paramètre timeout fournit un délai de tentative de verrouillage. Si timeout est
              positif, la structure vers laquelle il pointe indique un  délai  absolu  mesuré  en
              fonction  de l'horloge CLOCK_REALTIME. Si timeout est NULL, l'opération se bloquera
              indéfiniment.

              Les paramètres uaddr2, val et val3 sont ignorés.

       FUTEX_LOCK_PI2 (depuis Linux 5.14)
              Cette opération est la même que FUTEX_LOCK_PI, sauf que  l'horloge  par  rapport  à
              laquelle  timeout  est mesuré peut être sélectionnée. Par défaut, le délai (absolu)
              indiqué dans timeout est mesuré par rapport à  l'horloge  CLOCK_MONOTONIC  mais  si
              l'attribut  FUTEX_CLOCK_REALTIME est indiqué dans futex_op, le délai est mesuré par
              rapport à l'horloge CLOCK_REALTIME.

       FUTEX_TRYLOCK_PI (depuis Linux 2.6.18)
              L'opération  essaie  d'acquérir  le  verrou  sur  uaddr.  Elle  est  appelée  quand
              l'acquisition  atomique  dans  l'espace utilisateur n'a pas réussi parce que le mot
              futex ne valait pas 0.

              Du fait que le noyau accède à plus d'informations d'état que l'espace  utilisateur,
              l'acquisition  du  verrou  pourrait réussir si elle est effectuée par le noyau dans
              les cas où le mot futex (c'est-à-dire  les  informations  d'état  accessibles  dans
              l'espace    utilisateur)    contient    un   état   stable   (FUTEX_WAITERS   et/ou
              FUTEX_OWNER_DIED). Cela peut arriver quand  le  propriétaire  du  futex  est  mort.
              L'espace utilisateur ne peut pas gérer cette condition de manière "race-free", mais
              le noyau peut corriger cela et acquérir le futex.

              Les paramètres uaddr2, val, timeout et val3 sont ignorés.

       FUTEX_UNLOCK_PI (depuis Linux 2.6.18)
              Cette opération réveille l'élément ayant la plus haute  priorité  et  attendant  un
              FUTEX_LOCK_PI ou un FUTEX_LOCK_PI2 à l'adresse indiquée par le paramètre uaddr.

              Cela  est  appelé  quand  la valeur dans l'espace utilisateur sur uaddr ne peut pas
              être passée à 0 de manière atomique depuis un TID (du propriétaire).

              Les paramètres uaddr2, val, timeout et val3 sont ignorés.

       FUTEX_CMP_REQUEUE_PI (depuis Linux 2.6.31)
              Cette opération est une variante  PI-aware  de  FUTEX_CMP_REQUEUE.  Elle  remet  en
              attente  des  éléments  bloqués  avec FUTEX_WAIT_REQUEUE_PI sur uaddr à partir d'un
              futex source non-PI (uaddr) vers un futex cible PI (uaddr2).

              Comme avec FUTEX_CMP_REQUEUE, cette opération réveille un maximum de  val  éléments
              qui  attendent  le  futex sur uaddr. Toutefois, pour FUTEX_CMP_REQUEUE_PI, val doit
              valoir 1 (puisque son but principal est d'éviter l’effet  de  troupeau  (thundering
              herd).  Les autres éléments sont supprimés de la file d'attente du futex source sur
              uaddr et ajoutés sur celle du futex cible sur uaddr2.

              Les paramètres val2 et val3 ont le même objectif qu'avec FUTEX_CMP_REQUEUE.

       FUTEX_WAIT_REQUEUE_PI (depuis Linux 2.6.31)
              Attendre un futex non-PI sur uaddr et se mettre potentiellement  en  attente  (avec
              une opération FUTEX_CMP_REQUEUE_PI dans une autre tâche), d'un futex PI sur uaddr2.
              L'opération d'attente sur uaddr est la même que pour FUTEX_WAIT.

              L'élément peut être retiré de la file d'attente sur uaddr sans être  transféré  sur
              uaddr2  à  l’aide  d’une  opération  FUTEX_WAKE  dans une autre tâche. Dans ce cas,
              l'opération FUTEX_WAIT_REQUEUE_PI échoue avec l'erreur EAGAIN.

              Si timeout n'est pas NULL, la structure vers laquelle il pointe  indique  un  délai
              absolu  de  l'opération d'attente. Si timeout est NULL, l'opération peut se bloquer
              indéfiniment.

              L'argument val3 est ignoré.

              FUTEX_WAIT_REQUEUE_PI et FUTEX_CMP_REQUEUE_PI ont été ajoutés  pour  gérer  un  cas
              d'utilisation  bien  particulier : la prise en charge des variables conditionnelles
              de threads POSIX ayant connaissance de l'héritage de priorité. L'idée est  que  ces
              opérations  devraient  toujours  aller  par  paires,  afin de garantir que l'espace
              utilisateur et le noyau restent  toujours  synchronisés.  Ainsi,  dans  l'opération
              FUTEX_WAIT_REQUEUE_PI, l'application dans l'espace utilisateur pré-indique la cible
              de la remise en attente qui va se faire dans l'opération FUTEX_CMP_REQUEUE_PI.

VALEUR RENVOYÉE

       En cas d'erreur (en supposant que futex() a été appelé à l’aide de syscall(2)), toutes les
       opérations renvoient -1 et positionnent errno pour indiquer l'erreur.

       En  cas  de  succès,  le  code de retour dépend de l'opération, comme décrit dans la liste
       suivante :

       FUTEX_WAIT
              Renvoie 0 si l'appelant a été  réveillé.  Remarquez  qu'un  réveil  peut  également
              résulter  de l'utilisation de motifs d'utilisation classiques de futex dans du code
              non lié qui a pu utiliser l'emplacement mémoire  du  mot  futex  (par  exemple  des
              implémentations  classiques  basées  sur  futex de mutex Pthreads peuvent provoquer
              cela dans certaines conditions). Donc, les appelants devraient  toujours,  à  titre
              conservatoire,  supposer  qu'un  code de retour 0 peut signifier un faux réveil, et
              donc utiliser la valeur du mot futex (à savoir  le  schéma  de  synchronisation  de
              l'espace utilisateur) pour décider de rester bloqués ou pas.

       FUTEX_WAKE
              Renvoie le nombre de processus en attente qui ont été réveillés.

       FUTEX_FD
              Renvoie le nouveau descripteur de fichier associé au futex.

       FUTEX_REQUEUE
              Renvoie le nombre de processus en attente qui ont été réveillés.

       FUTEX_CMP_REQUEUE
              Renvoie  le  nombre  total d'éléments en attente réveillés ou remis dans la file du
              futex pour le mot futex sur uaddr2. Si  cette  valeur  est  supérieure  à  val,  la
              différence devient le nombre d'éléments en attente remis dans la file du futex pour
              le mot futex sur uaddr2.

       FUTEX_WAKE_OP
              Renvoie le nombre total d'éléments en attente réveillés. Il s'agit de la somme  des
              éléments réveillés sur les deux futex pour les mots futex sur uaddr et uaddr2.

       FUTEX_WAIT_BITSET
              Renvoie   0   si  l'appelant  a  été  réveillé.  Voir  FUTEX_WAIT  sur  la  manière
              d'interpréter cela correctement en pratique.

       FUTEX_WAKE_BITSET
              Renvoie le nombre de processus en attente qui ont été réveillés.

       FUTEX_LOCK_PI
              Renvoie 0 si le futex a appliqué le verrou avec succès.

       FUTEX_LOCK_PI2
              Renvoie 0 si le futex a appliqué le verrou avec succès.

       FUTEX_TRYLOCK_PI
              Renvoie 0 si le futex a appliqué le verrou avec succès.

       FUTEX_UNLOCK_PI
              Renvoie 0 si le futex a correctement enlevé le verrou.

       FUTEX_CMP_REQUEUE_PI
              Renvoie le nombre total d'éléments en attente réveillés ou remis dans  la  file  du
              futex  pour  le  mot  futex  sur  uaddr2.  Si cette valeur est supérieure à val, la
              différence devient le nombre d'éléments en attente remis dans la file du futex pour
              le mot futex sur uaddr2.

       FUTEX_WAIT_REQUEUE_PI
              Renvoie  0 si l'appelant a été mis dans la file d'attente avec succès au futex pour
              le mot futex sur uaddr2.

ERREURS

       EACCES Pas d'accès en lecture à la mémoire d'un mot futex.

       EAGAIN (FUTEX_WAIT, FUTEX_WAIT_BITSET,  FUTEX_WAIT_REQUEUE_PI)  La  valeur  vers  laquelle
              pointait uaddr n'était pas égale à la valeur val attendue au moment de l'appel.

              Remarque :  sur  Linux,  les  noms  symboliques  EAGAIN  et  EWOULDBLOCK  (les deux
              apparaissent dans différents endroits du code futex du noyau) ont la même valeur.

       EAGAIN (FUTEX_CMP_REQUEUE, FUTEX_CMP_REQUEUE_PI) La valeur vers  laquelle  pointait  uaddr
              n'était pas égale à la valeur val3 attendue.

       EAGAIN (FUTEX_LOCK_PI,  FUTEX_LOCK_PI2,  FUTEX_TRYLOCK_PI,  FUTEX_CMP_REQUEUE_PI)  L'ID du
              thread propriétaire du futex sur uaddr (pour FUTEX_CMP_REQUEUE_PI : uaddr2) est sur
              le  point  de  se  terminer,  mais  il  n'a  pas encore géré le nettoyage de l'état
              interne. Réessayez.

       EDEADLK
              (FUTEX_LOCK_PI,  FUTEX_LOCK_PI2,  FUTEX_TRYLOCK_PI,  FUTEX_CMP_REQUEUE_PI)  Le  mot
              futex sur uaddr est déjà verrouillé par l'appelant.

       EDEADLK
              (FUTEX_CMP_REQUEUE_PI)  Pendant  qu'il  remettait en attente un élément du PI futex
              pour le mot futex sur uaddr2, le noyau a détecté un verrou mort (deadlock).

       EFAULT Le paramètre d'un pointeur nécessaire (c'est-à-dire uaddr, uaddr2  ou  timeout)  ne
              pointait pas vers une adresse valable de l'espace utilisateur.

       EINTR  Une opération FUTEX_WAIT ou FUTEX_WAIT_BITSET a été interrompue par un signal (voir
              signal(7)). Dans Linux 2.6.22, cette erreur pouvait aussi  être  renvoyée  pour  un
              faux réveil ; depuis Linux 2.6.22, cela n'arrive plus.

       EINVAL L'opération  dans  futex_op  fait  partie de celles qui utilisent un délai, mais le
              paramètre timeout fourni n'était pas valable (tv_sec valait moins de 0  ou  tv_nsec
              ne valait pas moins de 1 000 000 000).

       EINVAL L'opération  indiquée  dans  futex_op utilise uaddr et/ou uaddr2 mais l'un d'eux ne
              pointe pas vers un objet valable — c'est-à-dire, l'adresse n'est  pas  alignée  sur
              quatre octets.

       EINVAL (FUTEX_WAIT_BITSET, FUTEX_WAKE_BITSET) Le masque de bit fourni dans val3 vaut zéro.

       EINVAL (FUTEX_CMP_REQUEUE_PI)   uaddr  est  égal  à  uaddr2 (c'est-à-dire qu'une remise en
              attente a été tentée sur le même futex).

       EINVAL (FUTEX_FD) Le numéro du signal fourni dans val n'est pas valable.

       EINVAL (FUTEX_WAKE, FUTEX_WAKE_OP, FUTEX_WAKE_BITSET, FUTEX_REQUEUE, FUTEX_CMP_REQUEUE) Le
              noyau  a  détecté une incohérence entre l'état de l'espace utilisateur sur uaddr et
              l'état du  noyau  — c'est-à-dire  qu'il  a  détecté  un  élément  qui  attend  dans
              FUTEX_LOCK_PI ou FUTEX_LOCK_PI2 sur uaddr.

       EINVAL (FUTEX_LOCK_PI,  FUTEX_LOCK_PI2,  FUTEX_TRYLOCK_PI,  FUTEX_UNLOCK_PI)  Le  noyau  a
              détecté une incohérence entre l'état de l'espace utilisateur sur uaddr et l'état du
              noyau.  Cela  indique  soit  une  corruption  d'état, soit que le noyau a trouvé un
              élément en attente sur uaddr  qui  attend  aussi  à  l'aide  de  FUTEX_WAIT  ou  de
              FUTEX_WAIT_BITSET.

       EINVAL (FUTEX_CMP_REQUEUE_PI)  Le noyau a détecté une incohérence entre l'état de l'espace
              utilisateur sur uaddr et l'état du noyau ; c'est-à-dire qu'il a détecté un  élément
              qui attend via FUTEX_WAIT ou FUTEX_WAIT_BITSET sur uaddr2.

       EINVAL (FUTEX_CMP_REQUEUE_PI)  Le noyau a détecté une incohérence entre l'état de l'espace
              utilisateur sur uaddr et l'état du noyau ; c'est-à-dire qu'il a détecté un  élément
              qui attend à l'aide de FUTEX_WAIT ou de FUTEX_WAIT_BITESET sur uaddr.

       EINVAL (FUTEX_CMP_REQUEUE_PI)  Le noyau a détecté une incohérence entre l'état de l'espace
              utilisateur sur uaddr et l'état du noyau ; c'est-à-dire qu'il a détecté un  élément
              qui   attend   à   l'aide  de  FUTEX_LOCK_PI  ou  de  FUTEX_LOCK_PI2  (au  lieu  de
              FUTEX_WAIT_REQUEUE_PI).

       EINVAL (FUTEX_CMP_REQUEUE_PI) Tentative de remise dans la file  d'un  élément  en  attente
              vers  un  futex  différent  de  celui  indiqué  avec  l'appel FUTEX_WAIT_REQUEUE_PI
              correspondant pour cet élément.

       EINVAL (FUTEX_CMP_REQUEUE_PI) Le paramètre val ne vaut pas 1.

       EINVAL Argument incorrect.

       ENFILE (FUTEX_FD) La limite du nombre total de fichiers  ouverts  sur  le  système  a  été
              atteinte.

       ENOMEM (FUTEX_LOCK_PI,  FUTEX_LOCK_PI2,  FUTEX_TRYLOCK_PI,  FUTEX_CMP_REQUEUE_PI) Le noyau
              n'a pas pu allouer de la mémoire pour conserver les informations d'état.

       ENOSYS Opération non valable indiquée dans futex_op.

       ENOSYS L'option FUTEX_CLOCK_REALTIME était indiquée dans futex_op,  mais  l'opération  qui
              l'accompagne  n'est  ni FUTEX_WAIT, ni FUTEX_WAIT_BITSET, ni FUTEX_WAIT_REQUEUE_PI,
              ni FUTEX_LOCK_PI2.

       ENOSYS (FUTEX_LOCK_PI,      FUTEX_LOCK_PI2,       FUTEX_TRYLOCK_PI,       FUTEX_UNLOCK_PI,
              FUTEX_CMP_REQUEUE_PI, FUTEX_WAIT_REQUEUE_PI) Une vérification pendant l'exécution a
              déterminé que l'opération n'est pas disponible. Les opérations PI-futex ne sont pas
              implémentées  sur  toutes  les  architectures  et  ne sont pas prises en charge sur
              certaines variantes de processeur.

       EPERM  (FUTEX_LOCK_PI, FUTEX_LOCK_PI2, FUTEX_TRYLOCK_PI, FUTEX_CMP_REQUEUE_PI)  L'appelant
              n'est  pas  autorisé à se rattacher au futex sur uaddr (pour FUTEX_CMP_REQUEUE_PI :
              le futex sur uaddr2) (cela peut venir d'une  corruption  de  l'état  dans  l'espace
              utilisateur).

       EPERM  (FUTEX_UNLOCK_PI)  Le  verrou  représenté  par  le  mot  futex  n'appartient  pas à
              l'appelant.

       ESRCH  (FUTEX_LOCK_PI, FUTEX_LOCK_PI2,  FUTEX_TRYLOCK_PI,  FUTEX_CMP_REQUEUE_PI)  L'ID  du
              thread dans le mot futex sur uaddr n'existe pas.

       ESRCH  (FUTEX_CMP_REQUEUE_PI) L'ID du thread dans le mot futex sur uaddr2 n'existe pas.

       ETIMEDOUT
              L'opération  de  futex_op  a  utilisé  un  délai indiqué dans timeout et le délai a
              expiré avant la fin de l'opération.

STANDARDS

       Linux.

HISTORIQUE

       Linux 2.6.0.

       La prise en charge initiale des futex  a  été  ajoutée  dans  Linux 2.5.7  mais  avec  une
       sémantique  différente de celle décrite ci‐dessus. Un appel système à 4 paramètres avec la
       sémantique décrite dans cette page a été ajouté dans Linux 2.5.40. Dans  Linux 2.5.70,  un
       cinquième paramètre a été ajouté. Un sixième paramètre a été ajouté dans Linux 2.6.7.

EXEMPLES

       Le  programme  ci-dessous montre l'utilisation des futex dans un programme où un processus
       parent et un processus enfant utilisent une paire de futex située dans un tableau  anonyme
       partagé  pour  synchroniser  l'accès  à  une  ressource  partagée :  le terminal. Les deux
       processus écrivent chacun un message nloops (un paramètre en ligne de commande qui vaut  5
       par   défaut  s'il  est  absent)  sur  le  terminal  et  ils  utilisent  un  protocole  de
       synchronisation pour garantir qu'ils  alternent  dans  l'écriture  des  messages.  Pendant
       l'exécution de ce programme, nous voyons un affichage comme suit :

           $ ./futex_demo
           Parent (18534) 0
           Child  (18535) 0
           Parent (18534) 1
           Child  (18535) 1
           Parent (18534) 2
           Child  (18535) 2
           Parent (18534) 3
           Child  (18535) 3
           Parent (18534) 4
           Child  (18535) 4

   Source du programme

       /* futex_demo.c

          Usage: futex_demo [nloops]
                           (Default: 5)

          Montrer l'utilisation des futex dans un programme où le parent et
          l'enfant utilisent une paire de futex située dans un tableau anonyme
          partagé pour synchroniser l'accès à une ressource partagée : le
          terminal. Les processus écrivent chacun des messages 'num-loops'
          sur le terminal et ils utilisent un protocole de synchronisation qui
          garantit qu'ils alternent l'écriture des messages.
       */
       #define _GNU_SOURCE
       #include <err.h>
       #include <errno.h>
       #include <linux/futex.h>
       #include <stdatomic.h>
       #include <stdint.h>
       #include <stdio.h>
       #include <stdlib.h>
       #include <sys/mman.h>
       #include <sys/syscall.h>
       #include <sys/time.h>
       #include <sys/wait.h>
       #include <unistd.h>

       static uint32_t *futex1, *futex2, *iaddr;

       static int
       futex(uint32_t *uaddr, int futex_op, uint32_t val,
             const struct timespec *timeout, uint32_t *uaddr2, uint32_t val3)
       {
           return syscall(SYS_futex, uaddr, futex_op, val,
                          timeout, uaddr2, val3);
       }

       /* Acquérir le futex vers lequel pointe 'futexp' : attendre que sa
          valeur passe à 1 puis positionner la valeur sur 0. */

       static void
       fwait(uint32_t *futexp)
       {
           long            s;
           const uint32_t  one = 1;

           /* atomic_compare_exchange_strong(ptr, oldval, newval)
              fait atomiquement comme :

                  if (*ptr == *oldval)
                      *ptr = newval;

              Il renvoie true si le test a montré true et *ptr a été mis à jour. */

           while (1) {

               /* Le futex est-il disponible ? */
               if (atomic_compare_exchange_strong(futexp, &one, 0))
                   break;      /* Oui */

               /* Le futex n'est pas disponible ; attendre. */

               s = futex(futexp, FUTEX_WAIT, 0, NULL, NULL, 0);
               if (s == -1 && errno != EAGAIN)
                   err(EXIT_FAILURE, "futex-FUTEX_WAIT");
           }
       }

       /* Relâcher le futex vers lequel pointe 'futexp' : si le futex a
         actuellement la valeur 0, positionner la valeur à 1 et réveiller tous les
         futex en attente pour que si le pair est bloqué dans fwait(), ça puisse
         continuer. */

       static void
       fpost(uint32_t *futexp)
       {
           long            s;
           const uint32_t  zero = 0;

           /* atomic_compare_exchange_strong() a été décrit
              dans les commentaires ci-dessus. */

           if (atomic_compare_exchange_strong(futexp, &zero, 1)) {
               s = futex(futexp, FUTEX_WAKE, 1, NULL, NULL, 0);
               if (s  == -1)
                   err(EXIT_FAILURE, "futex-FUTEX_WAKE");
           }
       }

       int
       main(int argc, char *argv[])
       {
           pid_t         childPid;
           unsigned int  nloops;

           setbuf(stdout, NULL);

           nloops = (argc > 1) ? atoi(argv[1]) : 5;

           /* Créer un tableau anonyme partagé qui gardera les futex.
              Comme les futex vont être partagés entre les processus, nous
               utilisons donc les opérations futex « shared » (donc pas celles
               dont le suffixe est "_PRIVATE") */

           iaddr = mmap(NULL, sizeof(*iaddr) * 2, PROT_READ | PROT_WRITE,
                        MAP_ANONYMOUS | MAP_SHARED, -1, 0);
           if (iaddr == MAP_FAILED)
               err(EXIT_FAILURE, "mmap");

           futex1 = &iaddr[0];
           futex2 = &iaddr[1];

           *futex1 = 0;        /* State: unavailable */
           *futex2 = 1;        /* State: available */

           /* Créer un processus enfant qui hérite du tableau anonyme
              partagé. */

           childPid = fork();
           if (childPid == -1)
               err(EXIT_FAILURE, "fork");

           if (childPid == 0) {        /* Child */
               for (unsigned int j = 0; j < nloops; j++) {
                   fwait(futex1);
                   printf("Child  (%jd) %u\n", (intmax_t) getpid(), j);
                   fpost(futex2);
               }

               exit(EXIT_SUCCESS);
           }

           /* Le parent se retrouve ici. */

           for (unsigned int j = 0; j < nloops; j++) {
               fwait(futex2);
               printf("Parent (%jd) %u\n", (intmax_t) getpid(), j);
               fpost(futex1);
           }

           wait(NULL);

           exit(EXIT_SUCCESS);
       }

VOIR AUSSI

       get_robust_list(2), restart_syscall(2), pthread_mutexattr_getprotocol(3), futex(7),
       sched(7)

       Les fichiers suivants des sources du noyau :

       -  Documentation/pi-futex.txt

       -  Documentation/futex-requeue-pi.txt

       -  Documentation/locking/rt-mutex.txt

       -  Documentation/locking/rt-mutex-design.txt

       -  Documentation/robust-futex-ABI.txt

       Franke, H., Russell, R., and Kirwood, M., 2002. Fuss, Futexes and Furwocks: Fast Userlevel
       Locking in Linux (à partir des actions d'Ottawa Linux Symposium 2002),
       ⟨http://kernel.org/doc/ols/2002/ols2002-pages-479-495.pdf⟩

       Hart, D., 2009. A futex overview and update, ⟨http://lwn.net/Articles/360699/⟩

       Hart, D. et Guniguntala, D., 2009. Requeue-PI: Making Glibc Condvars PI-Aware (à partir
       des comptes rendus de l'atelier Real-Time Linux 2009),
       ⟨http://lwn.net/images/conf/rtlws11/papers/proc/p10.pdf⟩

       Drepper, U., 2011. Futexes Are Tricky, ⟨http://www.akkadia.org/drepper/futex.pdf⟩

       La bibliothèque d'exemples de futex, futex-*.tar.bz2 à
       ⟨https://mirrors.kernel.org/pub/linux/kernel/people/rusty/⟩

TRADUCTION

       La traduction française de cette page de manuel a été créée par Christophe Blaess
       <https://www.blaess.fr/christophe/>, Stéphan Rafin <stephan.rafin@laposte.net>, Thierry
       Vignaud <tvignaud@mandriva.com>, François Micaux, Alain Portal <aportal@univ-montp2.fr>,
       Jean-Philippe Guérard <fevrier@tigreraye.org>, Jean-Luc Coulon (f5ibh) <jean-
       luc.coulon@wanadoo.fr>, Julien Cristau <jcristau@debian.org>, Thomas Huriaux
       <thomas.huriaux@gmail.com>, Nicolas François <nicolas.francois@centraliens.net>, Florentin
       Duneau <fduneau@gmail.com>, Simon Paillard <simon.paillard@resel.enst-bretagne.fr>, Denis
       Barbier <barbier@debian.org>, David Prévot <david@tilapin.org> et Jean-Philippe MENGUAL
       <jpmengual@debian.org>

       Cette traduction est une documentation libre ; veuillez vous reporter à la GNU General
       Public License version 3 ⟨https://www.gnu.org/licenses/gpl-3.0.html⟩ concernant les
       conditions de copie et de distribution. Il n'y a aucune RESPONSABILITÉ LÉGALE.

       Si vous découvrez un bogue dans la traduction de cette page de manuel, veuillez envoyer un
       message à ⟨debian-l10n-french@lists.debian.org⟩.