Provided by: manpages-fr-dev_4.15.0-9_all bug

NOM

       futex – Verrouillage rapide en mode utilisateur

SYNOPSIS

       #include <linux/futex.h>      /* Definition of FUTEX_* constants */
       #include <sys/syscall.h>      /* Definition of SYS_* constants */
       #include <unistd.h>

       long syscall(SYS_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: glibc provides no wrapper for futex(), necessitating the use of 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.

   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)
              This   option   bit   can   be   employed   only   with   the    FUTEX_WAIT_BITSET,
              FUTEX_WAIT_REQUEUE_PI,  (since  Linux  4.5)   FUTEX_WAIT,  and  (since  Linux 5.14)
              FUTEX_LOCK_PI2 operations.

              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.

       If a futex is already acquired (i.e., has  a  nonzero  value),  waiters  must  employ  the
       FUTEX_LOCK_PI  or  FUTEX_LOCK_PI2  operations  to  acquire  the lock. If other threads are
       waiting for the lock, then the FUTEX_WAITERS bit is set in the futex value; in this  case,
       the lock owner must employ the FUTEX_UNLOCK_PI operation to release the lock.

       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,  and  FUTEX_TRYLOCK_PI   pair   with   FUTEX_UNLOCK_PI.
          FUTEX_UNLOCK_PI  must be called only on a futex owned by the calling thread, as defined
          by the value policy, otherwise the error EPERM results.

       –  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 (since Linux 5.14)
              This operation is the same as FUTEX_LOCK_PI, except that the  clock  against  which
              timeout  is measured is selectable. By default, the (absolute) timeout specified in
              timeout   is   measured   againt   the   CLOCK_MONOTONIC   clock,   but   if    the
              FUTEX_CLOCK_REALTIME  flag  is  specified in futex_op, then the timeout is measured
              against the CLOCK_REALTIME clock.

       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)
              This operation wakes the top priority waiter that is waiting  in  FUTEX_LOCK_PI  or
              FUTEX_LOCK_PI2 on the futex address provided by the uaddr argument.

              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

       In the event of an error (and assuming that futex()   was  invoked  via  syscall(2)),  all
       operations return -1 and set errno to indicate the error.

       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)  The futex
              owner thread ID of uaddr (for FUTEX_CMP_REQUEUE_PI: uaddr2)  is about to exit,  but
              has not yet handled the internal state cleanup. Try again.

       EDEADLK
              (FUTEX_LOCK_PI,  FUTEX_LOCK_PI2, FUTEX_TRYLOCK_PI, FUTEX_CMP_REQUEUE_PI)  The futex
              word at uaddr is already locked by the caller.

       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)
              The kernel detected an inconsistency between the user-space state at uaddr and  the
              kernel  state—that  is,  it  detected  a  waiter  which  waits  in FUTEX_LOCK_PI or
              FUTEX_LOCK_PI2 on uaddr.

       EINVAL (FUTEX_LOCK_PI,  FUTEX_LOCK_PI2,  FUTEX_TRYLOCK_PI,  FUTEX_UNLOCK_PI)   The  kernel
              detected  an  inconsistency  between  the  user-space state at uaddr and the kernel
              state. This indicates either state corruption or that the kernel found a waiter  on
              uaddr which is waiting via FUTEX_WAIT or 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)  The kernel detected an inconsistency between the user-space
              state at uaddr and the kernel state; that is, the kernel detected  a  waiter  which
              waits via FUTEX_WAIT or FUTEX_WAIT_BITSET on uaddr.

       EINVAL (FUTEX_CMP_REQUEUE_PI)  The kernel detected an inconsistency between the user-space
              state at uaddr and the kernel state; that is, the kernel detected  a  waiter  which
              waits    on    uaddr    via    FUTEX_LOCK_PI    or   FUTEX_LOCK_PI2   (instead   of
              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)  The kernel
              could not allocate memory to hold state information.

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

       ENOSYS The FUTEX_CLOCK_REALTIME option was specified in  futex_op,  but  the  accompanying
              operation  was  neither  FUTEX_WAIT,  FUTEX_WAIT_BITSET, FUTEX_WAIT_REQUEUE_PI, nor
              FUTEX_LOCK_PI2.

       ENOSYS (FUTEX_LOCK_PI,      FUTEX_LOCK_PI2,       FUTEX_TRYLOCK_PI,       FUTEX_UNLOCK_PI,
              FUTEX_CMP_REQUEUE_PI,  FUTEX_WAIT_REQUEUE_PI)  A run-time check determined that the
              operation is not available. The PI-futex operations  are  not  implemented  on  all
              architectures and are not supported on some CPU variants.

       EPERM  (FUTEX_LOCK_PI, FUTEX_LOCK_PI2, FUTEX_TRYLOCK_PI, FUTEX_CMP_REQUEUE_PI)  The caller
              is not allowed to attach itself to the futex at  uaddr  (for  FUTEX_CMP_REQUEUE_PI:
              the futex at uaddr2). (This may be caused by a state corruption in user space.)

       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)  The thread
              ID in the futex word at uaddr does not exist.

       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

       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 */

               /* Futex is not available; wait. */

               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;

           /* Create a shared anonymous mapping that will hold the futexes.
              Since the futexes are being shared between processes, we
              subsequently use the "shared" futex operations (i.e., not the
              ones suffixed "_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 */

           /* Create a child process that inherits the shared anonymous
              mapping. */

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