(root)/
glibc-2.38/
nptl/
futex-internal.c
       1  /* futex helper functions for glibc-internal use.
       2     Copyright (C) 2020-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 <errno.h>
      20  #include <sysdep.h>
      21  #include <time.h>
      22  #include <futex-internal.h>
      23  #include <kernel-features.h>
      24  
      25  #ifndef __ASSUME_TIME64_SYSCALLS
      26  static int
      27  __futex_abstimed_wait_common32 (unsigned int* futex_word,
      28                                  unsigned int expected, int op,
      29                                  const struct __timespec64* abstime,
      30                                  int private, bool cancel)
      31  {
      32    struct timespec ts32, *pts32 = NULL;
      33    if (abstime != NULL)
      34      {
      35        ts32 = valid_timespec64_to_timespec (*abstime);
      36        pts32 = &ts32;
      37      }
      38  
      39    if (cancel)
      40      return INTERNAL_SYSCALL_CANCEL (futex, futex_word, op, expected,
      41                                      pts32, NULL /* Unused.  */,
      42                                      FUTEX_BITSET_MATCH_ANY);
      43    else
      44      return INTERNAL_SYSCALL_CALL (futex, futex_word, op, expected,
      45                                    pts32, NULL /* Unused.  */,
      46                                    FUTEX_BITSET_MATCH_ANY);
      47  }
      48  #endif /* ! __ASSUME_TIME64_SYSCALLS */
      49  
      50  static int
      51  __futex_abstimed_wait_common64 (unsigned int* futex_word,
      52                                  unsigned int expected, int op,
      53                                  const struct __timespec64* abstime,
      54                                  int private, bool cancel)
      55  {
      56    if (cancel)
      57      return INTERNAL_SYSCALL_CANCEL (futex_time64, futex_word, op, expected,
      58  				    abstime, NULL /* Unused.  */,
      59  				    FUTEX_BITSET_MATCH_ANY);
      60    else
      61      return INTERNAL_SYSCALL_CALL (futex_time64, futex_word, op, expected,
      62  				  abstime, NULL /* Unused.  */,
      63  				  FUTEX_BITSET_MATCH_ANY);
      64  }
      65  
      66  static int
      67  __futex_abstimed_wait_common (unsigned int* futex_word,
      68                                unsigned int expected, clockid_t clockid,
      69                                const struct __timespec64* abstime,
      70                                int private, bool cancel)
      71  {
      72    int err;
      73    unsigned int clockbit;
      74  
      75    /* Work around the fact that the kernel rejects negative timeout values
      76       despite them being valid.  */
      77    if (__glibc_unlikely ((abstime != NULL) && (abstime->tv_sec < 0)))
      78      return ETIMEDOUT;
      79  
      80    if (! lll_futex_supported_clockid (clockid))
      81      return EINVAL;
      82  
      83    clockbit = (clockid == CLOCK_REALTIME) ? FUTEX_CLOCK_REALTIME : 0;
      84    int op = __lll_private_flag (FUTEX_WAIT_BITSET | clockbit, private);
      85  
      86  #ifdef __ASSUME_TIME64_SYSCALLS
      87    err = __futex_abstimed_wait_common64 (futex_word, expected, op, abstime,
      88  					private, cancel);
      89  #else
      90    bool need_time64 = abstime != NULL && !in_int32_t_range (abstime->tv_sec);
      91    if (need_time64)
      92      {
      93        err = __futex_abstimed_wait_common64 (futex_word, expected, op, abstime,
      94  					    private, cancel);
      95        if (err == -ENOSYS)
      96  	err = -EOVERFLOW;
      97      }
      98    else
      99      err = __futex_abstimed_wait_common32 (futex_word, expected, op, abstime,
     100                                            private, cancel);
     101  #endif
     102  
     103    switch (err)
     104      {
     105      case 0:
     106      case -EAGAIN:
     107      case -EINTR:
     108      case -ETIMEDOUT:
     109      case -EINVAL:
     110      case -EOVERFLOW:  /* Passed absolute timeout uses 64 bit time_t type, but
     111                           underlying kernel does not support 64 bit time_t futex
     112                           syscalls.  */
     113        return -err;
     114  
     115      case -EFAULT: /* Must have been caused by a glibc or application bug.  */
     116      case -ENOSYS: /* Must have been caused by a glibc bug.  */
     117      /* No other errors are documented at this time.  */
     118      default:
     119        futex_fatal_error ();
     120      }
     121  }
     122  
     123  int
     124  __futex_abstimed_wait64 (unsigned int* futex_word, unsigned int expected,
     125                           clockid_t clockid,
     126                           const struct __timespec64* abstime, int private)
     127  {
     128    return __futex_abstimed_wait_common (futex_word, expected, clockid,
     129                                         abstime, private, false);
     130  }
     131  libc_hidden_def (__futex_abstimed_wait64)
     132  
     133  int
     134  __futex_abstimed_wait_cancelable64 (unsigned int* futex_word,
     135                                      unsigned int expected, clockid_t clockid,
     136                                      const struct __timespec64* abstime,
     137                                      int private)
     138  {
     139    return __futex_abstimed_wait_common (futex_word, expected, clockid,
     140                                         abstime, private, true);
     141  }
     142  libc_hidden_def (__futex_abstimed_wait_cancelable64)
     143  
     144  int
     145  __futex_lock_pi64 (int *futex_word, clockid_t clockid,
     146  		   const struct __timespec64 *abstime, int private)
     147  {
     148    int err;
     149  
     150    unsigned int clockbit = clockid == CLOCK_REALTIME
     151  			  ? FUTEX_CLOCK_REALTIME : 0;
     152    int op_pi2 = __lll_private_flag (FUTEX_LOCK_PI2 | clockbit, private);
     153  #if __ASSUME_FUTEX_LOCK_PI2
     154    /* Assume __ASSUME_TIME64_SYSCALLS since FUTEX_LOCK_PI2 was added later.  */
     155    err = INTERNAL_SYSCALL_CALL (futex_time64, futex_word, op_pi2, 0, abstime);
     156  #else
     157    /* FUTEX_LOCK_PI does not support clock selection, so for CLOCK_MONOTONIC
     158       the only option is to use FUTEX_LOCK_PI2.  */
     159    int op_pi1 = __lll_private_flag (FUTEX_LOCK_PI, private);
     160    int op_pi = abstime != NULL && clockid != CLOCK_REALTIME ? op_pi2 : op_pi1;
     161  
     162  # ifdef __ASSUME_TIME64_SYSCALLS
     163    err = INTERNAL_SYSCALL_CALL (futex_time64, futex_word, op_pi, 0, abstime);
     164  # else
     165    bool need_time64 = abstime != NULL && !in_int32_t_range (abstime->tv_sec);
     166    if (need_time64)
     167      err = INTERNAL_SYSCALL_CALL (futex_time64, futex_word, op_pi, 0, abstime);
     168    else
     169      {
     170        struct timespec ts32, *pts32 = NULL;
     171        if (abstime != NULL)
     172  	{
     173  	  ts32 = valid_timespec64_to_timespec (*abstime);
     174  	  pts32 = &ts32;
     175  	}
     176        err = INTERNAL_SYSCALL_CALL (futex, futex_word, op_pi, 0, pts32);
     177      }
     178  # endif	 /* __ASSUME_TIME64_SYSCALLS */
     179     /* FUTEX_LOCK_PI2 is not available on this kernel.  */
     180     if (err == -ENOSYS)
     181       err = -EINVAL;
     182  #endif /* __ASSUME_FUTEX_LOCK_PI2  */
     183  
     184    switch (err)
     185      {
     186      case 0:
     187      case -EAGAIN:
     188      case -EINTR:
     189      case -ETIMEDOUT:
     190      case -ESRCH:
     191      case -EDEADLK:
     192      case -EINVAL: /* This indicates either state corruption or that the kernel
     193                       found a waiter on futex address which is waiting via
     194                       FUTEX_WAIT or FUTEX_WAIT_BITSET.  This is reported on
     195                       some futex_lock_pi usage (pthread_mutex_timedlock for
     196                       instance).  */
     197        return -err;
     198  
     199      case -EFAULT: /* Must have been caused by a glibc or application bug.  */
     200      case -ENOSYS: /* Must have been caused by a glibc bug.  */
     201      /* No other errors are documented at this time.  */
     202      default:
     203        futex_fatal_error ();
     204      }
     205  }