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

Linux                                            1 novembre 2020                                        FUTEX(2)