(root)/
glibc-2.38/
hurd/
hurdlock.c
       1  /* Hurd helpers for lowlevellocks.
       2     Copyright (C) 1999-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include "hurdlock.h"
      20  #include <hurd.h>
      21  #include <hurd/hurd.h>
      22  #include <time.h>
      23  #include <errno.h>
      24  #include <unistd.h>
      25  
      26  /* Convert an absolute timeout in nanoseconds to a relative
      27     timeout in milliseconds.  */
      28  static inline int __attribute__ ((gnu_inline))
      29  compute_reltime (const struct timespec *abstime, clockid_t clk)
      30  {
      31    struct timespec ts;
      32    __clock_gettime (clk, &ts);
      33  
      34    ts.tv_sec = abstime->tv_sec - ts.tv_sec;
      35    ts.tv_nsec = abstime->tv_nsec - ts.tv_nsec;
      36  
      37    if (ts.tv_nsec < 0)
      38      {
      39        --ts.tv_sec;
      40        ts.tv_nsec += 1000000000;
      41      }
      42  
      43    return ts.tv_sec < 0 ? -1 : (int)(ts.tv_sec * 1000 + ts.tv_nsec / 1000000);
      44  }
      45  
      46  int
      47  __lll_abstimed_wait (void *ptr, int val,
      48    const struct timespec *tsp, int flags, int clk)
      49  {
      50    if (clk != CLOCK_REALTIME)
      51      return EINVAL;
      52  
      53    int mlsec = compute_reltime (tsp, clk);
      54    return mlsec < 0 ? KERN_TIMEDOUT : __lll_timed_wait (ptr, val, mlsec, flags);
      55  }
      56  
      57  int
      58  __lll_abstimed_wait_intr (void *ptr, int val,
      59    const struct timespec *tsp, int flags, int clk)
      60  {
      61    if (clk != CLOCK_REALTIME)
      62      return EINVAL;
      63  
      64    int mlsec = compute_reltime (tsp, clk);
      65    return mlsec < 0 ? KERN_TIMEDOUT : __lll_timed_wait_intr (ptr, val, mlsec, flags);
      66  }
      67  
      68  int
      69  __lll_abstimed_xwait (void *ptr, int lo, int hi,
      70    const struct timespec *tsp, int flags, int clk)
      71  {
      72    if (clk != CLOCK_REALTIME)
      73      return EINVAL;
      74  
      75    int mlsec = compute_reltime (tsp, clk);
      76    return mlsec < 0 ? KERN_TIMEDOUT : __lll_timed_xwait (ptr, lo, hi, mlsec,
      77  	                                              flags);
      78  }
      79  
      80  int
      81  __lll_abstimed_lock (void *ptr,
      82    const struct timespec *tsp, int flags, int clk)
      83  {
      84    if (clk != CLOCK_REALTIME)
      85      return EINVAL;
      86  
      87    if (__lll_trylock (ptr) == 0)
      88      return 0;
      89  
      90    while (1)
      91      {
      92        if (atomic_exchange_acquire ((int *)ptr, 2) == 0)
      93          return 0;
      94        else if (! valid_nanoseconds (tsp->tv_nsec))
      95          return EINVAL;
      96  
      97        int mlsec = compute_reltime (tsp, clk);
      98        if (mlsec < 0 || __lll_timed_wait (ptr, 2, mlsec, flags) == KERN_TIMEDOUT)
      99          return ETIMEDOUT;
     100      }
     101  }
     102  
     103  /* Robust locks.  */
     104  
     105  /* Test if a given process id is still valid.  */
     106  static inline int
     107  valid_pid (int pid)
     108  {
     109    task_t task = __pid2task (pid);
     110    if (task == MACH_PORT_NULL)
     111      return 0;
     112  
     113    __mach_port_deallocate (__mach_task_self (), task);
     114    return 1;
     115  }
     116  
     117  /* Robust locks have currently no support from the kernel; they
     118     are simply implemented with periodic polling. When sleeping, the
     119     maximum blocking time is determined by this constant.  */
     120  #define MAX_WAIT_TIME   1500
     121  
     122  int
     123  __lll_robust_lock (void *ptr, int flags)
     124  {
     125    int *iptr = (int *)ptr;
     126    int id = __getpid ();
     127    int wait_time = 25;
     128    unsigned int val;
     129  
     130    /* Try to set the lock word to our PID if it's clear. Otherwise,
     131       mark it as having waiters.  */
     132    while (1)
     133      {
     134        val = *iptr;
     135        if (!val && atomic_compare_and_exchange_bool_acq (iptr, id, 0) == 0)
     136          return 0;
     137        else if (atomic_compare_and_exchange_bool_acq (iptr,
     138                 val | LLL_WAITERS, val) == 0)
     139          break;
     140      }
     141  
     142    for (id |= LLL_WAITERS ; ; )
     143      {
     144        val = *iptr;
     145        if (!val && atomic_compare_and_exchange_bool_acq (iptr, id, 0) == 0)
     146          return 0;
     147        else if (val && !valid_pid (val & LLL_OWNER_MASK))
     148          {
     149            if (atomic_compare_and_exchange_bool_acq (iptr, id, val) == 0)
     150              return EOWNERDEAD;
     151          }
     152        else
     153          {
     154            __lll_timed_wait (iptr, val, wait_time, flags);
     155            if (wait_time < MAX_WAIT_TIME)
     156              wait_time <<= 1;
     157          }
     158      }
     159  }
     160  
     161  int
     162  __lll_robust_abstimed_lock (void *ptr,
     163    const struct timespec *tsp, int flags, int clk)
     164  {
     165    int *iptr = (int *)ptr;
     166    int id = __getpid ();
     167    int wait_time = 25;
     168    unsigned int val;
     169  
     170    if (clk != CLOCK_REALTIME)
     171      return EINVAL;
     172  
     173    while (1)
     174      {
     175        val = *iptr;
     176        if (!val && atomic_compare_and_exchange_bool_acq (iptr, id, 0) == 0)
     177          return 0;
     178        else if (atomic_compare_and_exchange_bool_acq (iptr,
     179            val | LLL_WAITERS, val) == 0)
     180          break;
     181      }
     182  
     183    for (id |= LLL_WAITERS ; ; )
     184      {
     185        val = *iptr;
     186        if (!val && atomic_compare_and_exchange_bool_acq (iptr, id, 0) == 0)
     187          return 0;
     188        else if (val && !valid_pid (val & LLL_OWNER_MASK))
     189          {
     190            if (atomic_compare_and_exchange_bool_acq (iptr, id, val) == 0)
     191              return EOWNERDEAD;
     192          }
     193        else
     194          {
     195            int mlsec = compute_reltime (tsp, clk);
     196            if (mlsec < 0)
     197              return ETIMEDOUT;
     198            else if (mlsec > wait_time)
     199              mlsec = wait_time;
     200  
     201            int res = __lll_timed_wait (iptr, val, mlsec, flags);
     202            if (res == KERN_TIMEDOUT)
     203              return ETIMEDOUT;
     204            else if (wait_time < MAX_WAIT_TIME)
     205              wait_time <<= 1;
     206          }
     207      }
     208  }
     209  
     210  int
     211  __lll_robust_trylock (void *ptr)
     212  {
     213    int *iptr = (int *)ptr;
     214    int id = __getpid ();
     215    unsigned int val = *iptr;
     216  
     217    if (!val)
     218      {
     219        if (atomic_compare_and_exchange_bool_acq (iptr, id, 0) == 0)
     220          return 0;
     221      }
     222    else if (!valid_pid (val & LLL_OWNER_MASK)
     223             && atomic_compare_and_exchange_bool_acq (iptr, id, val) == 0)
     224      return EOWNERDEAD;
     225  
     226    return EBUSY;
     227  }
     228  
     229  void
     230  __lll_robust_unlock (void *ptr, int flags)
     231  {
     232    unsigned int val = atomic_load_relaxed ((unsigned int *)ptr);
     233    while (1)
     234      {
     235        if (val & LLL_WAITERS)
     236          {
     237            __lll_set_wake (ptr, 0, flags);
     238            break;
     239          }
     240        else if (atomic_compare_exchange_weak_release ((unsigned int *)ptr, &val, 0))
     241          break;
     242      }
     243  }