Provided by: manpages-fr-dev_4.13-4_all bug

NOM

       futex – Verrouillage rapide en mode utilisateur

SYNOPSIS

       #include <linux/futex.h>
       #include <stdint.h>
       #include <sys/time.h>

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

       Note : il n'existe pas d'enveloppe pour cet appel système dans la glibc ; voir NOTES.

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.

   Arguments
       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 et (depuis Linux 4.5) FUTEX_WAIT.

              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 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_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 à 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 la cause de 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_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_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_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  les  noyaux  précédents 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 sur uaddr.

       EINVAL (FUTEX_LOCK_PI,  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 via FUTEX_WAIT ou 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 via FUTEX_WAIT ou 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 via FUTEX_LOCK_PI (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_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.

       ENOSYS (FUTEX_LOCK_PI,    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_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_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.

VERSIONS

       Les futex ont d'abord été disponibles dans une version stable du noyau avec 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.

CONFORMITÉ

       Cet appel système est spécifique à Linux.

NOTES

       La glibc ne fournit pas  de  fonction  autour  de  cet  appel  système ;  appelez-le  avec
       syscall(2).

       Plusieurs  abstractions  programmatiques  de haut niveau sont implémentées avec des futex,
       notamment les mécanismes POSIX de sémaphore  et  de  synchronisation  de  threads  (mutex,
       variables conditionnelles, verrous en lecture/écriture et barrières).

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

          Utilisation: futex_demo [nloops]
                           (Par défaut : 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 <stdio.h>
       #include <errno.h>
       #include <stdatomic.h>
       #include <stdint.h>
       #include <stdlib.h>
       #include <unistd.h>
       #include <sys/wait.h>
       #include <sys/mman.h>
       #include <sys/syscall.h>
       #include <linux/futex.h>
       #include <sys/time.h>

       #define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                               } while (0)

       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;

           /* 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 ? */
               const uint32_t one = 1;
               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)
                   errExit("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;

           /* atomic_compare_exchange_strong() was described
              in comments above */

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

       int
       main(int argc, char *argv[])
       {
           pid_t childPid;
           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)
               errExit("mmap");

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

           *futex1 = 0;        /* État : indisponible */
           *futex2 = 1;        /* État : disponible */

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

           childPid = fork();
           if (childPid == -1)
               errExit(

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

               exit(EXIT_SUCCESS);
           }

           /* Le parent se retrouve ici */

           for (int j = 0; j < nloops; j++) {
               fwait(futex2);
               printf("Parent (%jd) %d\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.txtDocumentation/futex-requeue-pi.txtDocumentation/locking/rt-mutex.txtDocumentation/locking/rt-mutex-design.txtDocumentation/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 recherches 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 à
       ⟨ftp://ftp.kernel.org/pub/linux/kernel/people/rusty/

COLOPHON

       Cette page fait partie de la publication 5.10 du projet man-pages Linux. Une description
       du projet et des instructions pour signaler des anomalies et la dernière version de cette
       page peuvent être trouvées à l'adresse https://www.kernel.org/doc/man-pages/.

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 ⟨⟩.